Giter VIP home page Giter VIP logo

itfsw / mybatis-generator-plugin Goto Github PK

View Code? Open in Web Editor NEW
1.3K 64.0 397.0 1.16 MB

Mybatis Generator 代码生成插件拓展,增加:查询单条数据插件(SelectOneByExamplePlugin)、MySQL分页插件(LimitPlugin)、数据Model链式构建插件(ModelBuilderPlugin)、Example Criteria 增强插件(ExampleEnhancedPlugin)、Example 目标包修改插件(ExampleTargetPlugin)、批量插入插件(BatchInsertPlugin)、逻辑删除插件(LogicalDeletePlugin)、数据Model属性对应Column获取插件(ModelColumnPlugin)、存在即更新(UpsertPlugin)、Selective选择插入更新增强插件(SelectiveEnhancedPlugin)、Table增加前缀插件(TableSuffixPlugin)、自定义注释插件(CommentPlugin)、增量插件(IncrementsPlugin)、查询结果选择性返回插件(SelectSelectivePlugin)、乐观锁插件(OptimisticLockerPlugin)、LombokPlugin等拓展。

License: Apache License 2.0

Java 98.08% FreeMarker 1.92%
mybatis-generator plugin limit batchinsert selectone upsert mysql mybatis lombok

mybatis-generator-plugin's Introduction

这是 MyBatis Generator 插件的拓展插件包

应该说使用Mybatis就一定离不开MyBatis Generator这款代码生成插件,而这款插件自身还提供了插件拓展功能用于强化插件本身,官方已经提供了一些拓展插件,本项目的目的也是通过该插件机制来强化Mybatis Generator本身,方便和减少我们平时的代码开发量。

因为插件是本人兴之所至所临时发布的项目(本人已近三年未做JAVA开发,代码水平请大家见谅),但基本插件都是在实际项目中经过检验的请大家放心使用,但因为项目目前主要数据库为MySQL,Mybatis实现使用Mapper.xml方式,所以代码生成时对于其他数据库和注解方式的支持未予考虑,请大家见谅。

