mybatis-plus

发布时间 2023-12-17 23:44:02作者: zL66

Mybatis-plus

简介 | MyBatis-Plus (baomidou.com)

特性

  • 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作

  • 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求

  • 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错

  • 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题

  • 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作

  • 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )

  • 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用

  • 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询

  • 分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库

  • 内置性能分析插件:可输出 SQL 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询

  • 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作

  • 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑

步骤

  • 导入mybatis-plus依赖

    <!--mybatis-plus-->
    <dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.4.3</version>
    </dependency>
    <!--mysql-->
    <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    </dependency>
    
  • 写入相应的pojo层和dao层

    pojo
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    import java.io.Serializable;
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class user implements Serializable {
    private Integer id;
    private String name;
    private Integer age;
    private String email;
    }
    dao
    
    
import com.baomidou.mybatisplus.core.mapper.BaseMapper;

import com.zhoulei.mybatis_plus01.pojo.user;

import org.apache.ibatis.annotations.Mapper;

import org.springframework.stereotype.Repository;

//@Mapper//也可以不在启动类中配置包扫描,在每个Mapper上面配置@Mapper,相当与mapper映射

@Repository//表示使Dao层

public interface userMapper extends BaseMapper<user> {

}
  • 测试
@SpringBootTest

class MybatisPlus01ApplicationTests {

  @Autowired

  private userMapper userMapper;

  @Test

  public void selectAll(){

//   List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

//        参数类型使一个Wrapper类型,表示是一个条件构造器

//      下面相当与 select * from user where null;

      List<user> users = userMapper.selectList(null);
    
     users.forEach(System.out::println);

  }

}

## CRUD扩展雪花算法

雪花算法

[分布式唯一ID解决方案-雪花算法 - 掘金 (juejin.cn)](https://juejin.cn/post/6916882326996090894)

​```java
@Data
@NoArgsConstructor
@AllArgsConstructor
public class user implements Serializable {
    //默认是雪花算法,同时自己也可以改变为自增ID
    private Integer id;
    private String name;
    private Integer age;
    private String email;
}

public enum IdType {
    AUTO(0),  //数据库id自增
    NONE(1),  //未设置主键
    INPUT(2),  //手动输入
    ASSIGN_ID(3), //表示程序分配一个数字型主键
    ASSIGN_UUID(4); //表示程序分配一个 UUID 作为主键
}

IdType4种值的表示MybatisPlus 主键策略(type=IdType.ASSIGN_ID等详解) - 双间 - 博客园 (cnblogs.com)

## 配置日志

​```yml
  #开启日志
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
public enum IdType {
    AUTO(0), //数据库自增
    NONE(1), //不管
    INPUT(2),//手动插入,与不管类似

    /* 以下3种类型、只有当插入对象ID 为空,才自动填充。 */
    /**
     * 分配ID (主键类型为number或string),
     * 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(雪花算法)
     */
    ASSIGN_ID(3),
    /**
     * 分配UUID (主键类型为 string)
     * 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(UUID.replace("-",""))
     */
    ASSIGN_UUID(4),
}
IdType type() default IdType.NONE;

自动填充

创建时间,修改时间!这些操作一般都是自动化完成的,我们不希望手动更新!

阿里巴巴开发手册:所有的数据库表:gmt_create,gmt_modified,几乎所有的表都要配置上!而且

需要自动化

方式一:数据库级别(不建议在工作中使用)

image-20230612120743130

在数据中新增两个字段,并将其默认值为current_timestamp,数据类型为datetime

方式二:代码级别

将表还原,避免数据的级别的自动填入对我们造成影响

image-20230612121320557

实现:

  • 需要在实现自动注入的字段上面添加@TableField注解,使用fill属性

    public enum FieldFill {
    DEFAULT, //什么都不干
    INSERT, //插入时填充
    UPDATE, //更新时填充
    INSERT_UPDATE; //在插入和更新的填充
    }
    
  • 编写处理类来处理注解

    @Slf4j
    @Component  //注意一定要将这个类放入到IOC容器中,在识别注解的时候就会利用反射机制运行到这个处理类
    public class MyMetaobjectHandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
    //* @param fieldName java bean属性名
    //* @param fieldVal java bean属性值
    //* @param metaObject元对象参数
    log.info("start insert ....");
    this.setFieldValByName("createTime",new Date(),metaObject );
    this.setFieldValByName("updateTime",new Date(),metaObject );
    }
    @Override
    public void updateFill(MetaObject metaObject) {
    log.info("start update ....");
    this.setFieldValByName("updateTime",new Date(),metaObject );
    }
    }
    
  • 测试

  • 查看数据库,注意字段值的变化.

