mybatis-plus使用+generator逆向工程

MyBatis-Plus (opens new window) (简称 MP)是一个  MyBatis (opens new window) 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。官网地址:  简介 | MyBatis-Plus
用idea开发的建议安装一个mybatisx的插件,可以实现xml和dao层之间的跳转以及天生自带generator的功能
本文将对mybatis-plus引入到工程以及generator逆向工程两个功能的着重描述配置

mybatis-plus引入到工程

1、pom文件引入mybatis-plus


    com.baomidou
    mybatis-plus





    com.baomidou
    mybatis-plus-boot-starter

2、application.yml文件配置多数据源

spring:
  application:
    name: user
    #自定义模块名name-ip,logback通过该字段区分日志来源
    moduleName: user-dev
  # 出现404错误时,抛出异常给应用层处理
  mvc:
    throw-exception-if-no-handler-found: true
  resources:
    add-mappings: false #告诉 SpringBoot 不要为我们工程中的资源文件建立映射
  redis:
    host: 192.168.1.1         # 服务器地址
    port: 6379                # 服务端口号
    database: 0               # 数据库索引
    password: 1               # 服务器连接密码默认为空
    jedis:
      pool:
        max-active: 1000  # 连接池最大连接数
        max-wait: 3000  # 连接池最大阻塞等待时间
        max-idle: 500    # 连接池中最大的空闲连接
        min-idle: 100     # 连接池中最小的空闲连接
        time-between-eviction-runs: 60000
    timeout: 200       # 连接超时时间


  jackson:
    time-zone: Asia/Shanghai
    date-format: yyyy-MM-dd HH:mm:ss
  # jdbc_config   datasource
  datasource:
    dynamic:
      primary: master  #设置默认的数据源或者数据源组,默认值即为master
      strict: false    #设置严格模式,默认false不启动. 启动后在未匹配到指定数据源时候会抛出异常,不启动则使用默认数据源.
      datasource:
        master:
          url: jdbc:mysql://192.168.1.1:3306/user?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai
          username: root
          password: 123456
          driver-class-name: com.mysql.cj.jdbc.Driver
          type: com.zaxxer.hikari.HikariDataSource
          hikari:
            minimum-idle: 5
            maximum-pool-size: 15
            auto-commit: true
            idle-timeout: 30000
            pool-name: springHikariCP
            max-lifetime: 1800000
            connection-timeout: 30000
            connection-test-query: SELECT 1
        slave:
          url: jdbc:mysql://192.168.1.1:3306/user2?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai
          username: root
          password: 123456
          driver-class-name: com.mysql.cj.jdbc.Driver
          type: com.zaxxer.hikari.HikariDataSource
          hikari:
            minimum-idle: 5
            maximum-pool-size: 15
            auto-commit: true
            idle-timeout: 30000
            pool-name: springHikariCP
            max-lifetime: 1800000
            connection-timeout: 30000
            connection-test-query: SELECT 1


mybatis-plus:
  # 配置mapper的扫描,找到所有的mapper.xml映射文件
  mapper-locations: classpath:mybatis/mapper/**/*Mapper.xml
  global-config:
    # 主键类型  0:"数据库ID自增", 1:"用户输入ID",2:"全局唯一ID (数字类型唯一ID)", 3:"全局唯一ID UUID";
    id-type: 0


#  configuration:
#    # 是否将sql打印到控制面板(该配置会将sql语句和查询的结果都打印到控制台)
#    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

3、主键id特殊功能实现

实现主键id既可以采用mysql的自增功能也可以手动传入id保存到db功能,此时需要对象属性为 @TableId (value = "id" , type = IdType . INPUT ), 如果设置为auto则无法手动传入id,如果简单设置为input则只能手动输入id无法自增,为了同时满足这两个条件,需要重写默认的sql注入器 DefaultSqlInjector的insert方法。
解释说明:如果对象属性的注解什么都不设置则是默认的雪花算法模型,但是雪花算法模型的缺点比较难以接受,比如生成的长度过长,长度短就容易分布式冲突,长度长会影响mysql索引性能,比较主键都是聚集索引,这类型索引会在每次插入数据的时候都会排好序,排序涉及到比较compare,字符过长影响比较性能,另外也会比较占用索引节点空间导致树的高度比较高,所以建议还是用mysql自增id
public class MySqlInjector extends DefaultSqlInjector {
    @Override
    public List getMethodList(Class mapperClass) {
        List methodList = super.getMethodList(mapperClass);
        methodList.removeIf(e -> e instanceof Insert);
        methodList.add(new com.user.common.injector.method.Insert());
        return methodList;
    }
}

@SuppressWarnings("all")
public class Insert extends AbstractMethod {