V1.3.x版本的测试基准基于mybatis-3.5.0,同时向下兼容V3.4.0(某些插件需要context节点配置mybatis版本信息[issues#70])。老版本参见分支V1.2.x

<context>
    <!-- 
        解决 批量插入插件(BatchInsertPlugin)在mybatis3.5.0以下版本无法返回自增主键的问题
        指定mybatis版本,让插件指定您所使用的mybatis版本生成对应代码
     -->
    <property name="mybatisVersion" value="3.4.0"/>
</context>

插件列表:


Maven引用:

<dependency>
  <groupId>com.itfsw</groupId>
  <artifactId>mybatis-generator-plugin</artifactId>
  <version>1.3.10</version>
</dependency>

MyBatis Generator 参考配置(插件依赖应该配置在mybatis-generator-maven-plugin插件依赖中[issues#6]

<!-- mybatis-generator 自动代码插件 -->
<plugin>
    <groupId>org.mybatis.generator</groupId>
    <artifactId>mybatis-generator-maven-plugin</artifactId>
    <version>1.3.7</version>
    <configuration>
        <!-- 配置文件 -->
        <configurationFile>src/main/resources/mybatis-generator.xml</configurationFile>
        <!-- 允许移动和修改 -->
        <verbose>true</verbose>
        <overwrite>true</overwrite>
    </configuration>
    <dependencies>
        <!-- jdbc 依赖 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.driver.version}</version>
        </dependency>
        <dependency>
            <groupId>com.itfsw</groupId>
            <artifactId>mybatis-generator-plugin</artifactId>
            <version>${mybatis.generator.plugin.version}</version>
        </dependency>
    </dependencies>
</plugin>

gradle集成[issues#41]),感谢masa-kunikata提供的脚本。

// https://gist.github.com/masa-kunikata/daaf0f51a8ab9b808f61805407e1654c
buildscript {
    repositories {
        maven { url "https://plugins.gradle.org/m2/" }
    }
    dependencies {
        classpath "gradle.plugin.com.arenagod.gradle:mybatis-generator-plugin:1.4"
    }
}

apply plugin: 'java-library'
apply plugin: "com.arenagod.gradle.MybatisGenerator"
apply plugin: 'eclipse'

sourceCompatibility = 1.8
targetCompatibility = 1.8


def mybatisGeneratorCore = 'org.mybatis.generator:mybatis-generator-core:1.3.7'
def itfswMybatisGeneratorPlugin = 'com.itfsw:mybatis-generator-plugin:1.3.9'

mybatisGenerator {
  verbose = false
  configFile = "config/mybatisGenerator/generatorConfig.xml"

  dependencies {
    mybatisGenerator project(':')
    mybatisGenerator itfswMybatisGeneratorPlugin
    mybatisGenerator mybatisGeneratorCore
  }
}

repositories {
    mavenCentral()
}

dependencies {
    compile mybatisGeneratorCore
    compile itfswMybatisGeneratorPlugin
    testCompile 'junit:junit:4.12'
}

def defaultEncoding = 'UTF-8'

compileJava {
    options.encoding = defaultEncoding
}
compileTestJava {
    options.encoding = defaultEncoding
}

1. 查询单条数据插件

对应表Mapper接口增加了方法
插件:

<!-- 查询单条数据插件 -->
<plugin type="com.itfsw.mybatis.generator.plugins.SelectOneByExamplePlugin"/>

使用:

public interface TbMapper {    
    /**
     * This method was generated by MyBatis Generator.
     * This method corresponds to the database table tb
     *
     * @mbg.generated
     * @project https://github.com/itfsw/mybatis-generator-plugin
     */
    Tb selectOneByExample(TbExample example);
    
    /**
     * This method was generated by MyBatis Generator.
     * This method corresponds to the database table tb
     *
     * @mbg.generated
     * @project https://github.com/itfsw/mybatis-generator-plugin
     */
    // Model WithBLOBs 时才有
    TbWithBLOBs selectOneByExampleWithBLOBs(TbExample example);
}

2. MySQL分页插件

对应表Example类增加了Mysql分页方法,limit(Integer rows)、limit(Integer offset, Integer rows)和page(Integer page, Integer pageSize)

warning: 分页默认从0开始,目前网上流行的大多数前端框架分页都是从0开始,插件保持这种方式(可通过配置startPage参数修改);

插件:

<!-- MySQL分页插件 -->
<plugin type="com.itfsw.mybatis.generator.plugins.LimitPlugin">
    <!-- 通过配置startPage影响Example中的page方法开始分页的页码,默认分页从0开始 -->
    <property name="startPage" value="0"/>
</plugin>

使用:

public class TbExample {
    /**
     * This field was generated by MyBatis Generator.
     * This field corresponds to the database table tb
     *
     * @mbg.generated
     * @project https://github.com/itfsw/mybatis-generator-plugin
     */
    protected Integer offset;

    /**
     * This field was generated by MyBatis Generator.
     * This field corresponds to the database table tb
     *
     * @mbg.generated
     * @project https://github.com/itfsw/mybatis-generator-plugin
     */
    protected Integer rows;
    
    /**
     * This method was generated by MyBatis Generator.
     * This method corresponds to the database table tb
     *
     * @mbg.generated
     * @project https://github.com/itfsw/mybatis-generator-plugin
     */
    public TbExample limit(Integer rows) {
        this.rows = rows;
        return this;
    }

    /**
     * This method was generated by MyBatis Generator.
     * This method corresponds to the database table tb
     *
     * @mbg.generated
     * @project https://github.com/itfsw/mybatis-generator-plugin
     */
    public TbExample limit(Integer offset, Integer rows) {
        this.offset = offset;
        this.rows = rows;
        return this;
    }

    /**
     * This method was generated by MyBatis Generator.
     * This method corresponds to the database table tb
     *
     * @mbg.generated
     * @project https://github.com/itfsw/mybatis-generator-plugin
     */
    public TbExample page(Integer page, Integer pageSize) {
        this.offset = page * pageSize;
        // !!! 如果配置了startPage且不为0
        // this.offset = (page - startPage) * pageSize;
        this.rows = pageSize;
        return this;
    }
    
    // offset 和 rows 的getter&setter
    
    // 修正了clear方法
    /**
     * This method was generated by MyBatis Generator.
     * This method corresponds to the database table tb
     *
     * @mbg.generated
     */
    public void clear() {
        oredCriteria.clear();
        orderByClause = null;
        distinct = false;
        rows = null;
        offset = null;
    }
}
public class Test {
    public static void main(String[] args) {
        this.tbMapper.selectByExample(
                new TbExample()
                .createCriteria()
                .andField1GreaterThan(1)
                .example()
                .limit(10)  // 查询前10条
                .limit(10, 10)  // 查询10~20条
                .page(1, 10)    // 查询第2页数据(每页10条)
        );
    }
}

3. 数据Model链式构建插件

这个是仿jquery的链式调用强化了表的Model的赋值操作
插件:

<!-- 数据Model链式构建插件 -->
<plugin type="com.itfsw.mybatis.generator.plugins.ModelBuilderPlugin"/>

使用:

public class Test {
    public static void main(String[] args) {
        // 直接new表Model的内部Builder类,赋值后调用build()方法返回对象
        Tb table = new Tb.Builder()
               .field1("xx")
               .field2("xx")
               .field3("xx")
               .field4("xx")
               .build();
        // 或者使用builder静态方法创建Builder
        Tb table = Tb.builder()
               .field1("xx")
               .field2("xx")
               .field3("xx")
               .field4("xx")
               .build();
    }
}

4. Example 增强插件(example,andIf,orderBy)

  • Criteria的快速返回example()方法。
  • Criteria链式调用增强,以前如果有按条件增加的查询语句会打乱链式查询构建,现在有了andIf(boolean ifAdd, CriteriaAdd add)方法可一直使用链式调用下去。
  • Example增强了setOrderByClause方法,新增orderBy(String orderByClause)、orderBy(String ... orderByClauses)方法直接返回example,增强链式调用,配合数据Model属性对应Column获取插件(ModelColumnPlugin)使用效果更佳。
  • 增加基于column的操作,当配置了数据Model属性对应Column获取插件(ModelColumnPlugin)插件时,提供column之间的比对操作。
  • 增加createCriteria静态方法newAndCreateCriteria简写example的创建。
  • 增加when方法(Example和Criteria都有),方便根据不同条件附加对应操作。

插件:

<!-- Example Criteria 增强插件 -->
<plugin type="com.itfsw.mybatis.generator.plugins.ExampleEnhancedPlugin">
    <!-- 是否支持已经过时的andIf方法(推荐使用when代替),默认支持 -->
    <property name="enableAndIf" value="true"/>
</plugin>

使用:

public class Test {
    public static void main(String[] args) {
        // -----------------------------------example-----------------------------------
        // 表Example.Criteria增加了工厂方法example()支持,使用后可链式构建查询条件使用example()返回Example对象
        this.tbMapper.selectByExample(
                new TbExample()
                .createCriteria()
                .andField1EqualTo(1)
                .andField2EqualTo("xxx")
                .example()
        );
        
        // ----------------- andIf (@Deprecated 尽量使用when代替)  ---------------------
        // Criteria增强了链式调用,现在一些按条件增加的查询条件不会打乱链式调用了
        // old
        TbExample oldEx = new TbExample();
        TbExample.Criteria criteria = oldEx
                .createCriteria()
                .andField1EqualTo(1)
                .andField2EqualTo("xxx");
        // 如果随机数大于0.5,附加Field3查询条件
        if (Math.random() > 0.5){
            criteria.andField3EqualTo(2)
                    .andField4EqualTo(new Date());
        }
        this.tbMapper.selectByExample(oldEx);

        // new
        this.tbMapper.selectByExample(
                new TbExample()
                .createCriteria()
                .andField1EqualTo(1)
                .andField2EqualTo("xxx")
                // 如果随机数大于0.5,附加Field3查询条件
                .andIf(Math.random() > 0.5, new TbExample.Criteria.ICriteriaAdd() {
                    @Override
                    public TbExample.Criteria add(TbExample.Criteria add) {
                        return add.andField3EqualTo(2)
                                .andField4EqualTo(new Date());
                    }
                })
                // 当然最简洁的写法是采用java8的Lambda表达式,当然你的项目是Java8+
                .andIf(Math.random() > 0.5, add -> add
                        .andField3EqualTo(2)
                        .andField4EqualTo(new Date())
                )
                .example()
        );
        
        // -----------------------------------when-----------------------------------
        this.tbMapper.selectByExample(
                TbExample.newAndCreateCriteria()
                // 如果随机数大于1,附加Field3查询条件
                .when(Math.random() > 1, new TbExample.ICriteriaWhen() {
                    @Override
                    public void criteria(TbExample.Criteria criteria) {
                        criteria.andField3EqualTo(2);
                    }
                })
                // 当然最简洁的写法是采用java8的Lambda表达式,当然你的项目是Java8+
                .when(Math.random() > 1, criteria -> criteria.andField3EqualTo(2))
                // 也支持 if else 这种写法
                .when(Math.random() > 1, criteria -> criteria.andField3EqualTo(2), criteria -> criteria.andField3EqualTo(3))
                .example()
                // example上也支持 when 方法
                .when(true, example -> example.orderBy("field1 DESC"))
        );
        
        // -----------------------------------orderBy-----------------------------------
        // old
        TbExample ex = new TbExample();
        ex.createCriteria().andField1GreaterThan(1);
        ex.setOrderByClause("field1 DESC");
        this.tbMapper.selectByExample(ex);

        // new
        this.tbMapper.selectByExample(
                new TbExample()
                .createCriteria()
                .andField1GreaterThan(1)
                .example()
                .orderBy("field1 DESC")
                // 这个配合数据Model属性对应Column获取插件(ModelColumnPlugin)使用
                .orderBy(Tb.Column.field1.asc(), Tb.Column.field3.desc())
        );
        
        // -----------------------------------column-----------------------------------
        this.tbMapper.selectByExample(
                new TbExample()
                .createCriteria()
                .andField1EqualToColumn(Tb.Column.field2)   // where field1 = field2
                .andField1NotEqualToColumn(Tb.Column.field2)    // where field1 <> field2
                .andField1GreaterThanColumn(Tb.Column.field2)   // where field1 > field2
                .andField1GreaterThanOrEqualToColumn(Tb.Column.field2)  // where field1 >= field2
                .andField1LessThanColumn(Tb.Column.field2)  // where field1 < field2
                .andField1LessThanOrEqualToColumn(Tb.Column.field2) // where field1 <= field2
                .example()
        );
        
        // ---------------------------- static createCriteria  -----------------------
        // simple
        this.tbMapper.selectByExample(
                new TbExample()
                .createCriteria()
                .example()
        );
        // new
        this.tbMapper.selectByExample(
                TbExample.newAndCreateCriteria()
                .example()
        );
    }
}

5. Example 目标包修改插件

Mybatis Generator 插件默认把Model类和Example类都生成到一个包下,这样该包下类就会很多不方便区分,该插件目的就是把Example类独立到一个新包下,方便查看。
插件:

<!-- Example 目标包修改插件 -->
<plugin type="com.itfsw.mybatis.generator.plugins.ExampleTargetPlugin">
    <!-- 修改Example类生成到目标包下 -->
    <property name="targetPackage" value="com.itfsw.mybatis.generator.dao.example"/>
</plugin>

6. 批量插入插件

提供了批量插入batchInsert和batchInsertSelective方法,需配合数据Model属性对应Column获取插件(ModelColumnPlugin)插件使用,实现类似于insertSelective插入列!

warning: 插件生成的batchInsertSelective方法在使用时必须指定selective列,因为插件本身是预编译生成sql,对于批量数据是无法提供类似insertSelective非空插入的方式的;

插件:

<!-- 批量插入插件 -->
<plugin type="com.itfsw.mybatis.generator.plugins.BatchInsertPlugin">
    <!-- 
    开启后可以实现官方插件根据属性是否为空决定是否插入该字段功能
    !需开启allowMultiQueries=true多条sql提交操作,所以不建议使用!插件默认不开启
    -->
    <property name="allowMultiQueries" value="false"/>
</plugin>

使用:

public class Test {
    public static void main(String[] args) {
        // 构建插入数据
        List<Tb> list = new ArrayList<>();
        list.add(
                Tb.builder()
                .field1(0)
                .field2("xx0")
                .field3(0)
                .createTime(new Date())
                .build()
        );
        list.add(
                Tb.builder()
                .field1(1)
                .field2("xx1")
                .field3(1)
                .createTime(new Date())
                .build()
        );
        // 普通插入,插入所有列
        this.tbMapper.batchInsert(list);
        // !!!下面按需插入指定列(类似于insertSelective),需要数据Model属性对应Column获取插件(ModelColumnPlugin)插件
        this.tbMapper.batchInsertSelective(list, Tb.Column.field1, Tb.Column.field2, Tb.Column.field3, Tb.Column.createTime);
        // 或者排除某些列
        this.tbMapper.batchInsertSelective(list, Tb.Column.excludes(Tb.Column.id, Tb.Column.delFlag));
    }
}

7. 逻辑删除插件

因为很多实际项目数据都不允许物理删除,多采用逻辑删除,所以单独为逻辑删除做了一个插件,方便使用。

  • 增加logicalDeleteByExample和logicalDeleteByPrimaryKey方法;
  • 增加selectByPrimaryKeyWithLogicalDelete方法([pull#12]);
  • 查询构造工具中增加逻辑删除条件andLogicalDeleted(boolean);
  • 数据Model增加逻辑删除条件andLogicalDeleted(boolean);
  • 增加逻辑删除常量IS_DELETED(已删除 默认值)、NOT_DELETED(未删除 默认值)([issues#11]);
  • 增加逻辑删除枚举;

warning: 注意在配合状态枚举生成插件(EnumTypeStatusPlugin)使用时的注释格式,枚举数量必须大于等于2,且逻辑删除和未删除的值能在枚举中找到。

插件:

<xml>
    <!-- 逻辑删除插件 -->
    <plugin type="com.itfsw.mybatis.generator.plugins.LogicalDeletePlugin">
        <!-- 这里配置的是全局逻辑删除列和逻辑删除值,当然在table中配置的值会覆盖该全局配置 -->
        <!-- 逻辑删除列类型只能为数字、字符串或者布尔类型 -->
        <property name="logicalDeleteColumn" value="del_flag"/>
        <!-- 逻辑删除-已删除值 -->
        <property name="logicalDeleteValue" value="9"/>
        <!-- 逻辑删除-未删除值 -->
        <property name="logicalUnDeleteValue" value="0"/>
        
        <!-- 是否生成逻辑删除常量(只有开启时 logicalDeleteConstName、logicalUnDeleteConstName 才生效) -->
        <property name="enableLogicalDeleteConst" value="true"/>
        <!-- 逻辑删除常量名称,不配置默认为 IS_DELETED -->
        <property name="logicalDeleteConstName" value="IS_DELETED"/>
        <!-- 逻辑删除常量(未删除)名称,不配置默认为 NOT_DELETED -->
        <property name="logicalUnDeleteConstName" value="NOT_DELETED"/>
    </plugin>
    
    <table tableName="tb">
        <!-- 这里可以单独表配置逻辑删除列和删除值,覆盖全局配置 -->
        <property name="logicalDeleteColumn" value="del_flag"/>
        <property name="logicalDeleteValue" value="1"/>
        <property name="logicalUnDeleteValue" value="-1"/>
    </table>
</xml>

使用:

public class Test {
    public static void main(String[] args) {
        // 1. 逻辑删除ByExample
        this.tbMapper.logicalDeleteByExample(
                new TbExample()
                .createCriteria()
                .andField1EqualTo(1)
                .example()
        );

        // 2. 逻辑删除ByPrimaryKey
        this.tbMapper.logicalDeleteByPrimaryKey(1L);

        // 3. 同时Example中提供了一个快捷方法来过滤逻辑删除数据
        this.tbMapper.selectByExample(
                new TbExample()
                .createCriteria()
                .andField1EqualTo(1)
                // 新增了一个andDeleted方法过滤逻辑删除数据
                .andLogicalDeleted(true)
                // 当然也可直接使用逻辑删除列的查询方法,我们数据Model中定义了一个逻辑删除常量DEL_FLAG
                .andDelFlagEqualTo(Tb.IS_DELETED)
                .example()
        );
        
        // 4. 逻辑删除和未删除常量
        Tb tb = Tb.builder()
                .delFlag(Tb.IS_DELETED)   // 删除
                .delFlag(Tb.NOT_DELETED)    // 未删除
                .build()
                .andLogicalDeleted(true);   // 也可以在这里使用true|false设置逻辑删除

        // 5. selectByPrimaryKeyWithLogicalDelete V1.0.18 版本增加
        // 因为之前觉得既然拿到了主键这种查询没有必要,但是实际使用中可能存在根据主键判断是否逻辑删除的情况,这种场景还是有用的
        this.tbMapper.selectByPrimaryKeyWithLogicalDelete(1, true);
        
        // 6. 使用逻辑删除枚举
        Tb tb = Tb.builder()
                .delFlag(Tb.DelFlag.IS_DELETED)   // 删除
                .delFlag(Tb.DelFlag.NOT_DELETED)    // 未删除
                .build()
                .andLogicalDeleted(true);   // 也可以在这里使用true|false设置逻辑删除
    }
}

通过注解覆盖逻辑删除配置

CREATE TABLE `tb` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '注释1',
  `del_flag` smallint(3) COMMENT '注释[enable(1):第一项必须是代表未删除, disable(0):第二项必须是代表已删除, other(2):当然还可以附加其他状态]',
  PRIMARY KEY (`id`)
);
/**
 * 生成的Tb会根据注释覆盖逻辑删除配置
 */
public class Tb {
    public static final Short ENABLE = DelFlag.ENABLE.value();
    public static final Short DISABLE = DelFlag.DISABLE.value();
    
    public enum DelFlag {
        ENABLE(new Short("1"), "第一项必须是代表未删除"),
        DISABLE(new Short("0"), "第二项必须是代表已删除"),
        OTHER(new Short("2"), "当然还可以附加其他状态");
        
        private final Short value;
        private final String name;
        
        DelFlag(Short value, String name) {
            this.value = value;
            this.name = name;
        }
        
        public Short getValue() {
            return this.value;
        }
        public Short value() {
            return this.value;
        }
        public String getName() {
            return this.name;
        }
    }
}

8. 数据Model属性对应Column获取插件

项目中我们有时需要获取数据Model对应数据库字段的名称,一般直接根据数据Model的属性就可以猜出数据库对应column的名字,可是有的时候当column使用了columnOverride或者columnRenamingRule时就需要去看数据库设计了,所以提供了这个插件获取model对应的数据库Column。

  • 配合Example Criteria 增强插件(ExampleEnhancedPlugin)使用,这个插件还提供了asc()和desc()方法配合Example的orderBy方法效果更佳。
  • 配合批量插入插件(BatchInsertPlugin)使用,batchInsertSelective(@Param("list") List list, @Param("selective") XXX.Column ... insertColumns)。
  • 提供静态excludes方法来进行快速反选。

插件:

<!-- 数据Model属性对应Column获取插件 -->
<plugin type="com.itfsw.mybatis.generator.plugins.ModelColumnPlugin"/>

使用:

public class Test {
    public static void main(String[] args) {
        // 1. 获取Model对应column
        String column = Tb.Column.createTime.value();

        // 2. 配合Example Criteria 增强插件(ExampleEnhancedPlugin)使用orderBy方法
        // old
        this.tbMapper.selectByExample(
                new TbExample()
                .createCriteria()
                .andField1GreaterThan(1)
                .example()
                .orderBy("field1 DESC, field3 ASC")
        );

        // better
        this.tbMapper.selectByExample(
                new TbExample()
                .createCriteria()
                .andField1GreaterThan(1)
                .example()
                .orderBy(Tb.Column.field1.desc(), Tb.Column.field3.asc())
        );
        
        // 3. 配合批量插入插件(BatchInsertPlugin)使用实现按需插入指定列
        List<Tb> list = new ArrayList<>();
        list.add(
                Tb.builder()
                .field1(0)
                .field2("xx0")
                .field3(0)
                .field4(new Date())
                .build()
        );
        list.add(
                Tb.builder()
                .field1(1)
                .field2("xx1")
                .field3(1)
                .field4(new Date())
                .build()
        );
        // 这个会插入表所有列
        this.tbMapper.batchInsert(list);
        // 下面按需插入指定列(类似于insertSelective)
        this.tbMapper.batchInsertSelective(list, Tb.Column.field1, Tb.Column.field2, Tb.Column.field3, Tb.Column.createTime);
        
        // 4. excludes 方法
        this.tbMapper.batchInsertSelective(list, Tb.Column.excludes(Tb.Column.id, Tb.Column.delFlag));
        
        // 5. all 方法
        this.tbMapper.batchInsertSelective(list, Tb.Column.all());
    }
}

9. 存在即更新插件

  1. 使用MySQL的“insert ... on duplicate key update”实现存在即更新操作,简化数据入库操作([issues#2])。
  2. 在开启allowMultiQueries=true(默认不会开启)情况下支持upsertByExample,upsertByExampleSelective操作,但强力建议不要使用(需保证团队没有使用statement提交sql,否则会存在sql注入风险)([issues#2])。

插件:

<!-- 存在即更新插件 -->
<plugin type="com.itfsw.mybatis.generator.plugins.UpsertPlugin">
    <!-- 
    支持upsertByExample,upsertByExampleSelective操作
    !需开启allowMultiQueries=true多条sql提交操作,所以不建议使用!插件默认不开启
    -->
    <property name="allowMultiQueries" value="false"/>
    <!-- 
    开启批量功能,支持batchUpsert,batchUpsertWithBLOBs,batchUpserSelective 
    !这几个方法中无法支持IncrementsPlugin的方法!插件默认不开启
    -->
    <property name="allowBatchUpsert" value="fasle"/>
</plugin>

使用:

public class Test {
    public static void main(String[] args) {
        // 1. 未入库数据入库,执行insert
        Tb tb = Tb.builder()
                .field1(1)
                .field2("xx0")
                .delFlag(Tb.DEL_FLAG_ON)
                .build();
        int k0 = this.tbMapper.upsert(tb);
        // 2. 已入库数据再次入库,执行update(!!需要注意如触发update其返回的受影响行数为2)
        tb.setField2("xx1");
        int k1 = this.tbMapper.upsert(tb);

        // 3. 类似insertSelective实现选择入库
        Tb tb1 = Tb.builder()
                .field1(1)
                .field2("xx0")
                .build();
        int k2 = this.tbMapper.upsertSelective(tb1);
        tb1.setField2("xx1");
        int k3 = this.tbMapper.upsertSelective(tb1);

        // --------------------------------- allowMultiQueries=true ------------------------------
        // 4. 开启allowMultiQueries后增加upsertByExample,upsertByExampleSelective但强力建议不要使用(需保证团队没有使用statement提交sql,否则会存在sql注入风险)
        Tb tb2 = Tb.builder()
                .field1(1)
                .field2("xx0")
                .field3(1003)
                .delFlag(Tb.DEL_FLAG_ON)
                .build();
        int k4 = this.tbMapper.upsertByExample(tb2,
                new TbExample()
                        .createCriteria()
                        .andField3EqualTo(1003)
                        .example()
        );
        tb2.setField2("xx1");
        // !!! upsertByExample,upsertByExampleSelective触发更新时,更新条数返回是有问题的,这里只会返回0
        // 这是mybatis自身问题,也是不怎么建议开启allowMultiQueries功能原因之一
        int k5 = this.tbMapper.upsertByExample(tb2,
                new TbExample()
                        .createCriteria()
                        .andField3EqualTo(1003)
                        .example()
        );
        // upsertByExampleSelective 用法类似
        
        // 当Model WithBLOBs 存在时上述方法增加对应的 WithBLOBs 方法,举例如下:
        TbWithBLOBs tb3 = Tb.builder()
                            .field1(1)
                            .field2("xx0")
                            .delFlag(Tb.DEL_FLAG_ON)
                            .build();
        int k6 = this.tbMapper.upsertWithBLOBs(tb);
        
        // --------------------------------- allowBatchUpsert=true ------------------------------
        List<Tb> list = new ArrayList<>();
        list.add(
                Tb.builder()
                .field1(0)
                .field2("xx0")
                .field3(0)
                .field4(new Date())
                .build()
        );
        list.add(
                Tb.builder()
                .field1(1)
                .field2("xx1")
                .field3(1)
                .field4(new Date())
                .build()
        );
        this.tbMapper.batchUpsert(list);    // 对于BLOBs 有batchUpsertWithBLOBs方法
        this.tbMapper.batchUpsertSelective(list, Tb.Column.field1, Tb.Column.field2, Tb.Column.field3, Tb.Column.createTime);
        this.tbMapper.batchUpsertSelective(list, Tb.Column.excludes(Tb.Column.id, Tb.Column.delFlag)); // 排除某些列
    }
}

10. Selective选择插入更新增强插件

项目中往往需要指定某些字段进行插入或者更新,或者把某些字段进行设置null处理,这种情况下原生xxxSelective方法往往不能达到需求,因为它的判断条件是对象字段是否为null,这种情况下可使用该插件对xxxSelective方法进行增强。

warning: 以前老版本(1.1.x)插件处理需要指定的列时是放入Model中指定的,但在实际使用过程中有同事反馈这个处理有点反直觉,导致某些新同事不能及时找到对应方法,而且和增强的SelectSelectivePlugin以及UpsertSelective使用方式都不一致,所以统一修改之。

插件:

<!-- Selective选择插入更新增强插件 -->
<plugin type="com.itfsw.mybatis.generator.plugins.SelectiveEnhancedPlugin"/>

使用:

public class Test {
    public static void main(String[] args) {
        // ------------------------------ 新版本(SelectiveEnhancedPlugin)--------------------------------
        // 1. 指定插入或更新字段
        Tb tb = Tb.builder()
                .field1(1)
                .field2("xx2")
                .field3(1)
                .createTime(new Date())
                .build();
        // 只插入或者更新field1,field2字段
        this.tbMapper.insertSelective(tb, Tb.Column.field1, Tb.Column.field2);
        this.tbMapper.updateByExampleSelective(
                tb,
                new TbExample()
                        .createCriteria()
                        .andIdEqualTo(1l)
                        .example(),
                Tb.Column.field1, Tb.Column.field2
        );
        this.tbMapper.updateByPrimaryKeySelective(tb, Tb.Column.field1, Tb.Column.field2);
        this.tbMapper.upsertSelective(tb, Tb.Column.field1, Tb.Column.field2);
        this.tbMapper.upsertByExampleSelective(
                tb,
                new TbExample()
                        .createCriteria()
                        .andField3EqualTo(1)
                        .example(),
                Tb.Column.field1, Tb.Column.field2
        );

        // 2. 更新某些字段为null
        this.tbMapper.updateByPrimaryKeySelective(
                Tb.builder()
                .id(1l)
                .field1(null)   // 方便展示,不用设也可以
                .build(),
                Tb.Column.field1
        );
        
        // 3. 排除某些列
        this.tbMapper.insertSelective(tb, Tb.Column.excludes(Tb.Column.id, Tb.Column.delFlag));
    }
}

11. Table增加前缀插件

项目中有时会遇到配置多数据源对应多业务的情况,这种情况下可能会出现不同数据源出现重复表名,造成异常冲突。 该插件允许为表增加前缀,改变最终生成的Model、Mapper、Example类名以及xml名。

warning: 使用Table重命名插件可以实现相同功能!
warning: 官方最新版本中已提供domainObjectRenamingRule支持(可以配合表重命名配置插件进行全局配置)!

<table tableName="tb">
    <domainObjectRenamingRule searchString="^" replaceString="DB1" />
</table>

插件:

<xml>
    <!-- Table增加前缀插件 -->
    <plugin type="com.itfsw.mybatis.generator.plugins.TablePrefixPlugin">
        <!-- 这里配置的是全局表前缀,当然在table中配置的值会覆盖该全局配置 -->
        <property name="prefix" value="Cm"/>
    </plugin>
    
    <table tableName="tb">
        <!-- 这里可以单独表配置前缀,覆盖全局配置 -->
        <property name="suffix" value="Db1"/>
    </table>
</xml>

使用:

public class Test {
    public static void main(String[] args) {
        // Tb 表对应的Model、Mapper、Example类都增加了Db1的前缀
        // Model类名: Tb -> Db1Tb
        // Mapper类名: TbMapper -> Db1TbMapper
        // Example类名: TbExample -> Db1TbExample
        // xml文件名: TbMapper.xml -> Db1TbMapper.xml
    }
}

12. Table重命名插件

插件由来:
记得才开始工作时某个隔壁项目组的坑爹项目,由于某些特定的原因数据库设计表名为t1tn,字段名为f1fn。 这种情况下如何利用Mybatis Generator生成可读的代码呢,字段可以用columnOverride来替换,Model、Mapper等则需要使用domainObjectName+mapperName来实现方便辨识的代码。

该插件解决:domainObjectName+mapperName?好吧我想简化一下,所以直接使用tableOverride(仿照columnOverride)来实现便于配置的理解。

某些DBA喜欢在数据库设计时使用t_、f_这种类似的设计([issues#4]), 这种情况下我们就希望能有类似columnRenamingRule这种重命名插件来修正最终生成的Model、Mapper等命名。

该插件解决:使用正则替换table生成的Model、Example、Mapper等命名。

项目中有时会遇到配置多数据源对应多业务的情况,这种情况下可能会出现不同数据源出现重复表名,造成异常冲突。 该插件可以实现和Table增加前缀插件相同的功能,仿照如下配置。

<property name="searchString" value="^"/>
<property name="replaceString" value="DB1"/>

warning: 官方最新版本中已提供domainObjectRenamingRule支持(可以配合表重命名配置插件进行全局配置)!

<table tableName="tb">
    <domainObjectRenamingRule searchString="^T" replaceString="" />
</table>

插件:

<xml>
    <!-- Table重命名插件 -->
    <plugin type="com.itfsw.mybatis.generator.plugins.TableRenamePlugin">
        <!-- 可根据具体需求确定是否配置 -->
        <property name="searchString" value="^T"/>
        <property name="replaceString" value=""/>
    </plugin>
    
    <table tableName="tb">
        <!-- 这个优先级最高,会忽略searchString、replaceString的配置 -->
        <property name="tableOverride" value="TestDb"/>
    </table>
</xml>

使用:

public class Test {
    public static void main(String[] args) {
        // 1. 使用searchString和replaceString时,和Mybatis Generator一样使用的是java.util.regex.Matcher.replaceAll去进行正则替换
        // 表: T_USER
        // Model类名: TUser -> User
        // Mapper类名: TUserMapper -> UserMapper
        // Example类名: TUserExample -> UserExample
        // xml文件名: TUserMapper.xml -> UserMapper.xml
        
        // 2. 使用表tableOverride时,该配置优先级最高
        // 表: T_BOOK
        // Model类名: TBook -> TestDb
        // Mapper类名: TBookMapper -> TestDbMapper
        // Example类名: TBookExample -> TestDbExample
        // xml文件名: TBookMapper.xml -> TestDbMapper.xml
    }
}

13. 自定义注释插件

Mybatis Generator是原生支持自定义注释的(commentGenerator配置type属性),但使用比较麻烦需要自己实现CommentGenerator接口并打包配置赖等等。
该插件借助freemarker极佳的灵活性实现了自定义注释的快速配置。

warning: 下方提供了一个参考模板,需要注意${mgb}的输出,因为Mybatis Generator就是通过该字符串判断是否为自身生成代码进行覆盖重写。

warning: 请注意拷贝参考模板注释前方空格,idea等工具拷贝进去后自动格式化会造成格式错乱。

warning: 模板引擎采用的是freemarker所以一些freemarker指令参数(如:<#if xx></#if>、${.now?string("yyyy-MM-dd HH:mm:ss")})都是可以使用的,请自己尝试。

warning: 默认模板

注释ID 传入参数 备注
addJavaFileComment mgb
compilationUnit
Java文件注释
addComment mgb
xmlElement
Xml节点注释
addRootComment mgb
rootElement
Xml根节点注释
addFieldComment mgb
field
introspectedTable
introspectedColumn
Java 字段注释(非生成Model对应表字段时,introspectedColumn可能不存在)
addModelClassComment mgb
topLevelClass
introspectedTable
表Model类注释
addClassComment mgb
innerClass
introspectedTable
类注释
addEnumComment mgb
innerEnum
introspectedTable
枚举注释
addInterfaceComment mgb
innerInterface
introspectedTable
接口注释(itfsw插件新增类型)
addGetterComment mgb
method
introspectedTable
introspectedColumn
getter方法注释(introspectedColumn可能不存在)
addSetterComment mgb
method
introspectedTable
introspectedColumn
setter方法注释(introspectedColumn可能不存在)
addGeneralMethodComment mgb
method
introspectedTable
方法注释

插件:

<xml>
    <!-- 自定义注释插件 -->
    <plugin type="com.itfsw.mybatis.generator.plugins.CommentPlugin">
        <!-- 自定义模板路径 -->
        <property name="template" value="src/main/resources/mybatis-generator-comment.ftl" />
    </plugin>
</xml>

使用(参考模板):

<?xml version="1.0" encoding="UTF-8"?>
<template>
    <!-- #############################################################################################################
    /**
     * This method is called to add a file level comment to a generated java file. This method could be used to add a
     * general file comment (such as a copyright notice). However, note that the Java file merge function in Eclipse
     * does not deal with this comment. If you run the generator repeatedly, you will only retain the comment from the
     * initial run.
     * <p>
     *
     * The default implementation does nothing.
     *
     * @param compilationUnit
     *            the compilation unit
     */
    -->
    <comment ID="addJavaFileComment"></comment>

    <!-- #############################################################################################################
    /**
     * This method should add a suitable comment as a child element of the specified xmlElement to warn users that the
     * element was generated and is subject to regeneration.
     *
     * @param xmlElement
     *            the xml element
     */
    -->
    <comment ID="addComment"><![CDATA[
<!--
  WARNING - ${mgb}
  This element is automatically generated by MyBatis Generator, do not modify.
  @project https://github.com/itfsw/mybatis-generator-plugin
-->
        ]]></comment>

    <!-- #############################################################################################################
    /**
     * This method is called to add a comment as the first child of the root element. This method could be used to add a
     * general file comment (such as a copyright notice). However, note that the XML file merge function does not deal
     * with this comment. If you run the generator repeatedly, you will only retain the comment from the initial run.
     * <p>
     *
     * The default implementation does nothing.
     *
     * @param rootElement
     *            the root element
     */
    -->
    <comment ID="addRootComment"></comment>

    <!-- #############################################################################################################
    /**
     * This method should add a Javadoc comment to the specified field. The field is related to the specified table and
     * is used to hold the value of the specified column.
     * <p>
     *
     * <b>Important:</b> This method should add a the nonstandard JavaDoc tag "@mbg.generated" to the comment. Without
     * this tag, the Eclipse based Java merge feature will fail.
     *
     * @param field
     *            the field
     * @param introspectedTable
     *            the introspected table
     * @param introspectedColumn
     *            the introspected column
     */
    -->
    <comment ID="addFieldComment"><![CDATA[
<#if introspectedColumn??>
/**
    <#if introspectedColumn.remarks?? && introspectedColumn.remarks != ''>
 * Database Column Remarks:
        <#list introspectedColumn.remarks?split("\n") as remark>
 *   ${remark}
        </#list>
    </#if>
 *
 * This field was generated by MyBatis Generator.
 * This field corresponds to the database column ${introspectedTable.fullyQualifiedTable}.${introspectedColumn.actualColumnName}
 *
 * ${mgb}
 * @project https://github.com/itfsw/mybatis-generator-plugin
 */
<#else>
/**
 * This field was generated by MyBatis Generator.
 * This field corresponds to the database table ${introspectedTable.fullyQualifiedTable}
 *
 * ${mgb}
 * @project https://github.com/itfsw/mybatis-generator-plugin
 */
</#if>
    ]]></comment>

    <!-- #############################################################################################################
    /**
     * Adds a comment for a model class.  The Java code merger should
     * be notified not to delete the entire class in case any manual
     * changes have been made.  So this method will always use the
     * "do not delete" annotation.
     *
     * Because of difficulties with the Java file merger, the default implementation
     * of this method should NOT add comments.  Comments should only be added if
     * specifically requested by the user (for example, by enabling table remark comments).
     *
     * @param topLevelClass
     *            the top level class
     * @param introspectedTable
     *            the introspected table
     */
    -->
    <comment ID="addModelClassComment"><![CDATA[
/**
<#if introspectedTable.remarks?? && introspectedTable.remarks != ''>
 * Database Table Remarks:
<#list introspectedTable.remarks?split("\n") as remark>
 *   ${remark}
</#list>
</#if>
 *
 * This class was generated by MyBatis Generator.
 * This class corresponds to the database table ${introspectedTable.fullyQualifiedTable}
 *
 * ${mgb} do_not_delete_during_merge
 * @project https://github.com/itfsw/mybatis-generator-plugin
 */
        ]]></comment>

    <!-- #############################################################################################################
    /**
     * Adds the inner class comment.
     *
     * @param innerClass
     *            the inner class
     * @param introspectedTable
     *            the introspected table
     * @param markAsDoNotDelete
     *            the mark as do not delete
     */
    -->
    <comment ID="addClassComment"><![CDATA[
/**
 * This class was generated by MyBatis Generator.
 * This class corresponds to the database table ${introspectedTable.fullyQualifiedTable}
 *
 * ${mgb}<#if markAsDoNotDelete?? && markAsDoNotDelete> do_not_delete_during_merge</#if>
 * @project https://github.com/itfsw/mybatis-generator-plugin
 */
        ]]></comment>

    <!-- #############################################################################################################
    /**
     * Adds the enum comment.
     *
     * @param innerEnum
     *            the inner enum
     * @param introspectedTable
     *            the introspected table
     */
    -->
    <comment ID="addEnumComment"><![CDATA[
/**
 * This enum was generated by MyBatis Generator.
 * This enum corresponds to the database table ${introspectedTable.fullyQualifiedTable}
 *
 * ${mgb}
 * @project https://github.com/itfsw/mybatis-generator-plugin
 */
        ]]></comment>

    <!-- #############################################################################################################
    /**
     * Adds the interface comment.
     *
     * @param innerInterface
     *            the inner interface
     * @param introspectedTable
     *            the introspected table
     */
    -->
    <comment ID="addInterfaceComment"><![CDATA[
/**
 * This interface was generated by MyBatis Generator.
 * This interface corresponds to the database table ${introspectedTable.fullyQualifiedTable}
 *
 * ${mgb}
 * @project https://github.com/itfsw/mybatis-generator-plugin
 */
        ]]></comment>

    <!-- #############################################################################################################
    /**
     * Adds the getter comment.
     *
     * @param method
     *            the method
     * @param introspectedTable
     *            the introspected table
     * @param introspectedColumn
     *            the introspected column
     */
    -->
    <comment ID="addGetterComment"><![CDATA[
<#if introspectedColumn??>
/**
 * This method was generated by MyBatis Generator.
 * This method returns the value of the database column ${introspectedTable.fullyQualifiedTable}.${introspectedColumn.actualColumnName}
 *
 * @return the value of ${introspectedTable.fullyQualifiedTable}.${introspectedColumn.actualColumnName}
 *
 * ${mgb}
 * @project https://github.com/itfsw/mybatis-generator-plugin
 */
<#else>
/**
 * This method was generated by MyBatis Generator.
 * This method returns the value of the field ${method.name?replace("get","")?replace("is", "")?uncap_first}
 *
 * @return the value of field ${method.name?replace("get","")?replace("is", "")?uncap_first}
 *
 * ${mgb}
 * @project https://github.com/itfsw/mybatis-generator-plugin
 */
</#if>
        ]]></comment>

    <!-- #############################################################################################################
    /**
     * Adds the setter comment.
     *
     * @param method
     *            the method
     * @param introspectedTable
     *            the introspected table
     * @param introspectedColumn
     *            the introspected column
     */
    -->
    <comment ID="addSetterComment"><![CDATA[
<#if introspectedColumn??>
/**
 * This method was generated by MyBatis Generator.
 * This method sets the value of the database column ${introspectedTable.fullyQualifiedTable}.${introspectedColumn.actualColumnName}
 *
 * @param ${method.parameters[0].name} the value for ${introspectedTable.fullyQualifiedTable}.${introspectedColumn.actualColumnName}
 *
 * ${mgb}
 * @project https://github.com/itfsw/mybatis-generator-plugin
 */
<#else>
/**
 * This method was generated by MyBatis Generator.
 * This method sets the value of the field ${method.name?replace("set","")?uncap_first}
 *
 * @param ${method.parameters[0].name} the value for field ${method.name?replace("set","")?uncap_first}
 *
 * ${mgb}
 * @project https://github.com/itfsw/mybatis-generator-plugin
 */
</#if>
        ]]></comment>

    <!-- #############################################################################################################
    /**
     * Adds the general method comment.
     *
     * @param method
     *            the method
     * @param introspectedTable
     *            the introspected table
     */
    -->
    <comment ID="addGeneralMethodComment"><![CDATA[
/**
 * This method was generated by MyBatis Generator.
 * This method corresponds to the database table ${introspectedTable.fullyQualifiedTable}
 *
 * ${mgb}
 * @project https://github.com/itfsw/mybatis-generator-plugin
 */
        ]]></comment>
</template>

14. 增量插件

为更新操作生成set filedxxx = filedxxx +/- inc 操作,方便某些统计字段的更新操作,常用于某些需要计数的场景;

warning:该插件在整合LombokPlugin使用时会生成大量附加代码影响代码美观,强力建议切换到新版插件IncrementPlugin;

插件:

<xml>
    <!-- 增量插件 -->
    <plugin type="com.itfsw.mybatis.generator.plugins.IncrementsPlugin" />
    
    <table tableName="tb">
        <!-- 配置需要进行增量操作的列名称(英文半角逗号分隔) -->
        <property name="incrementsColumns" value="field1,field2"/>
    </table>
</xml>

使用:

public class Test {
    public static void main(String[] args) {
        // 在构建更新对象时,配置了增量支持的字段会增加传入增量枚举的方法
        Tb tb = Tb.builder()
                .id(102)
                .field1(1, Tb.Builder.Inc.INC)  // 字段1 统计增加1
                .field2(2, Tb.Builder.Inc.DEC)  // 字段2 统计减去2
                .field4(new Date())
                .build();
        // 更新操作,可以是 updateByExample, updateByExampleSelective, updateByPrimaryKey
        // , updateByPrimaryKeySelective, upsert, upsertSelective等所有涉及更新的操作
        this.tbMapper.updateByPrimaryKey(tb);
    }
}

15. 查询结果选择性返回插件

一般我们在做查询优化的时候会要求查询返回时不要返回自己不需要的字段数据,因为Sending data所花费的时间和加大内存的占用 ,所以我们看到官方对于大数据的字段会拆分成xxxWithBLOBs的操作,但是这种拆分还是不能精确到具体列返回。
所以该插件的作用就是精确指定查询操作所需要返回的字段信息,实现查询的精确返回。

warning: 因为采用的是resultMap进行的属性绑定(即时设置了constructorBased=true也无效,因为参数个数不一致会导致异常,该插件也会另外生成一个基于属性绑定的resultMap), 所以可能会出现list中存在null元素的问题,这个是mybatis自身机制造成的当查询出来的某行所有列都为null时不会生成对象(PS:其实这个不能算是错误,mybatis这样处理也说的通,但是在使用时还是要注意null的判断,当然如果有什么配置或者其他处理方式欢迎反馈哦)。

插件:

<xml>
    <!-- 查询结果选择性返回插件 -->
    <plugin type="com.itfsw.mybatis.generator.plugins.SelectSelectivePlugin" />
</xml>

使用:

public class Test {
    public static void main(String[] args) {
        // 查询操作精确返回需要的列
        this.tbMapper.selectByExampleSelective(
            new TbExample()
            .createCriteria()
            .andField1GreaterThan(1)
            .example(),
            Tb.Column.field1,
            Tb.Column.field2
        );
        // 同理还有 selectByPrimaryKeySelective,selectOneByExampleSelective(SelectOneByExamplePlugin插件配合使用)方法
        
        // 当然可以使用excludes
        this.tbMapper.selectByExampleSelective(
            new TbExample()
            .createCriteria()
            .andField1GreaterThan(1)
            .example(),
            Tb.Column.excludes(Tb.Column.id, Tb.Column.delFlag)
        );
    }
}

16. 官方ConstructorBased配置BUG临时修正插件

当javaModelGenerator配置constructorBased=true时,如果表中只有一个column类型为“blob”时java model没有生成BaseResultMap对应的构造函数, 这个bug已经反馈给官方issues#267

官方V1.3.6版本将解决这个bug,老版本的可以使用这个插件临时修正问题。

插件:

<xml>
    <!-- 官方ConstructorBased配置BUG临时修正插件 -->
    <plugin type="com.itfsw.mybatis.generator.plugins.ConstructorBasedBugFixPlugin" />
</xml>

17. 乐观锁插件

为并发操作引入乐观锁,当发生删除或者更新操作时调用相应的WithVersion方法传入版本号,插件会在相应的查询条件上附加上版本号的检查,防止非法操作的发生。
同时在更新操作中支持自定义nextVersion或者利用sql 的“set column = column + 1”去维护版本号。

插件:

<xml>
    <!-- 乐观锁插件 -->
    <plugin type="com.itfsw.mybatis.generator.plugins.OptimisticLockerPlugin">
        <!-- 是否启用自定义nextVersion,默认不启用(插件会默认使用sql的 set column = column + 1) -->
        <property name="customizedNextVersion" value="false"/>
    </plugin>
    
    <table tableName="tb">
        <!-- 这里可以单独表配置,覆盖全局配置 -->
        <property name="customizedNextVersion" value="true"/>
        <!-- 指定版本列 -->
        <property name="versionColumn" value="version"/>
    </table>
</xml>

使用:

public class Test {
    public static void main(String[] args) {
        // ============================ 带版本号的删除更新等操作 ===============================
        int result = this.tbMapper.deleteWithVersionByExample(
                        100, // 版本号
                        new TbExample()
                           .createCriteria()
                           .andField1GreaterThan(1)
                           .example()
                     );
        if (result == 0){
            throw new Exception("没有找到数据或者数据版本号错误!");
        }
        // 对应生成的Sql: delete from tb WHERE version = 100 and ( ( field1 > 1 ) )
        
        // 带版本号的方法有:
        // deleteWithVersionByExample、deleteWithVersionByPrimaryKey、
        // updateWithVersionByExampleSelective、updateWithVersionByExampleWithBLOBs、updateWithVersionByExample
        // updateWithVersionByPrimaryKeySelective、updateWithVersionByPrimaryKeyWithBLOBs、updateWithVersionByPrimaryKey
        
        // ============================= 使用默认版本号生成策略 ===========================
        this.tbMapper.updateWithVersionByPrimaryKey(
                100,    // 版本号
                Tb.builder()
                  .id(102)
                  .field1("ts1")
                  .build()
        );
        // 对应生成的Sql: update tb set version = version + 1, field1 = 'ts1' where version = 100 and id = 102
        
        // ============================= 使用自定义版本号生成策略 ===========================
        this.tbMapper.updateWithVersionByPrimaryKey(
                100,    // 版本号
                Tb.builder()
                  .id(102)
                  .field1("ts1")
                  .build()
                  .nextVersion(System.currentTimeMillis())    // 传入nextVersion
        );
        // 对应生成的Sql: update tb set version = 1525773888559, field1 = 'ts1' where version = 100 and id = 102
    }
}

18. 表重命名配置插件

官方提供了domainObjectRenamingRule(官方最新版本已提供)、columnRenamingRule分别进行生成的表名称和对应表字段的重命名支持,但是它需要每个表单独进行配置,对于常用的如表附带前缀“t_”、字段前缀“f_”这种全局性替换会比较麻烦。
该插件提供了一种全局替换机制,当表没有单独指定domainObjectRenamingRule、columnRenamingRule时采用全局性配置。
同时插件提供clientSuffix、exampleSuffix、modelSuffix来修改对应生成的类和文件的结尾(之前issue中有用户希望能把Mapper替换成Dao)。

  • 全局domainObjectRenamingRule
<xml>
    <!-- 表重命名配置插件 -->
    <plugin type="com.itfsw.mybatis.generator.plugins.TableRenameConfigurationPlugin">
        <property name="domainObjectRenamingRule.searchString" value="^T"/>
        <property name="domainObjectRenamingRule.replaceString" value=""/>
    </plugin>
    
    <table tableName="tb">
        <!-- 这里可以禁用全局domainObjectRenamingRule配置 -->
        <property name="domainObjectRenamingRule.disable" value="true"/>
    </table>
    
    <table tableName="tb_ts1">
        <!-- 当然你也可以使用官方domainObjectRenamingRule的配置来覆盖全局配置 -->
        <domainObjectRenamingRule searchString="^Tb" replaceString="B"/>
    </table>
</xml>
  • 全局columnRenamingRule
<xml>
    <!-- 表重命名配置插件 -->
    <plugin type="com.itfsw.mybatis.generator.plugins.TableRenameConfigurationPlugin">
        <!-- 需要注意,这里的正则和官方一样是比对替换都是原始表的column名称 -->
        <property name="columnRenamingRule.searchString" value="^f_"/>
        <property name="columnRenamingRule.replaceString" value="_"/>
    </plugin>
    
    <table tableName="tb">
        <!-- 这里可以禁用全局columnRenamingRule配置 -->
        <property name="columnRenamingRule.disable" value="true"/>
    </table>
    
    <table tableName="tb_ts1">
        <!-- 当然你也可以使用官方domainObjectRenamingRule的配置来覆盖全局配置 -->
        <columnRenamingRule searchString="^f_" replaceString="_"/>
    </table>
</xml>
  • clientSuffix、exampleSuffix、modelSuffix
<xml>
    <!-- 表重命名配置插件 -->
    <plugin type="com.itfsw.mybatis.generator.plugins.TableRenameConfigurationPlugin">
        <!-- TbMapper -> TbDao, TbMapper.xml -> TbDao.xml -->
        <property name="clientSuffix" value="Dao"/>
        <!-- TbExmaple -> TbQuery -->
        <property name="exampleSuffix" value="Query"/>
        <!-- Tb -> TbEntity -->
        <property name="modelSuffix" value="Entity"/>
    </plugin>
</xml>

19. Lombok插件

使用Lombok的使用可以减少很多重复代码的书写,目前项目中已大量使用。 但Lombok的@Builder对于类的继承支持很不好,最近发现新版(>=1.18.2)已经提供了对@SuperBuilder的支持,所以新增该插件方便简写代码。

warning1: @Builder注解在Lombok 版本 >= 1.18.2 的情况下才能开启,对于存在继承关系的model会自动替换成@SuperBuilder注解(目前IDEA的插件对于SuperBuilder的还不支持(作者已经安排上更新日程), 可以开启配置supportSuperBuilderForIdea使插件在遇到@SuperBuilder注解时使用ModelBuilderPlugin替代该注解)。

warning2: 配合插件IncrementsPlugin(已不推荐使用,请使用新版IncrementPlugin解决该问题) 并且 @Builder开启的情况下,因为@SuperBuilder的一些限制, 插件模拟Lombok插件生成了一些附加代码可能在某些编译器上会提示错误,请忽略(Lombok = 1.18.2 已测试)。

<xml>
    <!-- Lombok插件 -->
    <plugin type="com.itfsw.mybatis.generator.plugins.LombokPlugin">
        <!-- @Data 默认开启,同时插件会对子类自动附加@EqualsAndHashCode(callSuper = true),@ToString(callSuper = true) -->
        <property name="@Data" value="true"/>
        <!-- @Builder 必须在 Lombok 版本 >= 1.18.2 的情况下开启,对存在继承关系的类自动替换成@SuperBuilder -->
        <property name="@Builder" value="false"/>
        <!-- @NoArgsConstructor 和 @AllArgsConstructor 使用规则和Lombok一致 -->
        <property name="@AllArgsConstructor" value="false"/>
        <property name="@NoArgsConstructor" value="false"/>
        <!-- @Getter、@Setter、@Accessors 等使用规则参见官方文档 -->
        <property name="@Accessors(chain = true)" value="false"/>
        <!-- 临时解决IDEA工具对@SuperBuilder的不支持问题,开启后(默认未开启)插件在遇到@SuperBuilder注解时会调用ModelBuilderPlugin来生成相应的builder代码 -->
        <property name="supportSuperBuilderForIdea" value="false"/>
    </plugin>
</xml>

20. 数据ModelCloneable插件

数据Model实现Cloneable接口。

<xml>
    <!-- 数据ModelCloneable插件 -->
    <plugin type="com.itfsw.mybatis.generator.plugins.ModelCloneablePlugin"/>
</xml>

21. 状态枚举生成插件

数据库中经常会定义一些状态字段,该工具可根据约定的注释格式生成对应的枚举类,方便使用。

warning:插件1.2.18版本以后默认开启自动扫描,根据约定注释格式自动生成对应枚举类

<xml>
    <!-- 状态枚举生成插件 -->
    <plugin type="com.itfsw.mybatis.generator.plugins.EnumTypeStatusPlugin">
        <!-- 是否开启自动扫描根据约定注释格式生成枚举,默认true -->
        <property name="autoScan" value="true"/>
        <!-- autoScan为false,这里可以定义全局需要检查生成枚举类的列名 -->
        <property name="enumColumns" value="type, status"/>
    </plugin>
    <table tableName="tb">
        <!-- autoScan为false,也可以为单独某个table增加配置 -->
        <property name="enumColumns" value="user_type"/>
    </table>
</xml>

warning: 约定的注释检查规则的正则表达式如下

public class EnumTypeStatusPlugin {
    public final static String REMARKS_PATTERN = ".*\\s*\\[\\s*(\\w+\\s*\\(\\s*[\\u4e00-\\u9fa5_\\-a-zA-Z0-9]+\\s*\\)\\s*:\\s*[\\u4e00-\\u9fa5_\\-a-zA-Z0-9]+\\s*\\,?\\s*)+\\s*\\]\\s*.*";
}

使用

CREATE TABLE `tb` (
  `type` smallint(3) COMMENT '注释[success(0):成功, fail(1):失败]',
  `status` bigint(3) COMMENT '换行的注释
                                         [
                                           login_success(0):登录成功,
                                           login_fail(1):登录失败
                                         ]',
  `user_type` varchar(20) COMMENT '具体注释的写法是比较宽泛的,只要匹配上面正则就行
   [    success (   我是具体值  )    : 我是值的描述_我可以是中英文数字和下划线_xxx_123, fail_xx_3
    (1  ) :  失败] 后面也可以跟注释'                                       
);
public class Tb {
    public enum Type {
        SUCCESS((short)0, "成功"),
        FAIL((short)1, "失败");
        
        private final Short value;
        private final String name;
        
        Type(Short value, String name) {
            this.value = value;
            this.name = name;
        }
        public Short getValue() {
            return this.value;
        }
        public Short value() {
            return this.value;
        }
        public String getName() {
            return this.name;
        }
        public Field2 parseValue(Short value) {
            if (value != null) {
                for (Field2 item : values()) {
                    if (item.value.equals(value)) {
                        return item;
                    }
                }
            }
            return null;
        }
        public Field2 parseName(String name) {
            if (name != null) {
                for (Field2 item : values()) {
                    if (item.name.equals(name)) {
                        return item;
                    }
                }
            }
            return null;
        }
    }

    public enum Status {
        LOGIN_SUCCESS(0L, "登录成功"),
        LOGIN_FAIL(1L, "登录失败");

        private final Long value;
        private final String name;

        Status(Long value, String name) {
            this.value = value;
            this.name = name;
        }
        public Long getValue() {
            return this.value;
        }
        public Long value() {
            return this.value;
        }
        public String getName() {
            return this.name;
        }
        public Status parseValue(Short value) {
            if (value != null) {
                for (Status item : values()) {
                    if (item.value.equals(value)) {
                        return item;
                    }
                }
            }
            return null;
        }
        public Status parseName(String name) {
            if (name != null) {
                for (Status item : values()) {
                    if (item.name.equals(name)) {
                        return item;
                    }
                }
            }
            return null;
        }
    }

    public enum UserType {
        SUCCESS("我是具体值", "我是值的描述_我可以是中英文数字和下划线_xxx_123"),
        FAIL_XX_3("1", "失败");

        private final String value;
        private final String name;

        UserType(String value, String name) {
            this.value = value;
            this.name = name;
        }
        public String getValue() {
            return this.value;
        }
        public String value() {
            return this.value;
        }
        public String getName() {
            return this.name;
        }
        public UserType parseValue(Short value) {
            if (value != null) {
                for (UserType item : values()) {
                    if (item.value.equals(value)) {
                        return item;
                    }
                }
            }
            return null;
        }
        public UserType parseName(String name) {
            if (name != null) {
                for (UserType item : values()) {
                    if (item.name.equals(name)) {
                        return item;
                    }
                }
            }
            return null;
        }
    }
}

22. 增量插件

为更新操作生成set filedxxx = filedxxx +/- inc 操作,方便某些统计字段的更新操作,常用于某些需要计数的场景,需配合(ModelColumnPlugin)插件使用;

插件:

<xml>
    <!-- 增量插件 -->
    <plugin type="com.itfsw.mybatis.generator.plugins.IncrementPlugin" />
    
    <table tableName="tb">
        <!-- 配置需要进行增量操作的列名称(英文半角逗号分隔) -->
        <property name="incrementColumns" value="field1,field2"/>
    </table>
</xml>

使用:

public class Test {
    public static void main(String[] args) {
        // 在构建更新对象时,配置了增量支持的字段会增加传入增量枚举的方法
        Tb tb = Tb.builder()
                .id(102)
                .field4(new Date())
                .build()
                .increment(Tb.Increment.field1.inc(1)) // 字段1 统计增加1
                .increment(Tb.Increment.field2.dec(2)); // 字段2 统计减去2
        // 更新操作,可以是 updateByExample, updateByExampleSelective, updateByPrimaryKey
        // , updateByPrimaryKeySelective, upsert, upsertSelective等所有涉及更新的操作
        this.tbMapper.updateByPrimaryKey(tb);
    }
}

23. Mapper注解插件

对官方的(MapperAnnotationPlugin)增强,可自定义附加@Repository注解(IDEA工具对@Mapper注解支持有问题,使用@Autowired会报无法找到对应bean,附加@Repository后解决);

插件:

<xml>
    <!-- Mapper注解插件 -->
    <plugin type="com.itfsw.mybatis.generator.plugins.MapperAnnotationPlugin">
        <!-- @Mapper 默认开启 -->
        <property name="@Mapper" value="true"/>
        <!-- @Repository 开启后解决IDEA工具@Autowired报错 -->
        <property name="@Repository" value="org.springframework.stereotype.Repository"/>
        <!-- 其他自定义注解 -->
        <property name="@DS(&quot;master&quot;)" value="com.baomidou.dynamic.datasource.annotation.DS"/>
    </plugin>
</xml>

mybatis-generator-plugin's People

Contributors

dependabot[bot] avatar hellsof avatar itfsw avatar linlinjava avatar tonycody avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mybatis-generator-plugin's Issues

blob方法的判断似乎有点问题

version

1.2.2
mybatis 1.3.6

log

org.apache.ibatis.builder.IncompleteElementException: Could not find SQL statement to include with refid 'com.github.flowerwrong.model.mapper.RegionMapper.Blob_Column_List'

我比较了一下,以前版本是没有生成下图方法的。
2018-05-15 5 32 38

能不能提一个BaseDomain和BaseExample类

1.能不能提取一个BaseExample类,存放Criteria 这样减少很多重复代码,并且提取了一个抽象,然后统一的查询条件Criteria,可以被使用到多个表查询中。
2.改写andEqual方法,这样代码的复用性更强
之前: public Criteria andIdEqualTo(Long value) {
addCriterion("id =", value, "id");
return (Criteria) this;
}
之后:public Criteria and(String column,Object value){
addCriterion(column+" =", value, column);
return (Criteria) this;
}

SelectiveEnhancedPlugin插件和ModelColumnPlugin插件对数组的支持

在1.2.9中,SelectiveEnhancedPlugin插件和ModelColumnPlugin插件一起启动以后,生产的代码对数组支持有问题。

在我的项目中,数据库表中的有些字段虽然是varchar,但是实际是采用JSON数组格式存储,因此我这里自定义了typeHandler,产生的java代码中有些域是数组类型。
_20180703105056

在1.2.6中,编译运行都正常,但是在1.2.9中,引入了新的Colume特性,但是对数组支持有问题。
_20180703104858

在上面可以看到,mybatis产生的代码中andSpecificationsLessThanOrEqualTo方法的参数是String[], 而andSpecificationsLessThanColumn中实际则是调用了String类型。

预期的效果应该是andSpecificationsLessThanColumn里面应该是产生数组,然后内部调用andSpecificationsLessThanOrEqualTo。

使用这个插件报错,问下这个问题怎么解决?

Failed to execute goal org.mybatis.generator:mybatis-generator-maven-plugin:1.3.5:generate (default-cli) on project lepai: Execution default-cli of goal org.mybatis.generator:mybatis-generator-maven-plugin:1.3.5:generate failed: Cannot instantiate object of type com.itfsw.mybatis.generator.plugins.BatchInsertPlugin -> [Help 1]

不能找到对应的依赖呢?

你好,为什么我不能找到对应的依赖呢?
这是我的配置:

    <plugin>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-maven-plugin</artifactId>
                <version>1.3.5</version>
                <configuration>
                    <!-- 配置文件 -->
                    <configurationFile>src/main/resources/excludes/mybatis-generator.xml</configurationFile>
                    <!-- 允许移动和修改 -->
                    <verbose>true</verbose>
                    <overwrite>true</overwrite>
                </configuration>
                <dependencies>
                    <!-- jdbc 依赖 -->
                    <dependency>
                        <groupId>mysql</groupId>
                        <artifactId>mysql-connector-java</artifactId>
                        <version>5.1.40</version>
                    </dependency>
                    <dependency>
                        <groupId>com.itfsw</groupId>
                        <artifactId>mybatis-generator-plugin</artifactId>
                        <version>1.0.4</version>
                    </dependency>
                </dependencies>
            </plugin>

tb表会生产andLogicalDeleted?

基于1.2.4,在tb表中增加了deleted属性,然后采用逻辑删除插件生产代码。

Tb类存在deleted域,存在getDeleted和setDeleted方法了。
但是tb表会多生产andLogicalDeleted方法,我不知道这里这个方法为什么要存在?
有什么作用吗,还是放错地方了。

    /**
     * This method was generated by MyBatis Generator.
     * This method corresponds to the database table tb
     *
     * @mbg.generated
     * @project https://github.com/itfsw/mybatis-generator-plugin
     */
    public void andLogicalDeleted(boolean deleted) {
        setDeleted(deleted ? IS_DELETED : NOT_DELETED);
    }

selectByExampleSelective 不支持 distinct

官方的selectByExample支持distinct, 例如

<select id="selectByExample" parameterType="org.demo.Demo" resultMap="BaseResultMap">
    select
    <if test="distinct">
      distinct
    </if>
    <include refid="Base_Column_List" />
   ...
</select>

而当前的SelectSelectivePlugin插件生产的selectByExampleSelective则不支持,例如

<select id="selectByExampleSelective" parameterType="map" resultMap="BaseResultMap">
    select
    <foreach collection="selective" item="column" separator=",">
      ${column.value}
    </foreach>
   ...
</select>

这里应该生产如下的效果,才可以:

<select id="selectByExampleSelective" parameterType="map" resultMap="BaseResultMap">
    select
    <if test="example.distinct">
      distinct
    </if>
    <foreach collection="selective" item="column" separator=",">
      ${column.value}
    </foreach>
   ...
</select>

事实上,distinct在官方生产的代码中意义不大,因为查询的全数据肯定是唯一的,而在这里的可选查询中才真正有意义,因为查询部分得到的数据存在重复性,因此应该支持。

感谢作者的付出。

逻辑删除标识符的理解

不知道logicalDeleteValue/logicalUnDeleteValue和生成的常量DEL_FLAG_ON/OFF是一个怎样的对应关系呢?按照我的理解DEL_FLAG_ON是说明删除标识符被置为true,DEL_FLAG_OFF说明删除标识符被置为false,但是实际上生成的与我理解的正好相反,您是怎样理解这个删除常量的呢?
配置:

    <property name="logicalDeleteColumn" value="is_deleted"/>
    <!-- 逻辑删除-已删除值 -->
    <property name="logicalDeleteValue" value="1"/>
    <!-- 逻辑删除-未删除值 -->
    <property name="logicalUnDeleteValue" value="0"/>

实际生成:

    public static final Boolean DEL_FLAG_ON = false;
    public static final Boolean DEL_FLAG_OFF = true;

自动生成的mapper文件中存在两个相同的selectOneByExampleSelective

首先对于作者下面的话不是很理解

因为1.2版本对Selective选择插入更新增强插件进行了重构,不再兼容老版。老版本参见分支V1.1.x(只进行BUG修正,不再添加新功能);

因为我现在需要乐观锁插件(感谢),因此我需要从1.1.x跟新到1.2,如果会带来不兼容,我觉得不要紧,我会重新更新自己依赖的代码。所以,你在release中所说的"建议使用老版(1.1.x)的用户不要升级",是不是就不要紧。

其次,今天发现一个问题是,更新到1.2以后,自动生成的mapper文件中存在两个相同的selectOneByExampleSelective,如下图所示

image

为了防止我自己失误,我把resources目录的dao文件夹都删除,重新运行生成器,仍然存在这个问题。

我的generatorConfig.xml是:

        <!-- 查询单条数据插件 -->
        <plugin type="com.itfsw.mybatis.generator.plugins.SelectOneByExamplePlugin"/>
        <!-- 查询结果选择性返回插件 -->
        <plugin type="com.itfsw.mybatis.generator.plugins.SelectSelectivePlugin" />
        <!-- Example Criteria 增强插件 -->
        <plugin type="com.itfsw.mybatis.generator.plugins.ExampleEnhancedPlugin"/>
        <!-- 数据Model属性对应Column获取插件 -->
        <plugin type="com.itfsw.mybatis.generator.plugins.ModelColumnPlugin"/>

我的pom.xml是

   <build>
        <plugins>
            <plugin>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-maven-plugin</artifactId>
                <version>1.3.6</version>
                <configuration>
                    <configurationFile>
                        mybatis-generator/generatorConfig.xml
                    </configurationFile>
                    <overwrite>true</overwrite>
                    <verbose>true</verbose>
                </configuration>
                <dependencies>
                    <dependency>
                        <groupId>mysql</groupId>
                        <artifactId>mysql-connector-java</artifactId>
                        <version>5.1.46</version>
                    </dependency>
                    <dependency>
                        <groupId>com.itfsw</groupId>
                        <artifactId>mybatis-generator-plugin</artifactId>
                        <version>1.2.2</version>
                    </dependency>
                </dependencies>
            </plugin>

        </plugins>

    </build>

可否为batch_insert增加 insert if not exists 逻辑?

因为很多时候有 insert if not exists 批量插入的需求, 是一个很实用功能.

本来这个repo[https://github.com/beihaifeiwu/dolphin] 已经完成了这个功能,但是觉得他的其他功能写的没你的好, 所以恳请您的repo里面添加这项功能.

MySQL分页插件 bug

MySQL分页插件中:
public MallExample page(Integer page, Integer pageSize) {
this.offset = page * pageSize;
this.rows = pageSize;
return this;
}
offset计算应该如下:
this.offset = (page - 1) * pageSize

logicDeleteWithVersion

目前的deleteWithVersion好像不支持logicDelete。

我觉得logicDeleteWithVersion还是有应用场景的吧。

增量插件使用delimitAllColumns属性后报错

如果配置列名用反引号包裹,使用增量插件后执行mybatis generator插件会报错,去掉后可以正常生成。

配置信息:

        <property name="beginningDelimiter" value="`"/>
        <property name="endingDelimiter" value="`"/>
        ...
        <table tableName="test"
                  delimitAllColumns="true"
          ....

错误信息

[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.182 s
[INFO] Finished at: 2017-08-03T16:51:02+08:00
[INFO] Final Memory: 11M/135M
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.mybatis.generator:mybatis-generator-maven-plugin:1.3.5:generate (default-cli) on project sharejam-core: Execution default-cli of goal org.mybatis.generator:mybatis-generator-maven-plugin:1.3.5:generate failed.: NullPointerException -> [Help 1]
org.apache.maven.lifecycle.LifecycleExecutionException: Failed to execute goal org.mybatis.generator:mybatis-generator-maven-plugin:1.3.5:generate (default-cli) on project sharejam-core: Execution default-cli of goal org.mybatis.generator:mybatis-generator-maven-plugin:1.3.5:generate failed.
	at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:213)
	at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:154)
	at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:146)
	at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:117)
	at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:81)
	at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build(SingleThreadedBuilder.java:51)
	at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:128)
	at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:309)
	at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:194)
	at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:107)
	at org.apache.maven.cli.MavenCli.execute(MavenCli.java:993)
	at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:345)
	at org.apache.maven.cli.MavenCli.main(MavenCli.java:191)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:289)
	at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:229)
	at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:415)
	at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:356)
	at org.codehaus.classworlds.Launcher.main(Launcher.java:47)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
Caused by: org.apache.maven.plugin.PluginExecutionException: Execution default-cli of goal org.mybatis.generator:mybatis-generator-maven-plugin:1.3.5:generate failed.
	at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:145)
	at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:208)
	... 26 more