乐观锁处理

乐观锁:十分乐观,它总是认为自己不会出现问题,所以在执行前不会去加锁,如果出现错误就在次去更新值去测试.

悲观锁:十分的悲观,它总是认为自己做什么都会出现问题,所以在执行之前都会加上锁.在去操作

使用乐观锁,一般都会加上一个version字段.

当要更新一条记录的时候,希望这条记录没有被别人更新

乐观锁实现方式:

  • 取出记录时,获取当前 version

  • 更新时,带上这个 version

  • 执行更新时, set version = newVersion where version = oldVersion

  • 如果 version 不对,就更新失败

举例说明:

线程A要执行的SQL语句

update form user set name="aaa" ,version =version+1 where id='1' and  version =1;

在线程A还没有执行这个SQL语句之前,来了一个线程B同样执行这条SQL语句,会导致在线程A执行时version!=1,SQL语句执行错误.

在代码中实现乐观锁

  • 在数据库表中添加version字段,并将其defualt值为1

  • 在实体类中同步数据库字段,并在version字段上面添加@Version注解

    @Version //乐观锁version注解
    private Integer version;
    
  • 注册组件

    • spring xml 方式:

      <bean class="com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor" id="optimisticLockerInnerInterceptor"/>
      <bean id="mybatisPlusInterceptor" class="com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor">
      <property name="interceptors">
      <list>
      <ref bean="optimisticLockerInnerInterceptor"/>
      </list>
      </property>
      </bean>
      
    • spring boot 注解方式:

      @Bean
      public MybatisPlusInterceptor mybatisPlusInterceptor() {
      MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
      interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
      return interceptor;
      }
      

测试:

成功:

    @Test
    public void versionTest (){
        user user= userMapper.selectById(1);
        System.out.println(user);
        user.setName("zhou");
        user.setAge(5);
        int i = userMapper.updateById(user);
    }

image-20230612134203575

失败:

    @Test
    public void versionTest (){
        user user= userMapper.selectById(1);
        System.out.println(user);
        user.setName("zhou");
        user.setAge(5);
        user user2= userMapper.selectById(1);
        user2.setName("zhou");
        user2.setAge(6);
       userMapper.updateById(user2);
       userMapper.updateById(user);
    }
最终结果是age=6 age=5是不会成功的

条件构造器Wrapper

image-20231016233859532

介绍一个重要的方法

获取自定义SQL 简化自定义XML复杂情况
使用方法: `select xxx from table` + ${ew.customSqlSegment}
注意事项: 1. 逻辑删除需要自己拼接条件 (之前自定义也同样) 2. 不支持wrapper中附带实体的情况 (wrapper自带实体会更麻烦) 3. 用法 ${ew.customSqlSegment} (不需要where标签包裹,切记!) 4. ew是wrapper定义别名,不能使用其他的替换
    public String getCustomSqlSegment() {
    MergeSegments expression = getExpression();
    if (Objects.nonNull(expression)) {
        NormalSegmentList normal = expression.getNormal();
        String sqlSegment = getSqlSegment();
        if (StringUtils.isNotBlank(sqlSegment)) {
            if (normal.isEmpty()) {
                return sqlSegment;
            } else {
                return Constants.WHERE + StringPool.SPACE + sqlSegment;
            }
        }
    }
    return StringPool.EMPTY;
}

我们使用一些的复杂SQL就可以只用这个来替代