    @Override
    public MappedStatement injectMappedStatement(Class mapperClass, Class modelClass, TableInfo tableInfo) {
        KeyGenerator keyGenerator = new NoKeyGenerator();
        SqlMethod sqlMethod = SqlMethod.INSERT_ONE;
        String columnScript = SqlScriptUtils.convertTrim(tableInfo.getAllInsertSqlColumnMaybeIf(null),
                LEFT_BRACKET, RIGHT_BRACKET, null, COMMA);
        String valuesScript = SqlScriptUtils.convertTrim(tableInfo.getAllInsertSqlPropertyMaybeIf(null),
                LEFT_BRACKET, RIGHT_BRACKET, null, COMMA);
        String keyProperty = null;
        String keyColumn = null;
        // 表包含主键处理逻辑,如果不包含主键当普通字段处理
        if (StringUtils.isNotBlank(tableInfo.getKeyProperty())) {
            keyGenerator = new Jdbc3KeyGenerator();
            keyProperty = tableInfo.getKeyProperty();
            keyColumn = tableInfo.getKeyColumn();
        }
        String sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(), columnScript, valuesScript);
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
        return this.addInsertMappedStatement(mapperClass, modelClass, getMethod(sqlMethod), sqlSource, keyGenerator, keyProperty, keyColumn);
    }
}

4、mybatis-plus的数据源配置,多数据源、分表、以上3点的自增id实现、自动转换表操作等功能需要在此注入进来

@EnableTransactionManagement
@Configuration
@MapperScan("com.user.*.*.mapper")
public class MyBatisConfig {


    @Resource
    private DataSource myRoutingDataSource;

    @Autowired
    private MybatisPlusInterceptor mybatisPlusInterceptor;

    @Value("${mybatis-plus.mapper-locations}")
    private String mapperLocations;


    @Bean
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        MybatisSqlSessionFactoryBean sqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
        sqlSessionFactoryBean.setPlugins(new Interceptor[]{mybatisPlusInterceptor});
        sqlSessionFactoryBean.setDataSource(myRoutingDataSource);
        GlobalConfig globalConfig=new GlobalConfig();
        globalConfig.setSqlInjector(new MySqlInjector());
        sqlSessionFactoryBean.setGlobalConfig(globalConfig);
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        sqlSessionFactoryBean.setMapperLocations(resolver.getResources(mapperLocations));
        // SpringBoot项目集成mybatis打包为jar运行时setTypeAliasesPackage无效解决
        VFS.addImplClass(SpringBootVFS.class);
        return sqlSessionFactoryBean.getObject();
    }


    public DynamicTableNameInnerInterceptor getDynamicTableNameInnerInterceptor(){
        DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor = new DynamicTableNameInnerInterceptor();
        HashMap map = new HashMap(2) {{
            put("common",(sql, tableName) -> {
                //common 代表可能向任何一张表进行操作
                return ThreadLocalUtil.get("tableName");
            });
        }};
        dynamicTableNameInnerInterceptor.setTableNameHandlerMap(map);
        return dynamicTableNameInnerInterceptor;
    }




    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        //分表拦截器
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(getDynamicTableNameInnerInterceptor());
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }

mybatis generator自动生成器

首先需要先引入pom依赖freemarker



    org.springframework.boot
    spring-boot-starter-freemarker

1、在resources/template下创建模板文件

包括对象entity、操作接口dao、service服务接口以及其实现类,entity对象将支持swagger、lombokModel注解生成、数据库表映射注解、以及父子类集成

mybatis-plus使用+generator逆向工程_第1张图片

entity.java.ftl文件如下

此文件对主键的处理,采用input方式,使之可传可不传id,并能够利用mysql的自增id

此文件将会从数据库表中读取信息,自动生成字段注释和字段默认值

package ${package.Entity};


<#list table.importPackages as pkg>
import ${pkg};

<#if swagger2>
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;

<#if entityLombokModel>
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;