Caused by: java.lang.NullPointerException
	at com.itfsw.mybatis.generator.plugins.utils.IncrementsPluginTools.supportColumn(IncrementsPluginTools.java:121)
	at com.itfsw.mybatis.generator.plugins.IncrementsPlugin.generatedWithSelective(IncrementsPlugin.java:189)
	at com.itfsw.mybatis.generator.plugins.IncrementsPlugin.sqlMapUpdateByExampleSelectiveElementGenerated(IncrementsPlugin.java:105)
	at org.mybatis.generator.internal.PluginAggregator.sqlMapUpdateByExampleSelectiveElementGenerated(PluginAggregator.java:320)
	at org.mybatis.generator.codegen.mybatis3.xmlmapper.elements.UpdateByExampleSelectiveElementGenerator.addElements(UpdateByExampleSelectiveElementGenerator.java:81)
	at org.mybatis.generator.codegen.mybatis3.xmlmapper.XMLMapperGenerator.initializeAndExecuteGenerator(XMLMapperGenerator.java:250)
	at org.mybatis.generator.codegen.mybatis3.xmlmapper.XMLMapperGenerator.addUpdateByExampleSelectiveElement(XMLMapperGenerator.java:199)
	at org.mybatis.generator.codegen.mybatis3.xmlmapper.XMLMapperGenerator.getSqlMapElement(XMLMapperGenerator.java:83)
	at org.mybatis.generator.codegen.mybatis3.xmlmapper.XMLMapperGenerator.getDocument(XMLMapperGenerator.java:258)
	at org.mybatis.generator.codegen.mybatis3.IntrospectedTableMyBatis3Impl.getGeneratedXmlFiles(IntrospectedTableMyBatis3Impl.java:268)
	at org.mybatis.generator.config.Context.generateFiles(Context.java:723)
	at org.mybatis.generator.api.MyBatisGenerator.generate(MyBatisGenerator.java:269)
	at org.mybatis.generator.api.MyBatisGenerator.generate(MyBatisGenerator.java:189)
	at org.mybatis.generator.maven.MyBatisGeneratorMojo.execute(MyBatisGeneratorMojo.java:199)
	at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:134)
	... 27 more