@SpringBootTest
public class WrapperTest {
    @Autowired
    private userMapper userMapper;
    @Test
    public void test01(){
        QueryWrapper<user> wrapper =new QueryWrapper<>();
        //注意:字段名并不是写你实体类里面的字段名称,而是写数据库里面的字段名称。
        wrapper.isNotNull("create_time");
        List<user> users = userMapper.selectList(wrapper);
        users.forEach(System.out::println);
    }
    @Test
    public void test02(){
        QueryWrapper<user> wrapper =new QueryWrapper<>();
        wrapper.like("name","L")
                .ge("age",6)
                .isNotNull("email");
        List<user> users = userMapper.selectList(wrapper);
    }
    @Test
    public void test03(){
        QueryWrapper<user> wrapper =new QueryWrapper<>();
        wrapper
                .notLike("name","z")
                .likeRight("email","t"); //相当于是t%
        List<Map<String, Object>> maps = userMapper.selectMaps(wrapper);
        maps.forEach(System.out::println);
    }
       @Test
    //条件子查询
    public void test04(){
        QueryWrapper<user> wrapper =new QueryWrapper<>();
        wrapper.inSql("id","select id from user where id>3");
        List<Object> objects = userMapper.selectObjs(wrapper);
        objects.forEach(System.out::println);
 }
}

注意test03()的查询结果是

image-20230613140626073

QueryWrapper介绍

select方法

主要功能:可以选择自己需要查询的列;

@Override
public QueryWrapper<T> select(String... columns) {
    if (ArrayUtils.isNotEmpty(columns)) {
        this.sqlSelect.setStringValue(String.join(StringPool.COMMA, columns));
    }
    return typedThis;
}

Select重载形式

//第一个参数是实体类的字节码对象。第二个参数是一个函数式接口,可以通过里面test返回的Boolean值,来选择该列是否查询。
public QueryWrapper<T> select(Class<T> entityClass, Predicate<TableFieldInfo> predicate)

UpdateWrapper介绍

实列将ID大于1的用户的年龄修改为99

update
      user
set 
      age=99
where 
      ID>1
UpdateWrapper<User>  updateWrapper=new UpdateWrapper<>();
updateWrapper.gt("id",1);
updateWrapper.set("age",99);
userMapper.update(null,updateWrapper);

查询操作与拓展

操作:①查询一个ID②查询一组ID

 @Test
    public void selectById(){
        //查询一个数据
        user user =userMapper.selectById(1L);
        System.out.println(user);
        //查询多个数据
        List<user> users = userMapper.selectBatchIds(Arrays.asList(1,2,3));
        users.forEach(System.out::println);
    }

按条件查询

简单的条件可以使用万能map来实现

    @Test
    public void selectBymap(){
        Map<String,Object> map =new HashMap<>();
        map.put("name","L");
        map.put("Id",9);
        List<user> users = userMapper.selectByMap(map);
        users.forEach(System.out::println);
    }

运行查看日志信息

复杂的添加比如大小于,或者不是查询所有字段的话就要使用Wrapper条件构造器了

分页查询

分页方法:

  • 使用limit

  • 使用pagehalper第三方插件

  • 使用mybaits-plus分页插件

mybatis-plus分页插件的使用最新版本

再mybatis-plus的配置类中添加内置插件配置
  @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2));
        return interceptor;
    }

老版本使用

    //分页插件
    @Bean
    public PaginationInterceptor paginationInterceptor(){
        return new PaginationInterceptor();
    }

然后就可以直接的去测试使用:

    @Test
    public  void selectByPages(){
        //第一个参数是每页几个
        //当前是第几页
        Page<user> page =new Page<>(1,5);
        userMapper.selectPage(page,null);
        page.getRecords().forEach(System.out::println);
    }

多表的分页查询

自定义方法,并将自定义的方式支持分页查询。

例如:将返回值和参数使用Page。MP会自动给我添加上LIMI语句

  IPage<User> findAll(Page<User>page);

删除操作及拓展

和查找操作一样有三种方式

  • 通过删除一个ID deleteById();

  • 通过删除一组ID deleteBatchIds(Arrays.asList(1,2,3,4))

  • 通过万能的map删除deleteByMap();

逻辑删除

物理删除:在数据库中直接删除掉

逻辑删除:没有在数据中移除数据,而是通过一个变量让他失效,比如使用deleted =0 = =>deleted=1

比如回收站里面,我们将自己不需要的文件丢道到回收站里面的操作就是逻辑删除。数据时还可以恢复的

mybatis-plus逻辑删除步骤:

  • 在数据库中增加一个deleted字段,表示逻辑删除的标识。官网说明

只对自动注入的 sql 起效:

插入: 不作限制

查找: 追加 where 条件过滤掉已删除数据,如果使用 wrapper.entity 生成的 where 条件也会自动追加该字段

更新: 追加 where 条件防止更新到已删除数据,如果使用 wrapper.entity 生成的 where 条件也会自动追加该字段