/**
* 

* ${table.comment!} *

* * @author ${author} * @since ${date} */ <#if entityLombokModel> @Data <#if superEntityClass??> @EqualsAndHashCode(callSuper = true) <#else> @EqualsAndHashCode(callSuper = false) @Accessors(chain = true) <#if table.convert> @TableName("${table.name}") <#if swagger2> @ApiModel(value="${table.comment!}", description="${table.comment!}") <#if superEntityClass??> public class ${entity} extends ${superEntityClass}<#if activeRecord><${entity}> { <#elseif activeRecord> public class ${entity} extends Model<${entity}> { <#else> public class ${entity} implements Serializable { private static final long serialVersionUID = 1L; <#-- ---------- BEGIN 字段循环遍历 ----------> <#list table.fields as field> <#if field.keyFlag> <#assign keyPropertyName="${field.propertyName}"/> <#if field.comment!?length gt 0> <#if swagger2> @ApiModelProperty(value = "${field.comment}") <#else> /** * ${field.comment} */ <#if field.keyFlag> <#-- 主键 --> <#if field.keyIdentityFlag> @TableId(value = "${field.name}", type = IdType.AUTO) <#elseif idType??> @TableId(value = "${field.name}", type = IdType.${idType}) <#elseif field.convert> @TableId("${field.name}") <#-- 普通字段 --> <#elseif field.fill??> <#-- ----- 存在字段填充设置 -----> <#if field.convert> @TableField(value = "${field.name}", fill = FieldFill.${field.fill}) <#else> @TableField(fill = FieldFill.${field.fill}) <#elseif field.convert> @TableField("${field.name}") <#-- 乐观锁注解 --> <#if (versionFieldName!"") == field.name> @Version <#-- 逻辑删除注解 --> <#if (logicDeleteFieldName!"") == field.name> @TableLogic private ${field.propertyType} ${field.propertyName}; <#------------ END 字段循环遍历 ----------> <#if !entityLombokModel> <#list table.fields as field> <#if field.propertyType == "boolean"> <#assign getprefix="is"/> <#else> <#assign getprefix="get"/> public ${field.propertyType} ${getprefix}${field.capitalName}() { return ${field.propertyName}; } <#if entityBuilderModel> public ${entity} set${field.capitalName}(${field.propertyType} ${field.propertyName}) { <#else> public void set${field.capitalName}(${field.propertyType} ${field.propertyName}) { this.${field.propertyName} = ${field.propertyName}; <#if entityBuilderModel> return this; } <#if entityColumnConstant> <#list table.fields as field> public static final String ${field.name?upper_case} = "${field.name}"; <#if activeRecord> @Override protected Serializable pkVal() { <#if keyPropertyName??> return this.${keyPropertyName}; <#else> return null; } <#if !entityLombokModel> @Override public String toString() { return "${entity}{" + <#list table.fields as field> <#if field_index==0> "${field.propertyName}=" + ${field.propertyName} + <#else> ", ${field.propertyName}=" + ${field.propertyName} + "}"; } }

mapper.java.ftl

此文件将会生成mapper类,这个类将会集成BaseMapper类(Mapper 继承该接口后,无需编写 mapper.xml 文件,即可获得CRUD功能)

package ${package.Mapper};


import ${package.Entity}.${entity};
import ${superMapperClassPackage};


/**
* 

* ${table.comment!} Mapper 接口 *

* * @author ${author} * @since ${date} */ <#if kotlin> interface ${table.mapperName} : ${superMapperClass}<${entity}> <#else> public interface ${table.mapperName} extends ${superMapperClass}<${entity}> { }

mapper.xml.ftl

此文件申明开启二级缓存,并生成一个mapper映射文件,这个映射文件只编码了映射关系,没有实际的crud操作






<#if enableCache>
    
    



<#if baseResultMap>
    
    
<#list table.fields as field>
<#if field.keyFlag><#--生成主键排在第一位-->
        


<#list table.commonFields as field><#--生成公共字段 -->
    

<#list table.fields as field>
<#if !field.keyFlag><#--生成普通字段 -->
        


    



<#if baseColumnList>
    
    
<#list table.commonFields as field>
        ${field.name},

        ${table.fieldNames}
    



服务接口

service.java.ftl

package ${package.Service};


import ${package.Entity}.${entity};
import ${superServiceClassPackage};


/**
* 

* ${table.comment!} 服务类 *

* * @author ${author} * @since ${date} */ <#if kotlin> interface ${table.serviceName} : ${superServiceClass}<${entity}> <#else> public interface ${table.serviceName} extends ${superServiceClass}<${entity}> { }

serviceImpl.java.ftl

服务dao实现类

package ${package.ServiceImpl};


import ${package.Entity}.${entity};
import ${package.Mapper}.${table.mapperName};
import ${package.Service}.${table.serviceName};
import ${superServiceImplClassPackage};
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;


/**
* 

* ${table.comment!} 服务实现类 *

* * @author ${author} * @since ${date} */ @Slf4j @Service <#if kotlin> open class ${table.serviceImplName} : ${superServiceImplClass}<${table.mapperName}, ${entity}>(), ${table.serviceName} { } <#else> @Transactional(rollbackFor = Exception.class) public class ${table.serviceImplName} extends ${superServiceImplClass}<${table.mapperName}, ${entity}> implements ${table.serviceName} { }

2、将以上一步的模板文件用来生成真正的class类文件到工程目录中

