一、业务背景

  由于需要从A数据库提取大量数据同步到B系统,采用了tomikos+jta进行分布式事务管理,先将系统数据源切换到数据提供方,将需要同步的数据查询出来,然后再将系统数据源切换到数据接收方,进行批量的插入和更新操作,

关于数据源的切换可以参考之前的文章《spring+springMVC+Mybatis架构下采用AbstractRoutingDataSource、atomikos、JTA实现多数据源灵活切换以及分布式事务管理

二、批量插入的具体实现

  1.查询需要同步的数据:

    @Autowired
SysPersonPOMapper sysPersonPOMapper; public void dataDs(){
//根据具体情况可以创建查询条件或者采用自定义的Mapper进行查询
SysPersonPOExample sysPersonPOExample = new SysPersonPOExample();
sysPersonPOExample.createCriteria().andIsDeleteEqualTo(false);
//查询需要同步的数据
List<SysPersonPO> persons = sysPersonPOMapper.selectByExample(sysPersonPOExample);
}

  2.将不能进行遍历的PO实体对象转为Map,此处有一个前提条件:MySQL中的表字段是按全大写加下划线命名,实体类PO映射字段为对应的标准驼峰命名方式,进行PO到Map

的转换时,会遵循这一规则。

    @Autowired
SysPersonPOMapper sysPersonPOMapper; public void dataDs() throws Exception{
//1.查询需要同步的数据
//根据具体情况可以创建查询条件或者采用自定义的Mapper进行查询
SysPersonPOExample sysPersonPOExample = new SysPersonPOExample();
sysPersonPOExample.createCriteria().andIsDeleteEqualTo(false);
//查询需要同步的数据
List<SysPersonPO> persons = sysPersonPOMapper.selectByExample(sysPersonPOExample); //2.将不能进行遍历的PO实体对象转为Map //用于存放转换后的对象的List
List<Map<String,Object>> insertItems = Lists.newArrayList();
for (SysPersonPO sysPersonPO : persons) {
Map<String,Object> insertItem = BeanMapUtil.convertBean2MapWithUnderscoreName(sysPersonPO);
insertItems.add(insertItem);
}
}

  

BeanMapUtil类,PO到Map的转换方法,基于类反射技术:
import com.fms.common.utils.other.StringUtil;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.MethodDescriptor;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.sql.Timestamp;
import java.util.Date;
import java.util.HashMap;
import java.util.Map; import org.apache.commons.beanutils.BeanUtils; public class BeanMapUtil { @SuppressWarnings({"unchecked", "rawtypes"})
public static Map convertBean2MapWithUnderscoreName(Object bean) throws Exception {
Map returnMap = null;
try {
Class type = bean.getClass();
returnMap = new HashMap();
BeanInfo beanInfo = Introspector.getBeanInfo(type);
PropertyDescriptor[] propertyDescriptors = beanInfo
.getPropertyDescriptors();
for (int i = 0; i < propertyDescriptors.length; i++) {
PropertyDescriptor descriptor = propertyDescriptors[i];
String propertyName = descriptor.getName();
if (!propertyName.equalsIgnoreCase("class")) {
Method readMethod = descriptor.getReadMethod();
Object result = readMethod.invoke(bean, new Object[0]);
returnMap.put( StringUtil.underscoreName(propertyName), result);
}
}
} catch (Exception e) {
// 解析错误时抛出服务器异常 记录日志
throw new Exception("从bean转换为map时异常!", e);
}
return returnMap;
} }

StringUtil类,标准驼峰命名与数据库下划线命名之间转换的方法:

public class StringUtil {

   /**
* 将驼峰式命名的字符串转换为下划线大写方式。如果转换前的驼峰式命名的字符串为空,则返回空字符串。</br>
* 例如:HelloWorld->HELLO_WORLD
*
* @param name 转换前的驼峰式命名的字符串
* @return 转换后下划线大写方式命名的字符串
*/
public static String underscoreName(String name) {
StringBuilder result = new StringBuilder();
if (name != null && name.length() > 0) {
// 将第一个字符处理成大写
result.append(name.substring(0, 1).toUpperCase());
// 循环处理其余字符
for (int i = 1; i < name.length(); i++) {
String s = name.substring(i, i + 1);
// 在大写字母前添加下划线
if (s.equals(s.toUpperCase()) && !Character.isDigit(s.charAt(0))) {
result.append("_");
}
// 其他字符直接转成大写
result.append(s.toUpperCase());
}
}
return result.toString();
}
}

3.编写Mybatis映射文件fmsDataDsMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.fms.common.dao.fmsDataDsMapper"> <!-- 批量插入,传入表名和需要插入的数据的集合 -->
<insert id="insertDatas" parameterType="map">
insert into ${table_name}
<foreach collection="fields" index="field" item="fieldVal" separator="," open="(" close=")">
${field}
</foreach>
values
<foreach collection="list" index="index" item="record" separator="," >
<foreach collection="record" index="key" item="item" separator="," open="(" close=")">
#{item}
</foreach>
</foreach>
</insert> </mapper>

4.调用sqlsession相关API的insert方法插入数据:

    @Autowired
