mybatis generator 是什么?
什么是数据版本号?
方式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>