- 背景介绍:
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等数据库。