解读tk.mybatis的通用批量更新方式

 更新时间:2024年08月17日 12:15:39   作者: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数据库需要在语句的前后添加关键字BEGINEND;
  • 其他的实现方式都无法在多种数据库中使用;
  • 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等数据库。

这些仅为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • java的线程池框架及线程池的原理

    java的线程池框架及线程池的原理

    这篇文章主要介绍了java的线程池框架及线程池的原理的相关资料,需要的朋友可以参考下
    2017-03-03
  • Java内存溢出案例模拟和原理分析过程

    Java内存溢出案例模拟和原理分析过程

    这篇文章主要介绍了Java内存溢出案例模拟和原理分析过程,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-04-04
  • Java Hibernate使用SessionFactory创建Session案例详解

    Java Hibernate使用SessionFactory创建Session案例详解

    这篇文章主要介绍了Java Hibernate使用SessionFactory创建Session案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-08-08
  • JDK 8和JDK 17的区别和新特性大全

    JDK 8和JDK 17的区别和新特性大全

    这篇文章主要给大家介绍了关于JDK 8和JDK 17的区别和新特性的相关资料,文中总结一些Jdk8到Jdk17的一些新特性,给大家选择jdk版本的时候有些参考性,需要的朋友可以参考下
    2023-06-06
  • Spring实战之容器后处理器操作示例

    Spring实战之容器后处理器操作示例

    这篇文章主要介绍了Spring实战之容器后处理器操作,结合实例形式分析了spring容器后处理器配置、使用操作技巧与相关注意事项,需要的朋友可以参考下
    2019-12-12
  • Springboot项目使用html5的video标签完成视频播放功能

    Springboot项目使用html5的video标签完成视频播放功能

    这篇文章主要介绍了Springboot项目使用html5的video标签完成视频播放功能,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-12-12
  • springboot默认扫描的路径方式

    springboot默认扫描的路径方式

    这篇文章主要介绍了springboot默认扫描的路径方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-07-07
  • Java关键字this与super详解用法

    Java关键字this与super详解用法

    这篇文章主要介绍了Java关键字this与super的用法,this与super是类实例化时通往Object类通道的打通者;this和super在程序中由于其经常被隐式的使用而被我们忽略,但是理解其作用和使用规范肯定是必须的。接下来将详述this与super各自的的作用,需要的朋友可以参考一下
    2022-04-04
  • SpringBoot项目中的视图解析器问题(两种)

    SpringBoot项目中的视图解析器问题(两种)

    SpringBoot官网推荐使用HTML视图解析器,但是根据个人的具体业务也有可能使用到JSP视图解析器,所以本文介绍了两种视图解析器,感兴趣的可以了解下
    2020-06-06
  • 新闻列表的分页查询java代码实现

    新闻列表的分页查询java代码实现

    这篇文章主要为大家详细介绍了新闻列表的分页查询java代码实现,感兴趣的小伙伴们可以参考一下
    2016-08-08

最新评论