SysPersonPOMapper sysPersonPOMapper; public void dataDs() throws Exception{
//1.查询需要同步的数据
//根据具体情况可以创建查询条件或者采用自定义的Mapper进行查询
SysPersonPOExample sysPersonPOExample = new SysPersonPOExample();
sysPersonPOExample.createCriteria().andIsDeleteEqualTo(false);
//查询需要同步的数据
List<SysPersonPO> persons = sysPersonPOMapper.selectByExample(sysPersonPOExample); //2.将不能进行遍历的PO实体对象转为Map //用于存放转换后的对象的List
List<Map<String,Object>> insertItems = Lists.newArrayList();
for (SysPersonPO sysPersonPO : persons) {
Map<String,Object> insertItem = BeanMapUtil.convertBean2MapWithUnderscoreName(sysPersonPO);
insertItems.add(insertItem);
} //3.插入数据
insertDatas(insertItems,"sys_person");
} @Autowired
SqlSessionTemplate sqlSessionTemplate;
private String dataDsNameSpace = "com.fms.common.dao.fmsDataDsMapper";
private void insertDatas(List<Map<String,Object>> insertItems, String tableName){
if (!insertItems.isEmpty()) {
Map<String,Object> params = Maps.newHashMap();
       //这里把数据分成每1000条执行一次,可根据实际情况进行调整
int count = insertItems.size() / 1000;
int yu = insertItems.size() % 1000;
for (int i = 0; i <= count; i++) {
List<Map<String,Object>> subList = Lists.newArrayList();
if (i == count) {
if(yu != 0){
subList = insertItems.subList(i * 1000, 1000 * i + yu);
}else {
continue;
}
} else {
subList = insertItems.subList(i * 1000, 1000 * (i + 1));
}
params.put("table_name", tableName);
params.put("fields", subList.get(0));
params.put("list", subList);
sqlSessionTemplate.insert(dataDsNameSpace+".insertDatas", params);
}
}
}

三 、批量更新的具体实现

  通常我们根据主键更新时的语句是 update  table_name set column1 = val1 , column2 = val2 [,......] where id = ?  或者使用Mybatis提供的updateByPrimaryKey接口,

  当我们希望一次性更新大量数据时,每条SQL只能执行一条更新,MySQL支持另外一种更新方式,让我们可以实现通过一条SQL语句一次性更新多条 根据主键更新的记录:

  ON DUPLICATE KEY UPDATE

  注意:这不同于在满足where条件时将某个table中的某一字段全部更新为同一各值,而是每条记录修改后的值都不一样。

1.构建需要更新的数据,需要注意的是如果PO中存在不想要更新的字段,你有两种处理方式,一种是将该字段的值在此处设为数据库中原来的值,另一种方式就是在进行Map转换时,

不将此字段构建到Map中,这只需要对convertBean2MapWithUnderscoreName方法做一些小的修改,你可以把不需要保留的字段作为参数传给它,然后在转换的时候过滤掉(主键不能省略)。

        //4.构建批量更新的数据
//用于存放转换后的对象的List
List<Map<String,Object>> updateItems = Lists.newArrayList();
for (SysPersonPO sysPersonPO : persons) {
sysPersonPO.setCode(sysPersonPO.getCode()+"updateTest");
Map<String,Object> updatetItem = BeanMapUtil.convertBean2MapWithUnderscoreName(sysPersonPO);
updateItems.add(updatetItem);
}

2.编写Mybatis映射文件fmsDataDsMapper.xml

  <!-- 根据主键批量更新某个字段,传入表名和需要更新的数据的集合 -->
<insert id="updateDatas" parameterType="map">
INSERT INTO ${table_name}
<foreach collection="fields" index="field" item="fieldVal" separator="," open="(" close=")">
${field}
</foreach>
VALUES
<foreach collection="list" index="index" item="record" separator="," >
<foreach collection="record" index="key" item="item" separator="," open="(" close=")">
#{item}
</foreach>
</foreach>
ON DUPLICATE KEY UPDATE
<foreach collection="fields" index="field" item="fieldVal" separator=",">
${field}=VALUES(${field})
</foreach>
</insert>

3.更新数据

//5.更新数据
updateDatas(updateItems,"sys_person"); private void updateDatas(List<Map<String,Object>> updateItems, String tableName){
if (!updateItems.isEmpty()) {
Map<String,Object> params = Maps.newHashMap();
       //这里将数据分成每1000条执行一次,可根据实际情况调整
int count = updateItems.size() / 1000;
int yu = updateItems.size() % 1000;
for (int i = 0; i <= count; i++) {
List<Map<String,Object>> subList = Lists.newArrayList();
if (i == count) {
if(yu != 0){
subList = updateItems.subList(i * 1000, 1000 * i + yu);
}else {
continue;
}
} else {
subList = updateItems.subList(i * 1000, 1000 * (i + 1));
}
params.put("table_name", tableName);
params.put("fields", subList.get(0));
params.put("list", subList);
sqlSessionTemplate.insert(dataDsNameSpace+".updateDatas", params);
}
}
}

