MyBatis 教程

👉 所有文章
文章列表 准备工作 回顾 JDBC 数据准备 查找id为1的用户信息 自定义连接池 不用MyBatis配置文件 查询密码为123的所有用户 如果Bean中成员变量和表中字段命名不一致 更多查询用户的方式 对查询结果排序 日志 添加、删除、修改数据 事务 动态SQL 一对一和一对多的实现 一对一和一对多的延迟加载 多对多的实现 分页查询 把SQL写在注解中 自动生成Mapper代码和映射XML mybatis generator 生成 select for update mybatis generator 支持数据版本号

mybatis generator 支持数据版本号


mybatis generator 是什么?

MyBatis: 自动生成Mapper代码和映射XML

什么是数据版本号?

数据版本号

方式1:使用 select for update 查询数据后修改版本号

参考:mybatis generator 生成 select for update 实现 select for update 。

在事务中通过 select for update 拿到数据的版本号,代码中对版本号加1,然后更新。

方式2:写一个版本号自动加1的插件

参考 mybatis generator 生成 select for update 写一个版本号自动加1的插件,插件会生成新的支持版本号自动更新的方法,例如 updateByExampleSelectiveWithAutoVersion ,其内部会对版本号字段自动加1,类似:

update <table> set version = version + 1 where <condition>;

插件示例:

package tool.plugin;

import org.mybatis.generator.api.FullyQualifiedTable;
import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.PluginAdapter;
import org.mybatis.generator.api.dom.java.Interface;
import org.mybatis.generator.api.dom.java.Method;
import org.mybatis.generator.api.dom.xml.Attribute;
import org.mybatis.generator.api.dom.xml.Document;
import org.mybatis.generator.api.dom.xml.TextElement;
import org.mybatis.generator.api.dom.xml.XmlElement;
import org.mybatis.generator.codegen.mybatis3.ListUtilities;
import org.mybatis.generator.codegen.mybatis3.MyBatis3FormattingUtilities;

import java.util.*;

public class AutoAddVersionPlugin extends PluginAdapter {


    private final Map<FullyQualifiedTable, List<XmlElement>> elementsToAdd = new HashMap<>();

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


    /**
     * 扩展接口中的  UpdateByExampleSelective 方法
     */
    @Override
    public boolean clientUpdateByExampleSelectiveMethodGenerated(Method method,
                                                                 Interface interfaze, IntrospectedTable introspectedTable) {
        if (introspectedTable.getTargetRuntime() == IntrospectedTable.TargetRuntime.MYBATIS3) {
            if (isTableHasVersionFiled(introspectedTable)) {
                copyAndAddMethod(method, interfaze);
            }
        }
        return true;
    }


    /**
     * 扩展 xml 中的  UpdateByExampleSelective 方法
     */
    @Override
    public boolean sqlMapUpdateByExampleSelectiveElementGenerated(
            XmlElement element, IntrospectedTable introspectedTable) {
        if (introspectedTable.getTargetRuntime() == IntrospectedTable.TargetRuntime.MYBATIS3) {
            if (isTableHasVersionFiled(introspectedTable)) {
                copyAndSaveElement(element, introspectedTable);
            }
        }
        return true;
    }


    /**
     * 将 XML 追加的内容放入 XML 根节点中
     * 本方法会在 sqlMapUpdateByExampleSelectiveElementGenerated 之后运行
     */
    @Override
    public boolean sqlMapDocumentGenerated(Document document,
                                           IntrospectedTable introspectedTable) {
        List<XmlElement> elements = elementsToAdd.get(introspectedTable.getFullyQualifiedTable());
        if (elements != null) {
            for (XmlElement element : elements) {
                document.getRootElement().addElement(element);
            }
        }
        return true;
    }


    /**
     * 接口类增加方法
     */
    private void copyAndAddMethod(Method method, Interface interfaze) {
        Method newMethod = new Method(method);
        newMethod.setName(method.getName() + "AndAddVersion");
        interfaze.addMethod(newMethod);
    }