集成gradle

项目中现在用到了gradle、用到了官方的generator,如何再引入这个插件,还请执教,谢谢

乐观锁插件

并发更新应该引入乐观锁,原理是简单,基于version就可以,但是如果插件自动生成代码,那应该更方便了。

目前google结果是,mybatis项目本身对乐观锁提供了支持,然后以下mybatis项目好像支持乐观锁:

第一个也是插件,但是我觉得代码有点复杂,不像本项目一样一个插件就是一个类,同时不支持maven;后面两个项目,我觉得会引入依赖,目前我觉得插件形式自动生成代码既简单又直观有效。

当前项目没有乐观锁插件,不知道作者在实际项目是是否有更好的解决方案,希望交流一下。

hi

我尝试使用了插件消除表名 t 前缀
发现,仍然无法消除。
具体情况:
mybatis-generator-config.xml

    <!-- Table重命名插件 -->
    <plugin type="com.itfsw.mybatis.generator.plugins.TablePrefixPlugin">
      <!-- 可根据具体需求确定是否配置 -->
      <property name="searchString" value="^T"/>
      <property name="replaceString" value=""/>
    </plugin>

    <!-- 该插件给实体类添加toString()方法  -->
    <plugin type="org.mybatis.generator.plugins.ToStringPlugin"/>

    <!-- 这个插件给由MBG生成的Java模型对象增加了equals和hashCode方法 -->
    <plugin type="org.mybatis.generator.plugins.EqualsHashCodePlugin"/>
    <!-- 序列化插件 -->
    <plugin type="org.mybatis.generator.plugins.SerializablePlugin"/>

