背景介绍
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等数据库。
这些仅为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论