    /**
     * XML 增加内容
     * 参考源码中的 UpdateByExampleSelectiveElementGenerator 类
     */
    private void copyAndSaveElement(XmlElement element, IntrospectedTable introspectedTable) {
        XmlElement answer = new XmlElement("update"); 

        answer.addAttribute(new Attribute(
                "id", introspectedTable.getUpdateByExampleSelectiveStatementId() + "AndAddVersion")); 

        answer.addAttribute(new Attribute("parameterType", "map"));  

        context.getCommentGenerator().addComment(answer);

        StringBuilder sb = new StringBuilder();
        sb.append("update "); 
        sb.append(introspectedTable
                .getAliasedFullyQualifiedTableNameAtRuntime());
        answer.addElement(new TextElement(sb.toString()));

        XmlElement dynamicElement = new XmlElement("set"); 
        answer.addElement(dynamicElement);

        for (IntrospectedColumn introspectedColumn : ListUtilities.removeGeneratedAlwaysColumns(introspectedTable
                .getAllColumns())) {

            // 如果是版本号字段
            if (Objects.equals(introspectedColumn.getProperties().get("isVersionField"), "true")) {
                sb.setLength(0);
                sb.append(MyBatis3FormattingUtilities.getAliasedEscapedColumnName(introspectedColumn));
                sb.append(" = "); 
                sb.append(MyBatis3FormattingUtilities.getAliasedEscapedColumnName(introspectedColumn));
                sb.append(" + 1 ,");
                dynamicElement.addElement(new TextElement(sb.toString()));
            }
            else {
                sb.setLength(0);
                sb.append(introspectedColumn.getJavaProperty("record.")); 
                sb.append(" != null"); 
                XmlElement isNotNullElement = new XmlElement("if"); 
                isNotNullElement.addAttribute(new Attribute("test", sb.toString())); 
                dynamicElement.addElement(isNotNullElement);

                sb.setLength(0);
                sb.append(MyBatis3FormattingUtilities.getAliasedEscapedColumnName(introspectedColumn));
                sb.append(" = "); 
                sb.append(MyBatis3FormattingUtilities.getParameterClause(introspectedColumn, "record.")); 
                sb.append(',');

                isNotNullElement.addElement(new TextElement(sb.toString()));
            }


        }

        answer.addElement(getUpdateByExampleIncludeElement(introspectedTable));

        // 追加
        List<XmlElement> elements = elementsToAdd.get(introspectedTable.getFullyQualifiedTable());
        if (elements == null) {
            elements = new ArrayList<XmlElement>();
            elementsToAdd.put(introspectedTable.getFullyQualifiedTable(), elements);
        }
        elements.add(answer);

    }


    private XmlElement getUpdateByExampleIncludeElement(IntrospectedTable introspectedTable) {
        XmlElement ifElement = new XmlElement("if"); 
        ifElement.addAttribute(new Attribute("test", "_parameter != null"));  

        XmlElement includeElement = new XmlElement("include"); 
        includeElement.addAttribute(new Attribute("refid", 
                introspectedTable.getMyBatis3UpdateByExampleWhereClauseId()));
        ifElement.addElement(includeElement);

        return ifElement;
    }

    private boolean isTableHasVersionFiled(IntrospectedTable introspectedTable) {
        for (IntrospectedColumn introspectedColumn : ListUtilities.removeGeneratedAlwaysColumns(introspectedTable.getAllColumns())) {
            if (Objects.equals(introspectedColumn.getProperties().get("isVersionField"), "true")) {
                return true;
            }
        }
        return false;
    }



}

对于版本号字段需要在 generator 用到的 XML 文件中指定 isVersionField 属性为 true,示例:

<table tableName="user_info" domainObjectName="UserInfo" modelType="flat" delimitIdentifiers="true" delimitAllColumns="true">
    <generatedKey column="id" sqlStatement="MySql" identity="true" />
    <columnOverride column="version">
        <property name="isVersionField" value="true"/>
    </columnOverride>
</table>

( 本文完 )

文章目录