pom.xml

<!-- mybatis generator 插件 -->
      <plugin>
        <groupId>org.mybatis.generator</groupId>
        <artifactId>mybatis-generator-maven-plugin</artifactId>
        <version>${dev.plugin.mybatis-generator}</version>
        <configuration>
          <configurationFile>${basedir}/src/main/resources/mybatis-generator-config.xml</configurationFile>
          <overwrite>true</overwrite>
          <verbose>true</verbose>
        </configuration>
        <dependencies>
          <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${dev.lib.mysql-connector}</version>
          </dependency>
          <!-- 增强mybatis-generator 插件 -->
          <dependency>
            <groupId>com.itfsw</groupId>
            <artifactId>mybatis-generator-plugin</artifactId>
            <version>1.0.11</version>
          </dependency>
        </dependencies>
      </plugin>

配置如上,执行的时候,T表前缀仍然无法去除。

13. 自定义注释插件 复制模板到idea文件导致格式混乱的问题

找了很久很久,终于找到了这样的插件中的插件,很感谢。

复制模板到idea文件导致格式混乱的问题

由于idea的原因,模板中的文本复制过去会被转换,导致生成注释格式混乱

  • 建议提供模板文件的下载地址,或者插件默认加载默认的模板文件
  • 临时解决方案,可以不使用idea粘贴文件,用最基本的文本编辑器粘贴模板文件。