删除: 转变为 更新

例如:

删除: update user set deleted=1 where id = 1 and deleted=0

查找: select id,name,deleted from user where deleted=0

字段类型支持说明:

支持所有数据类型(推荐使用 Integer,Boolean,LocalDateTime)

如果数据库字段使用datetime,逻辑未删除值和已删除值支持配置为字符串null,另一个值支持配置为函数来获取值如now()

附录:

逻辑删除是为了方便数据恢复和保护数据本身价值等等的一种方案,但实际就是删除。

如果你需要频繁查出来看就不应使用逻辑删除,而是以一个状态去表示。

![image-20230613121015768](C:%5CUsers%5Cpc%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5Cimage-20230613121015768.png)

- 在yml文件中配置相关的配置

​```yml
mybatis-plus:
global-config:
  db-config:
    logic-delete-field: flag # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
    logic-delete-value: 1 # 逻辑已删除值(默认为 1)
    logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
  • 再在实体类中同步字段,并在deleted字段上标明逻辑字段的注释

    @TableLogic //逻辑删除注解
    private Integer deleted;
    

自定义方法

在原来mybatis

  User  findone(String mail);

使用mybatis-plus

 User  findone(@Param(Constants.WRAPPER) Wrapper<User> queryWrapper);

XML

<select id="findone" resultType="com.zl.note.pojo.User">
    select * from z_user ${ew.customSqlSegment}
</select>

注意这里使用${},因为如果使用#{}是不会预编译的,但是ew.customSqlSegment里面是where条件等语句是需要预编译的。

Service接口

MP也为我们提供了Serviqe层的实现。我们只需要编写一个接口,继承IService,并创建一个接口实现类继承serviceImpl,即可使用。

相比于Mapper接口,Service层主要是支持了更多批量操作的方法。

Service改造

public interface UserService extends IService<User>

serviceImp改造

public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService

ServiceImpl提供了一系列的方法

image-20231017122716688

自定义方法

那么我们需要注入Mapper对象吗,其实MP里面存在一个方法getBaseMapper就可以获得响应的Mapper对象。

代码自动生成器(重点)

导入依赖

 <!--mybatis-plus 依赖-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.3.4</version>
        </dependency>
        <!--mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.5.1</version>
        </dependency>
        <!--freemarker 依赖-->
        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.31</version>
        </dependency>

方法实体类

方法一:将所有的配置提前全部写好

package com.zhoulei.mybatis_plus01;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.IFill;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.DbColumnType;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import com.baomidou.mybatisplus.generator.fill.Column;
import com.baomidou.mybatisplus.generator.fill.Property;

import java.sql.Types;
import java.util.Collections;

import static com.baomidou.mybatisplus.generator.config.GlobalConfig.*;

public class Autobuild {
    public static void main(String[] args) {
        String outurl="/mybatis_plus01";
        System.out.println((System.getProperty("user.dir")+outurl+"/src/main/java"));
        FastAutoGenerator.create("jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&serverTimezone=GMT%2B8",
                "root",
                "123456"
                )
                //配置全局变量
            .globalConfig((Builder builder) -> {
                builder.author("zl") // 设置作者
                        .enableSwagger() // 开启 swagger 模式
                        .disableOpenDir() //禁止打开windows资源管理器
                        .dateType(DateType.ONLY_DATE)//日期类型
                        .fileOverride()//开启覆盖之前生成的文件
//                        .fileOverride()//覆盖已经生成的文件
                        .outputDir(System.getProperty("user.dir")+outurl+"/src/main/java"); // 指定输出目录 将生成的代码就会生成到指定的路径下面
            })
                //包配置
            .packageConfig((PackageConfig.Builder builder) -> {
                builder.parent("com.zhoulei.mybatis_plus01") // 设置父包名,在那个包下面生成代码
                        .moduleName("blog") // 设置父包模块名
                        .entity("pojo") //配置pojo包 实体类包的名称
                        .mapper("mapper") //mapper dao 层的名称
                        .controller("controller") //配置controller层名称
                        .service("service")//配置servicec层的名称
                        .serviceImpl("serviceImpl")
                        .xml("mapper")
                        .pathInfo(Collections.singletonMap(OutputFile.mapperXml, System.getProperty("user.dir")+outurl+"/src/main/resources/mapper")); // 设置mapperXml生成路径
            })
          //策略设置
            .strategyConfig((StrategyConfig.Builder builder) -> {
                builder.addInclude("user") // 设置需要映射数据库的表名,这个需要的参数是一 Set<String> include;可以是多个表
                        .addTablePrefix("t_", "c_") // 设置过滤表前缀
                        .entityBuilder() //它用于配置实体类的构建器 EntityBuilder,从而定制实体类的生成策略
                        .enableLombok()//lombok生成
                        .logicDeleteColumnName("deleted") //设置逻辑删除的字段名
                        .versionColumnName("version")//设置乐观锁的字段名
                        .addTableFills(new Column("create_time", FieldFill.INSERT) )//添加自动填充,比如插入时间
                        .addTableFills(new Property("updateTime",FieldFill.UPDATE))//添加自动填充,比如更新时间
                //两种方式一个是通过数据库的表名创建,另一个是通过实体类属性来创建
//                 .addTableFills(new Property("createTime", FieldFill.INSERT) )//添加自动填充,比如插入时间
//                  .addTableFills(new Column("update_time",FieldFill.UPDATE))//添加自动填充,比如更新时间
                      .idType(IdType.ASSIGN_ID)
                         .mapperBuilder()
                        .superClass(BaseMapper.class)
                        .enableBaseResultMap()//基础map映射
                        .enableBaseColumnList()//基础行列表映射
                        .formatMapperFileName("%sMapper")
                        .enableMapperAnnotation()
                        .formatXmlFileName("%sMapper")
                        .serviceBuilder()
                        .formatServiceFileName("%sService")
                        .formatServiceImplFileName("%sServiceImpl")
                        .controllerBuilder()
                        .formatFileName("%sController")
                        .enableRestStyle();
                ;
            }).templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
            .execute();


    }
}

//方法二:使用交互式

package com.zhoulei.mybatis_plus01;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import com.baomidou.mybatisplus.generator.fill.Column;
import com.baomidou.mybatisplus.generator.fill.Property;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.*;
import java.util.stream.Collectors;

@SpringBootTest
public class UserGenerator {
    public static void main(String[] args) {
        String property = System.getProperty("user.dir");//获取目录路径
        String database = getDatabase(); //数据库名称;
        String username= getUsername();//数据库用户名
        String password=getPassword();//数据库密码
        String outurl=getOuturl();//输出路径
        String author=getAuthor();//获取作者
        List <String> tables= getTables();//表名集合
        String mapperXml =getMapperXML();//mapperXml生成路径
        String prefix = gettablePrefix();//表的前缀名
        String parent =getParent();//父包名
        String moduelName=getmoduelName();//模块名
        FastAutoGenerator.create("jdbc:mysql://localhost:3306/"+database+"?useSSL=false&serverTimezone=GMT%2B8",
                username,
                password
        )
                //配置全局变量
                .globalConfig((GlobalConfig.Builder builder) -> {
                    builder.author(author) // 设置作者
                            .enableSwagger() // 开启 swagger 模式
                            .disableOpenDir() //禁止打开windows资源管理器
                            .dateType(DateType.ONLY_DATE)//日期类型
                            .fileOverride()//开启覆盖之前生成的文件
//                        .fileOverride()//覆盖已经生成的文件
                            .outputDir(property+outurl+"/src/main/java"); // 指定输出目录 将生成的代码就会生成到指定的路径下面
                })
                //包配置
                .packageConfig((PackageConfig.Builder builder) -> {
                    builder.parent(parent) // 设置父包名,在那个包下面生成代码
                            .moduleName(moduelName) // 设置父包模块名
                            .entity("pojo") //配置pojo包 实体类包的名称
                            .mapper("mapper") //mapper dao 层的名称
                            .controller("controller") //配置controller层名称
                            .service("service")//配置servicec层的名称
                            .serviceImpl("serviceImpl")
                            .xml("mapper")
                            .pathInfo(Collections.singletonMap(OutputFile.mapperXml, System.getProperty("user.dir")+outurl+"/src/main/"+mapperXml)); // 设置mapperXml生成路径
                })
                //策略设置
                .strategyConfig((StrategyConfig.Builder builder) -> {
                    builder.addInclude(tables) // 设置需要映射数据库的表名,这个需要的参数是一 Set<String> include;可以是多个表
                            .addTablePrefix(prefix) // 设置过滤表前缀
                            .entityBuilder() //它用于配置实体类的构建器 EntityBuilder,从而定制实体类的生成策略
                            .enableLombok()//lombok生成
                            .logicDeleteColumnName("deleted") //设置逻辑删除的字段名
                            .versionColumnName("version")//设置乐观锁的字段名
                            .addTableFills(new Column("create_time", FieldFill.INSERT) )//添加自动填充,比如插入时间
                            .addTableFills(new Property("updateTime",FieldFill.UPDATE))//添加自动填充,比如更新时间
                            //两种方式一个是通过数据库的表名创建,另一个是通过实体类属性来创建
//                 .addTableFills(new Property("createTime", FieldFill.INSERT) )//添加自动填充,比如插入时间
//                  .addTableFills(new Column("update_time",FieldFill.UPDATE))//添加自动填充,比如更新时间
                            .idType(IdType.ASSIGN_ID)
                            .mapperBuilder()
                            .superClass(BaseMapper.class)
                            .enableBaseResultMap()//基础map映射
                            .enableBaseColumnList()//基础行列表映射
                            .formatMapperFileName("%sMapper")
                            .enableMapperAnnotation()
                            .formatXmlFileName("%sMapper")
                            .serviceBuilder()
                            .formatServiceFileName("%sService")
                            .formatServiceImplFileName("%sServiceImpl")
                            .controllerBuilder()
                            .formatFileName("%sController")
                            .enableRestStyle();
                    ;
                }).templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
                .execute();


    }

    //输入数据库名称
    public static String getDatabase() {
        Scanner sc = new Scanner(System.in);
        System.out.println("输入数据库名称");
        return sc.next();
    }

    //输入数据库用户名
    public static String getUsername() {
        Scanner sc = new Scanner(System.in);
        System.out.println("输入数据库用户名");
        return sc.next();
    }

    //输入数据库密码
    public static String getPassword() {
        Scanner sc = new Scanner(System.in);
        System.out.println("输入数据库密码");
        return sc.next();
    }

    //输入输出路径
    public static String getOuturl() {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入生成文件输入路径,建议输入项目根目录到src目录的距离");
        System.out.println("例如我当前总项目叫【project】那么我输入【/hy-generator】那么我输出路径为为【/hy-generator/src/main/java】");
        String outurl = sc.next();
        if (outurl.trim().equals("")) {
            outurl = "/";
        }
        return outurl;
    }

    //输入作者
    public static String getAuthor() {
        Scanner sc = new Scanner(System.in);
        System.out.println("输入作者");
        return sc.next();
    }

    //输入表名
    public static List <String> getTables() {
        Scanner sc = new Scanner(System.in);
        List<String> tables = new ArrayList<>();
        System.out.println("你可以输入一个或这是多个表名,但是要以','分隔开,并且以'$'标识结束");
        while (sc.hasNext()) {
            String table = sc.next();
            if(table.equals("$")){
                return tables;
            }
            String[] split = table.split(",");
            for (int i = 0; i < split.length; i++) {
                tables.add(split[i]);
            }
        }
        return tables;
    }
    //输入mapperXml生成路径
    public static String getMapperXML() {
        Scanner sc = new Scanner(System.in);
        System.out.println("mapperXml生成路径");
        System.out.println("比如是在/src/main/java/resources/mapper,就输入/resources/mapper");
        return sc.next();
    }
    //表前缀输入
    private static String gettablePrefix() {
        System.out.println("请输入表前缀");
        System.out.println("例如我数据库表叫【t_admin】那么我输入【t_】那么我的实体类名为【Admin】");
        Scanner scanner = new Scanner(System.in);
        String tablePrefix = scanner.next();
        System.out.println("你输入的表前缀是:" + tablePrefix);
        return tablePrefix;
    }
    //输入父包名
    private static String getParent() {
        System.out.println("请输入父包名");
        System.out.println("例如我的父包名是com.zl.mybatis_plus01,那写service,dao,controller层都会生成在该报下");
        Scanner sc = new Scanner(System.in);
        return sc.next();
    }
    //输入moduelName名
    private static String getmoduelName() {
        System.out.println("请输入moduelName");
        Scanner sc = new Scanner(System.in);
        return sc.next();
    }
}