需要创建一个main方法MybatisPlusGenerator,其中“模块名”将会被拼接成com.xxx.user.db.dao....这样的文件路径

public static void main(String[] args) {
    MybatisPlusGenerator.generate("username", "数据库名称", new String[]{"表名"},
            "springcloud模块文件夹名","模块名", true, true);
}
public class MybatisPlusGenerator {

    public static void generate(String author, String dataBase, String[] tables, String subProjectName,
                                String moduleName, boolean isExtendSuper, boolean isCreateDao ) {

        // 代码生成器
        AutoGenerator mpg = new AutoGenerator();
        String projectPath = System.getProperty("user.dir")+"/"+subProjectName;
        mpg.setGlobalConfig(getGlobalConfig(projectPath, author));
        // 数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://192.168.1.1:3306/" + dataBase + "?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=UTC");
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("123456");
        mpg.setDataSource(dsc);
        mpg.setPackageInfo(getPackageConfig(moduleName));
        // 自定义配置
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() {
            }
        };
        List focList = new ArrayList<>();
        focList.add(new FileOutConfig("/templates/mapper.xml.ftl") {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // 自定义输入文件名称
                return projectPath + "/src/main/resources/mybatis/mapper/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
            }
        });


        cfg.setFileOutConfigList(focList);
        mpg.setCfg(cfg);
        String daoTemplate = null;
        String daoITemplate = null;
        if(isCreateDao){
            daoITemplate = "template/mybatis/service.java";
            daoTemplate = "template/mybatis/serviceImpl.java";
        }
        mpg.setTemplate(new TemplateConfig()
                .setEntity("template/mybatis/entity.java")
                .setXml(null)
                .setController(null)
                .setService(daoITemplate)
                .setServiceImpl(daoTemplate));

        mpg.setStrategy(getStrategyConfig(tables,isExtendSuper));
        // 选择 freemarker 引擎需要指定如下加,注意 pom 依赖必须有!
        mpg.setTemplateEngine(new FreemarkerTemplateEngine());
        mpg.execute();


    }


    public static GlobalConfig getGlobalConfig(String projectPath, String author){
        // 全局配置
        GlobalConfig gc = new GlobalConfig();
        gc.setOutputDir(projectPath + "/src/main/java");
        gc.setAuthor(author);
        gc.setOpen(false);
        //是否开启swagger注释
        gc.setSwagger2(true);
        //是否覆盖文件
        gc.setFileOverride(true);
        //开启 BaseResultMap
        gc.setBaseResultMap(true);
        // baseColumnList
        gc.setBaseColumnList(true);
        //service改名为dao
        gc.setServiceName("I%sDao");
        gc.setServiceImplName("%sDaoImpl");
        return gc;
    }


    public static PackageConfig getPackageConfig(String moduleName){
        // 包配置
        PackageConfig pc = new PackageConfig();
        pc.setParent("com.xxx");
        pc.setModuleName(moduleName);


        pc.setController("controller");
        pc.setEntity("db.entity");
        pc.setService("db.dao");
        pc.setServiceImpl("db.dao.impl");
        pc.setMapper("db.mapper");
        return pc;
    }


    public static StrategyConfig getStrategyConfig(String[] tables, boolean isExtendSuper){
        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        if(isExtendSuper){
            strategy.setSuperEntityClass("com.xxx.common.generator.base.BaseEntity");
        }
        strategy.setSuperMapperClass("com.baomidou.mybatisplus.core.mapper.BaseMapper");
        strategy.setTablePrefix("ad");


        strategy.setEntityLombokModel(true);
        strategy.setInclude(tables);
        if(isExtendSuper){
            strategy.setSuperEntityColumns("id","creator","updator");
        }


        strategy.setSuperServiceClass(IDao.class);
        strategy.setSuperServiceImplClass(DaoImpl.class);
        strategy.setControllerMappingHyphenStyle(true);
        return strategy;
    }

以上生成entity文件由于需要继承BaseEntity类,此类主要是将表的通用字段进行抽象,至于创建时间和修改时间都不要传值,每次用mysql的默认值CURRENT_TIMESTAMP即可(前提是字段类型是timestamp)

@Data
public abstract class BaseEntity implements Serializable{


    private static final long serialVersionUID = 1L;

    @TableId(value = "id", type = IdType.INPUT)
    private Long id;

    @TableField(value = "creator", fill = FieldFill.INSERT)
    private String creator;

    @TableField(value = "updator", fill = FieldFill.INSERT_UPDATE)
    private String updator;

}

相关的自动生成crud的工具类到此已经完成,生成的路径如下

mybatis-plus使用+generator逆向工程_第2张图片

你可能感兴趣的:(mybatis,mybatis-plus,generator,spring,boot,后端)