关于in 空list的建议

当where中出现in list时,如果list为empty list,会报错,能否针对这种情况在生成代码时进行处理,让in empty list时返回空。
mbg生成的相关代码:

								<when test="criterion.listValue">
									and ${criterion.condition}
									<foreach collection="criterion.value" item="listItem"
										open="(" close=")" separator=",">
										#{listItem}
									</foreach>
								</when>

使用选择性插入(insertSelective) 时,useGeneratedKeys 属性无效

你好!

在我使用该插件生成选择性插入(insertSelective)代码后,在 XML 里指明对象 useGeneratedKeys=true ,调用该方法,对象的主键不自动赋值。

环境

  • MySQL: 5.7.22
  • JDK: 1.8.0_171
  • mybatis-generator-maven-plugin: 1.3.6
  • mybatis-generator-plugin: 1.2.9
  • mysql-connector-plugin: 5.1.38

对象定义

  • 表设计
create table tb_foo
(
 fid int priamry key auto_increment,
 fname varchar(50) not null,
 // 省略其它列的定义
)
  • 自动生成的 Java Model
public class Foo{

	private Integer fid;

	private String name;

	// 省略其它属性
	// 省略 getter 和 setter
}
  • insertSelective 代码
<insert id="insertSelective" useGeneratedKeys="true" keyPropertry="fid" parameterType="map">
	insert into tb_foo
	<choose>
		<when test="selective != null and selective.length &gt; 0">
			// 省略代码
		</when>
		<otherwise>
			// 省略代码
		</otherwise>
	</choose>
