一、业务背景

  由于需要从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. 【python之路41】web框架

    一.web框架介绍 一共有两种web框架 1.既包含socket又能逻辑处理 tornado框架 2.只能处理逻辑 Django bottle flask  二.web框架的本质 众所周知,对于所有的 ...

  2. Linux报错:“/bin/bash^M: 坏的解释器

    原因是linux下的文件,在windows下面编辑过.在win下编辑的时候,换行结尾是\n\r , 而在linux下 是\n 解决方法,将文件里面的内容做替换即可 sed -i 's/\r$//' b ...

  3. OCR Tesseract 识别报 empty page解决办法

    图片分辨率太低导致 周边加空白 然后重新操作,就行了

  4. Twitter web information

    http://developer.51cto.com/art/201307/404612.htm 150M active users 300K Qps (read, only 6000 write/s ...

  5. js点击复制文本

    // 动态创建 input 元素 var aux = document.createElement("input"); // 获得需要复制的内容 aux.setAttribute( ...

  6. 2019-3-1-WPF-从零开始开发-dotnet-Remoting-程序

    title author date CreateTime categories WPF 从零开始开发 dotnet Remoting 程序 lindexi 2019-03-01 09:30:45 +0 ...

  7. linux性能监视工具sar

    sar是一个优秀的一般性能监视工具,它可以输出Linux所完成的几乎所有工作的数据.sar命令在sysetat rpm中提供.示例中使用sysstat版本5.0.5,这是稳定的最新版本之一.关于版本和 ...

  8. Uva11384 Help is needed for Dexter

    Dexter is tired of Dee Dee. So he decided to keep Dee Dee busy in a game. The game he planned for he ...

  9. SQL Sever实验三 视图与数据更新

    一. 实验目的 1.掌握创建视图的 SQL 语句,数据更新的 SQL 语句. 2.了解使用创建视图向导创建视图的方法. 3.掌握使用 SQL 创建视图的方法,使用 SQL 更新数据的方法. 二. 实验 ...

  10. js里面的this指向

    1.  this是动态绑定的,或者说是在代码运行期绑定而不是在书写期 function fire () { console.log(this.a) } var obj = { a: 1, fire: ...