一、业务背景

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

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

二、批量插入的具体实现

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

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

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

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

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

  

  1. BeanMapUtil类,POMap的转换方法,基于类反射技术:
  1. import com.fms.common.utils.other.StringUtil;
  2.  
  3. import java.beans.BeanInfo;
  4. import java.beans.Introspector;
  5. import java.beans.MethodDescriptor;
  6. import java.beans.PropertyDescriptor;
  7. import java.lang.reflect.Method;
  8. import java.sql.Timestamp;
  9. import java.util.Date;
  10. import java.util.HashMap;
  11. import java.util.Map;
  12.  
  13. import org.apache.commons.beanutils.BeanUtils;
  14.  
  15. public class BeanMapUtil {
  16.  
  17. @SuppressWarnings({"unchecked", "rawtypes"})
  18. public static Map convertBean2MapWithUnderscoreName(Object bean) throws Exception {
  19. Map returnMap = null;
  20. try {
  21. Class type = bean.getClass();
  22. returnMap = new HashMap();
  23. BeanInfo beanInfo = Introspector.getBeanInfo(type);
  24. PropertyDescriptor[] propertyDescriptors = beanInfo
  25. .getPropertyDescriptors();
  26. for (int i = 0; i < propertyDescriptors.length; i++) {
  27. PropertyDescriptor descriptor = propertyDescriptors[i];
  28. String propertyName = descriptor.getName();
  29. if (!propertyName.equalsIgnoreCase("class")) {
  30. Method readMethod = descriptor.getReadMethod();
  31. Object result = readMethod.invoke(bean, new Object[0]);
  32. returnMap.put( StringUtil.underscoreName(propertyName), result);
  33. }
  34. }
  35. } catch (Exception e) {
  36. // 解析错误时抛出服务器异常 记录日志
  37. throw new Exception("从bean转换为map时异常!", e);
  38. }
  39. return returnMap;
  40. }
  41.  
  42. }

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

  1. public class StringUtil {
  2.  
  3. /**
  4. * 将驼峰式命名的字符串转换为下划线大写方式。如果转换前的驼峰式命名的字符串为空,则返回空字符串。</br>
  5. * 例如:HelloWorld->HELLO_WORLD
  6. *
  7. * @param name 转换前的驼峰式命名的字符串
  8. * @return 转换后下划线大写方式命名的字符串
  9. */
  10. public static String underscoreName(String name) {
  11. StringBuilder result = new StringBuilder();
  12. if (name != null && name.length() > 0) {
  13. // 将第一个字符处理成大写
  14. result.append(name.substring(0, 1).toUpperCase());
  15. // 循环处理其余字符
  16. for (int i = 1; i < name.length(); i++) {
  17. String s = name.substring(i, i + 1);
  18. // 在大写字母前添加下划线
  19. if (s.equals(s.toUpperCase()) && !Character.isDigit(s.charAt(0))) {
  20. result.append("_");
  21. }
  22. // 其他字符直接转成大写
  23. result.append(s.toUpperCase());
  24. }
  25. }
  26. return result.toString();
  27. }
  28. }

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

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

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

  1. @Autowired
  2. SysPersonPOMapper sysPersonPOMapper;
  3.  
  4. public void dataDs() throws Exception{
  5. //1.查询需要同步的数据
  6. //根据具体情况可以创建查询条件或者采用自定义的Mapper进行查询
  7. SysPersonPOExample sysPersonPOExample = new SysPersonPOExample();
  8. sysPersonPOExample.createCriteria().andIsDeleteEqualTo(false);
  9. //查询需要同步的数据
  10. List<SysPersonPO> persons = sysPersonPOMapper.selectByExample(sysPersonPOExample);
  11.  
  12. //2.将不能进行遍历的PO实体对象转为Map
  13.  
  14. //用于存放转换后的对象的List
  15. List<Map<String,Object>> insertItems = Lists.newArrayList();
  16. for (SysPersonPO sysPersonPO : persons) {
  17. Map<String,Object> insertItem = BeanMapUtil.convertBean2MapWithUnderscoreName(sysPersonPO);
  18. insertItems.add(insertItem);
  19. }
  20.  
  21. //3.插入数据
  22. insertDatas(insertItems,"sys_person");
  23. }
  24.  
  25. @Autowired
  26. SqlSessionTemplate sqlSessionTemplate;
  27. private String dataDsNameSpace = "com.fms.common.dao.fmsDataDsMapper";
  28. private void insertDatas(List<Map<String,Object>> insertItems, String tableName){
  29. if (!insertItems.isEmpty()) {
  30. Map<String,Object> params = Maps.newHashMap();
           //这里把数据分成每1000条执行一次,可根据实际情况进行调整
  31. int count = insertItems.size() / 1000;
  32. int yu = insertItems.size() % 1000;
  33. for (int i = 0; i <= count; i++) {
  34. List<Map<String,Object>> subList = Lists.newArrayList();
  35. if (i == count) {
  36. if(yu != 0){
  37. subList = insertItems.subList(i * 1000, 1000 * i + yu);
  38. }else {
  39. continue;
  40. }
  41. } else {
  42. subList = insertItems.subList(i * 1000, 1000 * (i + 1));
  43. }
  44. params.put("table_name", tableName);
  45. params.put("fields", subList.get(0));
  46. params.put("list", subList);
  47. sqlSessionTemplate.insert(dataDsNameSpace+".insertDatas", params);
  48. }
  49. }
  50. }