</insert>
  • 测试代码
@Autowired
private FooMapper fooMapper;

@Test
public void testInsertFoo(){

	Foo foo = Foo.builder()
		.name("fooName")
		.build();

	fooMapper.insertSelective(foo);

	// 测试不通过,foo的fid属性没有被赋值
	Assert.assertNotNull(foo.getfid());
}

使用该插件,进行全属性插入(insert)时,指明 userGeneratedKeys=true keyPropertry="fid" 可以为对象赋予主键 ID。如:

@Autowired
private FooMapper fooMapper;

@Test
public void testInsertFoo(){

	Foo foo = Foo.builder()
		.name("fooName")
		...  // 其它属性赋值
		.build();

	fooMapper.insert(foo);

	// 测试通过
	Assert.assertNotNull(foo.getfid());
}

可以增加个批量的 插入存在便更新的插件

-- auto-generated definition
CREATE TABLE student
(
id INT AUTO_INCREMENT
COMMENT '自增id'
PRIMARY KEY,
no INT DEFAULT '0' NOT NULL,
type TINYINT DEFAULT '0' NOT NULL,
age INT DEFAULT '0' NOT NULL
COMMENT '年龄',
name VARCHAR(64) DEFAULT '' NOT NULL
COMMENT '姓名',
CONSTRAINT no
UNIQUE (no, type)
)
ENGINE = InnoDB;


INSERT INTO test.student (id, no, type, age, name) VALUES (2, 15, 2, 100, 'AAACCC');
INSERT INTO test.student (id, no, type, age, name) VALUES (4, 12, 1, 9999, 'DDD');


增加如下操作功能???---- 现在是单条的 插入存在便更新的插件

INSERT INTO student (no, type, age, name) VALUES (15, 2, 100, "长者"), (12, 1, 9999, "香港记者")
ON DUPLICATE KEY UPDATE age = values(age), name = values(name)

关于mysql驱动支持

https://github.com/itfsw/mybatis-generator-plugin/blob/master/src/main/java/com/itfsw/mybatis/generator/plugins/BatchInsertPlugin.java

// 插件使用前提是数据库为MySQL或者SQLserver,因为返回主键使用了JDBC的getGenereatedKeys方法获取主键 if ("com.mysql.jdbc.Driver".equalsIgnoreCase(this.getContext().getJdbcConnectionConfiguration().getDriverClass()) == false && "com.microsoft.jdbc.sqlserver.SQLServer".equalsIgnoreCase(this.getContext().getJdbcConnectionConfiguration().getDriverClass()) == false && "com.microsoft.sqlserver.jdbc.SQLServerDriver".equalsIgnoreCase(this.getContext().getJdbcConnectionConfiguration().getDriverClass()) == false) { warnings.add("itfsw:插件" + this.getClass().getTypeName() + "插件使用前提是数据库为MySQL或者SQLserver,因为返回主键使用了JDBC的getGenereatedKeys方法获取主键!"); return false; }

这个地方,com.mysql.cj.jdbc.Driver ,新的驱动类型不支持。

批量插入的selective不太友好

批量插入插件,batchInsertSelective不太友好,需要指定特定的列。可否使用原生xml的
<if test="fielName != null">
这种方式拼接

文档错误

在README.md文件中关于逻辑删除插件使用说明有处错误,
配置逻辑删除常量名称的name错了,会导致生成的实体类中的值都是false。

这是原来的

    <!-- 逻辑删除常量名称,不配置默认为 IS_DELETED -->
    <property name="logicalDeleteValue" value="DEL"/>
    <!-- 逻辑删除常量(未删除)名称,不配置默认为 NOT_DELETED -->
    <property name="logicalUnDeleteValue" value="UN_DEL"/>

应改为

        <!-- 逻辑删除常量名称,不配置默认为 IS_DELETED -->
        <property name="logicalDeleteConstName" value="IS_DELETED"/>
        <!-- 逻辑删除常量(未删除)名称,不配置默认为 NOT_DELETED -->
        <property name="logicalUnDeleteConstName" value="NOT_DELETED"/>

batchUpsert

假设数据库中存在两条数据,修改两条数据(主键不变)再进行batchUpsert会返回结果为4

一些修正的建议

1,建议把分页里面的page方法修改为
this.offset = ( page - 1 ) * pageSize;
比较符合使用习惯 page就是页码

2,建议把BatchInsert方法和BatchInsertSelective切分成两个插件
我当前就只想使用BatchInsert方法,就只能修改你的源码了,不太方便

3, 使用作者项目是发现我的一些需求无法解决,就自己写了几个插件
贡献出来希望可以添加到作者的项目里
1>自动生成 mysql generatorKey 的插件 解决每个table设置generatorKey的问题
`

@Override
public boolean validate(List<String> list) {
    return true;
}

@Override
public void initialized(IntrospectedTable introspectedTable) {
    GeneratedKey gk = introspectedTable.getGeneratedKey();
    //如果需要生成参数
    if (gk == null) {
        List<IntrospectedColumn> keyColumns = introspectedTable.getPrimaryKeyColumns();
        IntrospectedColumn keyColumn = null;
        //只能有一个主键列;
        if (keyColumns.size() == 1) {
            //得到这个唯一的主键列
            keyColumn = keyColumns.get(0);
            //得到这个列映射成Java模型之后的属性对应的Java类型;
            FullyQualifiedJavaType javaType = keyColumn.getFullyQualifiedJavaType();
            //要求主键只能是递增的,所以我们把这个主键属性的类型分别和Integer,Long,Short做对比;
            if (javaType.equals(PrimitiveTypeWrapper.getIntegerInstance())
                    || javaType.equals(PrimitiveTypeWrapper.getLongInstance())
                    || javaType.equals(PrimitiveTypeWrapper.getShortInstance())) {
                //设置自增 insert 和 insertSelective 去除字段赋值
                keyColumn.setIdentity(true);
                //设置 Mysql generatedKey 用于生成 selectKey
                GeneratedKey generatedKey = new GeneratedKey(keyColumn.getActualColumnName(), "Mysql", true, "post");
                introspectedTable.getTableConfiguration().setGeneratedKey(generatedKey);
            }
        }
    }
    super.initialized(introspectedTable);
}

`

2> 需要修改mapper后缀为dao 所以写了下面这个插件
`
private String mapperSuffix;
private String sqlSuffix;

public boolean validate(List<String> warnings) {
    //获取配置
    mapperSuffix = properties.getProperty("mapperSuffix");
    sqlSuffix = properties.getProperty("sqlSuffix");
    //至少一个配置
    return stringHasValue(mapperSuffix) || stringHasValue(sqlSuffix);
}

@Override
public void initialized(IntrospectedTable introspectedTable) {
    String modelName = introspectedTable.getFullyQualifiedTable().getDomainObjectName();
    if (stringHasValue(mapperSuffix)) {
        String mapperType = introspectedTable.getMyBatis3JavaMapperType();
        mapperType = mapperType.replaceAll(modelName + "Mapper", modelName + mapperSuffix);
        introspectedTable.setMyBatis3JavaMapperType(mapperType);
    }
    if (stringHasValue(sqlSuffix)) {
        introspectedTable.setMyBatis3XmlMapperFileName(modelName + sqlSuffix);
    }
}`

最后感谢作者的无私奉献,希望项目越做越好

ExampleEnhancedPlugin生成了一个空的ICriteriaAdd类

我在使用的时候添加了

        <plugin type="com.itfsw.mybatis.generator.plugins.ExampleEnhancedPlugin"/>
        <plugin type="com.itfsw.mybatis.generator.plugins.LimitPlugin"/>

两个插件,然后生成的example对象中ICriteriaAdd对象是空的,在andIf方法的add.add(this)会报错

     public Criteria andIf(boolean ifAdd, ICriteriaAdd add) {
            if (ifAdd) {
                add.add(this);
            }
            return this;
        }

        class ICriteriaAdd {
        }

请教下是那里出了问题,谢谢。

逻辑删除有问题1.0.6

对于1.0.6实测,逻辑批量删除是问题的,因为用到了批量更新的sql,所以参数名都是example,但是因为Mapper文件中未加@param,导致执行时,会用XXXExample去找getExample的getter方法。会报错。实际在逻辑删除插件中,即使参数个数为1,也要加上@param。最后,感谢插件的提供

配置设置允许修改时,Mapper 文件中出现了 ID 相同的 SQL

环境列表

  • MyBatis Generator : 1.1.2
  • JDK : 1.8.0_152
  • Maven : 3.5.0

说明

Maven build 文件配置如下:

<configuration>
  <configurationFile>src/main/resources/mybatis_generator.xml</configurationFile>
  <verbose>true</verbose>
  <overwrite>true</overwrite>
</configuration>

当使用 Maven Generator 重新生成 Mapper 文件时,Mapper 文件会追加同名的 SQL 。如下面的图片所示:生成了同名的 selectByExample SQL。

第一次生成 Mapper 文件:

one-match

第二次生成 Mapper 文件:

two-matches

打包的时候发现有7个测试用例不能通过。



Tests run: 13, Failures: 7, Errors: 0, Skipped: 0, Time elapsed: 26.598 sec <<< FAILURE!
testWithLogicalDeletePlugin(com.itfsw.mybatis.generator.plugins.OptimisticLockerPluginTest)  Time elapsed: 0.851 sec  <<< FAILURE!
org.junit.ComparisonFailure: expected:<[]where inc_f1 = 1 and...> but was:<[update tb set inc_f1 = inc_f1 + 1,inc_f2 = 9 ]where inc_f1 = 1 and...>
	at org.junit.Assert.assertEquals(Assert.java:115)
	at org.junit.Assert.assertEquals(Assert.java:144)
	at com.itfsw.mybatis.generator.plugins.OptimisticLockerPluginTest$9.reloadProject(OptimisticLockerPluginTest.java:368)
	at com.itfsw.mybatis.generator.plugins.tools.AbstractShellCallback.refreshProject(AbstractShellCallback.java:59)
	at org.mybatis.generator.api.MyBatisGenerator.generate(MyBatisGenerator.java:289)
	at com.itfsw.mybatis.generator.plugins.tools.MyBatisGeneratorTool.generate(MyBatisGeneratorTool.java:95)
	at com.itfsw.mybatis.generator.plugins.OptimisticLockerPluginTest.testWithLogicalDeletePlugin(OptimisticLockerPluginTest.java:344)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:252)
	at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:141)
	at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:112)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:189)
	at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:165)
	at org.apache.maven.surefire.booter.ProviderFactory.invokeProvider(ProviderFactory.java:85)
	at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:115)
	at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:75)

