解读tk.mybatis的通用批量更新方式
作者:Zephyr丶Syn
这篇文章主要介绍了关于tk.mybatis的通用批量更新方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
背景介绍
mybatis没有提供批量更新的方法,通过代码中循环调用单个更新方法太消耗资源影响性能,
在xml中写批量更新SQL又太繁琐并且无法复用,并且项目中需要兼容多种类型数据库,
因此在tk.mybatis的基础上扩展一个通用批量更新Provider和Mapper;
实现原理
可选批量更新实现的方式:
on duplicate key update
语法,存在则更新,不存在则插入,能同时实现插入和更新,但on duplicate key update
是MySQL特有语法,切换成其他类型数据库就无法使用了。- foreach成多条SQL去执行,但Mybatis映射文件中的sql语句默认是不支持以" ; " 结尾的,也就是不支持多条sql语句的执行,为了支持这种方式,不同数据库的处理方式也不同,MySQL数据库需要在URL上设置
&allowMultiQueries=true
,Oracle数据库需要在语句的前后添加关键字BEGIN
和END;
: - 其他的实现方式都无法在多种数据库中使用;
case when
语法,该语法在常用的数据库(MySQL、Oracle、DM、OB等)中都是支持的。
最终选定通过case when语法来实现,并扩展一个通用批量更新Provider和Mapper;
实现代码
tk.mybatis的maven依赖: <dependency> <groupId>tk.mybatis</groupId> <artifactId>mapper-spring-boot-starter</artifactId> <version>2.2.4.4</version> </dependency>
UpdateListProvider类: package com.demo.ibatis.provider; import org.apache.ibatis.mapping.MappedStatement; import tk.mybatis.mapper.entity.EntityColumn; import tk.mybatis .mapper.mapperhelper.EntityHelper; import tk.mybatis.mapper.mapperhelper.MapperHelper; import tk.mybatis.mapper.mapperhelper.MapperTemplate; import tk.mybatis.mapper.mapperhelper.SglHeTper; import tk.mybatis.mapper.util.StringUtil; import java.util.Set; public class UpdateListProvider extends MapperTemplate { public UpdatelistProvider(Class<?> mapperClass, MapperHelper mapperHelper) { super(mapperClass, mapperHelper); } /** * 根据主键批量更新实体所有属性值,使用case when 方式,支持联合主键 * * @param ms MappedStatement * @return sql */ public String updateListByPrimaryKey(MappedStatement ms) { return this.sglHelper(ms, false); } /** *根据主键批量更新实体中不是null的属性值,使用case when方式,支持联合主键 * * @param ms MappedStatement * @return sql */ public String updateListByPrimaryKeySelective(MappedStatement ms) { return this.sglHelper(ms, true); } private String sqlHelper(MappedStatement ms, boolean notNull) { final Class<?> entityclass = getEntityClass(ms); // 开始拼sql StringBuilder sgl = new StringBuilder(); sql.append(SqlHelper.updateTable(entityclass,tableName(entityclass))); sql.append("<trim prefix=\"set\" suffixOverrides= \",\">"); // 获取全部列 Set<EntityColumn> allColumns = EntityHelper.getColumns(entityClass); // 找到主键列 Set<EntityColumn> pkColumns = EntityHelper.getPKColumns(entityclass); for (EntityColumn column : allColumns) { if (!column.isId() && column.isUpdatable()) { sql.append(" <trim prefix=\"").append(column.getColumn()).append(" = case\" suffix= \"end,\">"); sgl.append(" <foreach collection= \"list\" item= \"i\" index= \"index\">"); if (notNull) { sql.append(this.getIfNotNull("i", column, isNotEmpty())); } sql.append(" when "); int count = 0; for (EntityColumn pk : pkColumns) { if (count != 0) { sql.append("and "); } sql.append(pk.getColumn()).append("=#{i.").append(pk.getProperty()).append("} "); count++; } sql.append("then ").append(column.getColumnHolder("i")); if (notNull) { sql.append(" </if>"); } sql.append(" </foreach>"); sql.append(" </trim>"); } } sql.append("</trim>"); sql.append("WHERE ("); int count = 0; for (EntityColumn pk : pkColumns) { sql.append(pk.getCotumn()); if (count < pkColumns.size() - 1) { sql.append(", "); } count++; } sql.append(") IN"); sql.append("<trim prefix= \"(\" suffix= \")\">"); sql.append("<foreach collection=\"list\" separator=\"), (\" item=\"i\" index=\"index\" open=\"(\" close=\")\" >"); count = 0; for (EntityColumn pk : pkColumns) { sql.append("#{i.").append(pk.getProperty()).append("}"); if (count < pkColumns.size() - 1) { sg.append(", "); } count++; } sql.append("</foreach>"); sql.append("</trim>"); return sql.toString(); } private String getIfNotNull(String entityNameEntityColumn column, boolean empty) { StringBuilder sql = new StringBuilder(); sql.append(" <if test=\""); if (StringUtil.isNotEmpty(entityName)) { sql.append(entityName).append("."); } sql.append(column.getProperty()).append(" != null"); if (empty && column.getJavaType().equals(String.class)) { sql.append(" and "); if (StringUtil.isNotEmpty(entityName)) { sql.append(entityName).append("."); } sql.append(column.getProperty()).append(" != '' "); } sql.append("\">"); return sql.tostring(); } }
UpdateListByPrimaryKeyMapper类: package com.demo.ibatis.mapper; import com.demo.ibatis.provider.UpdateListProvider; import org.apache.ibatis.annotations.UpdateProvider; import tk.mybatis .mapper.annotation.RegisterMapper; import java.util.List; @RegisterMapper public interface UpdateListByPrimaryKeyMapper<T> { /** * 根据主键批量更新实体中所有属性值,支持联合主键 * * @param updateList 参数 * @return int */ @UpdateProvider(type = UpdateListProvider.class,method = "dynamicSQL") int updateListByPrimaryKey(List<T> updateList); }
UpdateListByPrimaryKeySelectiveMapper类: package com.demo.ibatis.mapper; import com.demo.ibatis.provider.UpdateListProvider; import org.apache.ibatis.annotations.UpdateProvider; import tk.mybatis .mapper.annotation.RegisterMapper; import java.util.List; @RegisterMapper public interface UpdateListByPrimaryKeySelectiveMapper<T> { /** 根据主键批量更新实体中不是null的属性值,支持联合主键 * * @param updateList 参数 * @return int */ @UpdateProvider(type = UpdateListProvider.class,method = "dynamicSQL") int updateListByPrimaryKeySelective(List<T> updateList); }
总结
以上,根据主键批量更新数据的方法就实现了,只需要自己的Mapper继承这两个通用扩展Mapper
就可以调用updateListByPrimaryKeySelective()
和updateListByPrimaryKey()
方法进行批量更新了,并且同时支持联合主键
和单一主键
,兼容MySQL、Oracle、DM、OB等数据库。
这些仅为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。