三 、批量更新的具体实现

  通常我们根据主键更新时的语句是 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方法做一些小的修改,你可以把不需要保留的字段作为参数传给它,然后在转换的时候过滤掉(主键不能省略)。

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

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

  1.   <!-- 根据主键批量更新某个字段,传入表名和需要更新的数据的集合 -->
  2. <insert id="updateDatas" parameterType="map">
  3. INSERT INTO ${table_name}
  4. <foreach collection="fields" index="field" item="fieldVal" separator="," open="(" close=")">
  5. ${field}
  6. </foreach>
  7. VALUES
  8. <foreach collection="list" index="index" item="record" separator="," >
  9. <foreach collection="record" index="key" item="item" separator="," open="(" close=")">
  10. #{item}
  11. </foreach>
  12. </foreach>
  13. ON DUPLICATE KEY UPDATE
  14. <foreach collection="fields" index="field" item="fieldVal" separator=",">
  15. ${field}=VALUES(${field})
  16. </foreach>
  17. </insert>

3.更新数据

  1. //5.更新数据
  2. updateDatas(updateItems,"sys_person");
  3.  
  4. private void updateDatas(List<Map<String,Object>> updateItems, String tableName){
  5. if (!updateItems.isEmpty()) {
  6. Map<String,Object> params = Maps.newHashMap();
           //这里将数据分成每1000条执行一次,可根据实际情况调整
  7. int count = updateItems.size() / 1000;
  8. int yu = updateItems.size() % 1000;
  9. for (int i = 0; i <= count; i++) {
  10. List<Map<String,Object>> subList = Lists.newArrayList();
  11. if (i == count) {
  12. if(yu != 0){
  13. subList = updateItems.subList(i * 1000, 1000 * i + yu);
  14. }else {
  15. continue;
  16. }
  17. } else {
  18. subList = updateItems.subList(i * 1000, 1000 * (i + 1));
  19. }
  20. params.put("table_name", tableName);
  21. params.put("fields", subList.get(0));
  22. params.put("list", subList);
  23. sqlSessionTemplate.insert(dataDsNameSpace+".updateDatas", params);
  24. }
  25. }
  26. }

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. MySQL事务、锁机制、查询缓存

    MySQL事务 何为事务? 事务(Transaction)是访问并可能更新数据库中各种数据项的一个程序执行单元(unit). 一个事务可以是一条SQL语句,一组SQL语句或整个程序. 事务的特性: 事 ...

  2. 安装office2016时弹出microsoft setup bootstrapper已停止工作的解决办法

    安装office2016时安装进度条走到最后又回滚,弹出microsoft setup bootstrapper已停止工作,最后“安装出错” 经过了1天的试尽了各种控制面板卸载.文件夹删除.offic ...

  3. 【python之路32】python异常处理

    一.捕获异常 1.try  except #!usr/bin/env python # -*- coding:utf-8 -*- num = input("请输入一个数字:") t ...

  4. LUOGU P4171 [JSOI2010]满汉全席

    传送门 解题思路 2-SAT 裸题. 代码 #include<iostream> #include<cstdio> #include<cstring> #inclu ...

  5. loj2322 「清华集训 2017」Hello world!

    https://loj.ac/problem/2322 先吐槽一下,sb数据毁我青春败我前程. 首先,一个数开根开不了多少次. 当我们把它开到1的时候,我们以后就不需要开他了,我们可以利用并查集跳过他 ...

  6. JavaScript实现继承的方式和各自的优缺点

    ECMAscript只支持实现继承,主要是依靠原型链来实现的. JavaScript实现继承的方式: 类式继承 构造函数继承 组合继承 寄生组合式继承 1.类式继承 //类式继承 //声明父类 fun ...

  7. 关系数据库(ch.2)

    2.1.1 关系 域 笛卡儿积 关系 candiate key 如果一组属性值可以唯一的标识一个元祖,但是他的子集不行,那么这是一个候选码 关系可以由三种类型 基本关系 查询关系 视图 为关系附加如下 ...

  8. OpenSmtp 发送邮件

    1.采用发送一个简单邮件 示例: private int smtpPort; private string smtpHost; private int recieveTimeout; private ...

  9. Laravel 安装mysql、表增加模拟数据、生成控制器

    参考中文网教程: 安装mysql.表增加模拟数据 http://www.golaravel.com/post/2016-ban-laravel-xi-lie-ru-men-jiao-cheng-yi/ ...

  10. Spring_Hibernate

    Spring与Hiberante整合 通过hibernate的学习,我们知道,hibernate主要在hibernate.cfg.xml配置文件中 接下来我们看一下hibernate的一个配置文件 h ...