testUpdateWithVersionByPrimaryKey(com.itfsw.mybatis.generator.plugins.OptimisticLockerPluginTest)  Time elapsed: 0.897 sec  <<< FAILURE!
org.junit.ComparisonFailure: expected:<[]where inc_f1 = 100 a...> but was:<[update tb set inc_f1 = inc_f1 + 1, field1 = 'null', inc_f2 = 10, inc_f3 = 5 ]where inc_f1 = 100 a...>
	at org.junit.Assert.assertEquals(Assert.java:115)
	at org.junit.Assert.assertEquals(Assert.java:144)
	at com.itfsw.mybatis.generator.plugins.OptimisticLockerPluginTest$17.reloadProject(OptimisticLockerPluginTest.java:675)
	at com.itfsw.mybatis.generator.plugins.tools.AbstractShellCallback.refreshProject(AbstractShellCallback.java:59)
	at org.mybatis.generator.api.MyBatisGenerator.generate(MyBatisGenerator.java:289)
	at com.itfsw.mybatis.generator.plugins.tools.MyBatisGeneratorTool.generate(MyBatisGeneratorTool.java:95)
	at com.itfsw.mybatis.generator.plugins.OptimisticLockerPluginTest.testUpdateWithVersionByPrimaryKey(OptimisticLockerPluginTest.java:662)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:252)
	at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:141)
	at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:112)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:189)
	at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:165)
	at org.apache.maven.surefire.booter.ProviderFactory.invokeProvider(ProviderFactory.java:85)
	at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:115)
	at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:75)

testUpdateWithVersionByPrimaryKeyWithBLOBs(com.itfsw.mybatis.generator.plugins.OptimisticLockerPluginTest)  Time elapsed: 0.97 sec  <<< FAILURE!
org.junit.ComparisonFailure: expected:<[]where inc_f1 = 100 a...> but was:<[update tb_blobs set inc_f1 = inc_f1 + 1, field1 = 'null', inc_f2 = 10, inc_f3 = 5 ]where inc_f1 = 100 a...>
	at org.junit.Assert.assertEquals(Assert.java:115)
	at org.junit.Assert.assertEquals(Assert.java:144)
	at com.itfsw.mybatis.generator.plugins.OptimisticLockerPluginTest$19.reloadProject(OptimisticLockerPluginTest.java:746)
	at com.itfsw.mybatis.generator.plugins.tools.AbstractShellCallback.refreshProject(AbstractShellCallback.java:59)
	at org.mybatis.generator.api.MyBatisGenerator.generate(MyBatisGenerator.java:289)
	at com.itfsw.mybatis.generator.plugins.tools.MyBatisGeneratorTool.generate(MyBatisGeneratorTool.java:95)
	at com.itfsw.mybatis.generator.plugins.OptimisticLockerPluginTest.testUpdateWithVersionByPrimaryKeyWithBLOBs(OptimisticLockerPluginTest.java:733)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:252)
	at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:141)
	at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:112)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:189)
	at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:165)
	at org.apache.maven.surefire.booter.ProviderFactory.invokeProvider(ProviderFactory.java:85)
	at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:115)
	at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:75)

testWithIncrementsPlugin(com.itfsw.mybatis.generator.plugins.OptimisticLockerPluginTest)  Time elapsed: 5.097 sec  <<< FAILURE!
org.junit.ComparisonFailure: expected:<[]where inc_f1 = 100 a...> but was:<[update tb SET inc_f1 = inc_f1 + 1, inc_f2 = inc_f2 + 5 , inc_f3 = 10 ]where inc_f1 = 100 a...>
	at org.junit.Assert.assertEquals(Assert.java:115)
	at org.junit.Assert.assertEquals(Assert.java:144)
	at com.itfsw.mybatis.generator.plugins.OptimisticLockerPluginTest$27.reloadProject(OptimisticLockerPluginTest.java:1030)
	at com.itfsw.mybatis.generator.plugins.tools.AbstractShellCallback.refreshProject(AbstractShellCallback.java:59)
	at org.mybatis.generator.api.MyBatisGenerator.generate(MyBatisGenerator.java:289)
	at com.itfsw.mybatis.generator.plugins.tools.MyBatisGeneratorTool.generate(MyBatisGeneratorTool.java:95)
	at com.itfsw.mybatis.generator.plugins.OptimisticLockerPluginTest.testWithIncrementsPlugin(OptimisticLockerPluginTest.java:1016)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:252)
	at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:141)
	at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:112)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:189)
	at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:165)
	at org.apache.maven.surefire.booter.ProviderFactory.invokeProvider(ProviderFactory.java:85)
	at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:115)
	at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:75)

testUpdateWithVersionByPrimaryKeySelective(com.itfsw.mybatis.generator.plugins.OptimisticLockerPluginTest)  Time elapsed: 0.843 sec  <<< FAILURE!
org.junit.ComparisonFailure: expected:<[]where inc_f1 = 100 a...> but was:<[update tb SET inc_f1 = inc_f1 + 1, inc_f2 = 10, inc_f3 = 5 ]where inc_f1 = 100 a...>
	at org.junit.Assert.assertEquals(Assert.java:115)
	at org.junit.Assert.assertEquals(Assert.java:144)
	at com.itfsw.mybatis.generator.plugins.OptimisticLockerPluginTest$15.reloadProject(OptimisticLockerPluginTest.java:606)
	at com.itfsw.mybatis.generator.plugins.tools.AbstractShellCallback.refreshProject(AbstractShellCallback.java:59)
	at org.mybatis.generator.api.MyBatisGenerator.generate(MyBatisGenerator.java:289)
	at com.itfsw.mybatis.generator.plugins.tools.MyBatisGeneratorTool.generate(MyBatisGeneratorTool.java:95)
	at com.itfsw.mybatis.generator.plugins.OptimisticLockerPluginTest.testUpdateWithVersionByPrimaryKeySelective(OptimisticLockerPluginTest.java:593)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:252)
	at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:141)
	at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:112)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:189)
	at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:165)
	at org.apache.maven.surefire.booter.ProviderFactory.invokeProvider(ProviderFactory.java:85)
	at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:115)
	at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:75)

testWithSelectiveEnhancedPlugin(com.itfsw.mybatis.generator.plugins.OptimisticLockerPluginTest)  Time elapsed: 2.115 sec  <<< FAILURE!
org.junit.ComparisonFailure: expected:<[]where inc_f1 = 100 a...> but was:<[update tb SET inc_f1 = inc_f1 + 1, inc_f2 = 10, inc_f3 = 5 ]where inc_f1 = 100 a...>
	at org.junit.Assert.assertEquals(Assert.java:115)
	at org.junit.Assert.assertEquals(Assert.java:144)
	at com.itfsw.mybatis.generator.plugins.OptimisticLockerPluginTest$32.reloadProject(OptimisticLockerPluginTest.java:1226)
	at com.itfsw.mybatis.generator.plugins.tools.AbstractShellCallback.refreshProject(AbstractShellCallback.java:59)
	at org.mybatis.generator.api.MyBatisGenerator.generate(MyBatisGenerator.java:289)
	at com.itfsw.mybatis.generator.plugins.tools.MyBatisGeneratorTool.generate(MyBatisGeneratorTool.java:95)
	at com.itfsw.mybatis.generator.plugins.OptimisticLockerPluginTest.testWithSelectiveEnhancedPlugin(OptimisticLockerPluginTest.java:1202)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:252)
	at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:141)
	at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:112)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:189)
	at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:165)
	at org.apache.maven.surefire.booter.ProviderFactory.invokeProvider(ProviderFactory.java:85)
	at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:115)
	at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:75)

testWithSelectiveEnhancedPluginAndIncrementsPlugin(com.itfsw.mybatis.generator.plugins.OptimisticLockerPluginTest)  Time elapsed: 2.181 sec  <<< FAILURE!
org.junit.ComparisonFailure: expected:<[]where inc_f1 = 100 a...> but was:<[update tb SET inc_f1 = inc_f1 + 1, inc_f2 = inc_f2 + 5 , inc_f3 = 10 ]where inc_f1 = 100 a...>
	at org.junit.Assert.assertEquals(Assert.java:115)
	at org.junit.Assert.assertEquals(Assert.java:144)
	at com.itfsw.mybatis.generator.plugins.OptimisticLockerPluginTest$34.reloadProject(OptimisticLockerPluginTest.java:1332)
	at com.itfsw.mybatis.generator.plugins.tools.AbstractShellCallback.refreshProject(AbstractShellCallback.java:59)
	at org.mybatis.generator.api.MyBatisGenerator.generate(MyBatisGenerator.java:289)
	at com.itfsw.mybatis.generator.plugins.tools.MyBatisGeneratorTool.generate(MyBatisGeneratorTool.java:95)
	at com.itfsw.mybatis.generator.plugins.OptimisticLockerPluginTest.testWithSelectiveEnhancedPluginAndIncrementsPlugin(OptimisticLockerPluginTest.java:1307)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:252)
	at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:141)
	at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:112)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:189)
	at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:165)
	at org.apache.maven.surefire.booter.ProviderFactory.invokeProvider(ProviderFactory.java:85)
	at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:115)
	at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:75)

Running com.itfsw.mybatis.generator.plugins.BatchInsertPluginTest

集成通用mapper时生成mapper出现问题

由于集成了通用mapper(tk.mybatis),在生成table时需要把example关闭,如:
<table tableName="t_user_info" domainObjectName="UserInfo" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"> </table>
但是设置为false之后生成的mapper没有import ibatis的Param:
Param cannot be resolved to
a type
请问楼主有集成过通用mapper吗?

OptimisticLockerPlugin如何使用以及可能的bug

最近准备采用乐观锁插件解决并发控制。
使用过程中发现一些问题。

  1. 当前OptimisticLockerPlugin的使用方法如下描述:
<xml>
    <!-- 乐观锁插件 -->
    <plugin type="com.itfsw.mybatis.generator.plugins.OptimisticLockerPlugin">
        <!-- 是否启用自定义nextVersion, 默认不启用使用(使用sql的 set column = column + 1) -->
        <property name="customizedNextVersion" value="true"/>
    </plugin>
    
    <table tableName="tb">
        <!-- 这里可以单独表配置,覆盖全局配置 -->
        <property name="customizedNextVersion" value="false"/>
        <!-- 指定版本列 -->
        <property name="versionColumn" value="version"/>
    </table>
</xml>

这里customizedNextVersion到底是什么以及如何使用不是很明白。

  1. 按照本人的理解,认为只要启动插件,即可自动生成相关代码。
    因此采用如下配置
<xml>
    <!-- 乐观锁插件 -->
    <plugin type="com.itfsw.mybatis.generator.plugins.OptimisticLockerPlugin">
    </plugin>
</xml>

然后,实际上没有任何反应,没有代码生成。

因此,我这里采用如下配置

<xml>
    <!-- 乐观锁插件 -->
    <plugin type="com.itfsw.mybatis.generator.plugins.OptimisticLockerPlugin">
    </plugin>
    
    <table tableName="tb">
        <property name="versionColumn" value="version"/>
    </table>
</xml>

这里,项目自动生成了乐观锁的相关代码。
因此,这里的使用方法到底是什么样的。是插件只是开启功能,在表配置中声明才能使用?

  1. 此外,还有一个与设想不一致的现象是,我这里多个表,但是只有表一采用了配置,而其他表没有采用配置。
<xml>
    <!-- 乐观锁插件 -->
    <plugin type="com.itfsw.mybatis.generator.plugins.OptimisticLockerPlugin">
    </plugin>
    
    <table tableName="tb">
        <property name="versionColumn" value="version"/>
    </table>
    <table tableName="tb2">
    </table>
</xml>

我预期是只有表一会生成相关代码,而其他表不会生成代码。
但是实际结果是表一和表二都会生成代码。
因此,个人觉得这里可能是插件代码bug,也可能是设计上不是很合理。

由于这里的问题,我目前是暂缓乐观锁插件的使用。

  1. 目前本人开源项目使用mybatis框架,以及作者这里的相关插件。
    不知道作者有没有qq或者微信,可以有机会实时沟通一些使用中的问题。
    本人也可以帮忙测试以及反馈使用中的问题。

1.1.2中有Bug

指定了逻辑删除列,且配置有rootClass的情况下,还是直接取bean中的删除列字段
这时删除列在rootClass中,是private属性,不能直接引用的

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.