MyBatis动态批量插入、更新Mysql数据库的通用实现方案的更多相关文章

  1. mybatis+oracle 批量插入,若数据库中有则做更新操作

    1.只批量插入: insert into WXPAY_ACCOUNT(id ,out_trade_no ,transaction_id)select SEQ_WXPAY_ACCOUNT.nextval ...

  2. mysql基础---->mybatis的批量插入(一)

    这里面记录一下使用mybatis处理mysql的批量插入的问题,测试有可能不准.只愿世间风景千般万般熙攘过后,字里行间,人我两忘,相对无言. mybatis的批量插入 我们的测试主体类是springb ...

  3. mybatis foreach批量插入数据:Oracle与MySQL区别

    mybatis foreach批量插入数据:Oracle与MySQL不同点: 主要不同点在于foreach标签内separator属性的设置问题: separator设置为","分 ...

  4. Mybatis 实现批量插入和批量删除源码实例

    Mybatis 实现批量插入数据和批量删除数据 学习内容: 准备工作 1.数据库新建表 2.新建 Maven 项目和设置编译版本及添加依赖 3.新建 db.properties 4.新建 mybati ...

  5. mybatis中批量插入的两种方式(高效插入)

    MyBatis简介 MyBatis是一个支持普通SQL查询,存储过程和高级映射的优秀持久层框架.MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及对结果集的检索封装.MyBatis可以使用 ...

  6. MyBatis原生批量插入的坑与解决方案!

    前面的文章咱们讲了 MyBatis 批量插入的 3 种方法:循环单次插入.MyBatis Plus 批量插入.MyBatis 原生批量插入,详情请点击<MyBatis 批量插入数据的 3 种方法 ...

  7. mybatis之批量插入

    一.导入功能优化 普通for循环,对于导入大量数据时非常耗时.可以通过Mybatis的批量插入功能提高效率.每批次导入的数据不能太多,否则会报错.通过测试发现,每批次200条为宜. 测试结果: 开启事 ...

  8. MySQL数据库的高可用方案总结

    高可用架构对于互联网服务基本是标配,无论是应用服务还是数据库服务都需要做到高可用.虽然互联网服务号称7*24小时不间断服务,但多多少少有一些时候服务不可用,比如某些时候网页打不开,百度不能搜索或者无法 ...

  9. mybatis中批量插入以及更新

    1:批量插入 批量插入就是在预编译的时候,将代码进行拼接,然后在数据库执行 <insert id="batchInsert" parameterType="java ...

随机推荐

  1. tmux使用教程

    1.安装 2.操作 如何操作快捷键呢? 比如新建一个窗口的命令是:ctrl+b+c 那么,先按住ctrl不放,接着按下b键,然后ctrl和b键都完全松开后,再立马按下c键. 3.使用命令行 tmux ...

  2. 配置android studio环境-Hello world

    运行hello world demo 运行D:\Program Files\Android\Android Studio\bin 选择创建一个项目 出现一系列的选择 如果没有出现下列问题,直接跳过 备 ...

  3. TZ_13_Hystix的熔断器

    1.作用:当服务繁忙时,如果服务出现异常,不是粗暴的直接报错,而是返回一个友好的提示,虽然拒绝了用户的访问,但是会返回一个结果. 熔断器的三种状态: Closed:关闭状态(断路器关闭),所有请求都正 ...

  4. input输入框的input事件和change事件

    input输入框的onchange事件,要在 input 失去焦点的时候才会触发: 在输入框内容变化的时候不会触发change,当鼠标在其他地方点一下才会触发: onchange 事件也可用于单选框与 ...

  5. 适配器模式--在NBA我需要翻译

     适配器模式:将一个类的接口转换成客户希望的另外一个接口.Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作. 在软件开发中,也就是系统的数据和行为都正确,但接口不符时,我们应 ...

  6. C# params 用法简介

    params 是C#的关键字, params主要是在声明方法时参数类型或者个数不确定时使用,关于params 参数数组,需掌握以下几点: 一.参数数组必须是一维数组 二.不允许将params修饰符与r ...

  7. IntelliJ IDEA 中如何查看一个类的所有继承关系(当前类的所有继承关系图)

    IntelliJ IDEA 中如何查看一个类的所有继承关系(当前类的所有继承关系图) .embody{ padding:10px 10px 10px; margin:0 -20px; border-b ...

  8. NOIP模拟17.9.21

    NOIP模拟17.9.21 3 58 145 201 161.5 样例输出21.6 数据规模及约定对于40% 的数据,N <= 20对于60% 的数据,N <= 1000对于100% 的数 ...

  9. Leetcode59. Spiral Matrix II螺旋矩阵2

    给定一个正整数 n,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵. 示例: 输入: 3 输出: [ [ 1, 2, 3 ], [ 8, 9, 4 ], [ 7, 6, ...

  10. Leetcode501.Find Mode in Binary Search Tree二叉搜索树中的众数

    给定一个有相同值的二叉搜索树(BST),找出 BST 中的所有众数(出现频率最高的元素). 假定 BST 有如下定义: 结点左子树中所含结点的值小于等于当前结点的值 结点右子树中所含结点的值大于等于当 ...