项目中迁移的需求用到了批量新增,批量更新来做迁移的需求,为了方便以后的迁移,这里写了一套完整的方法来实现insert和update
首先因为是老项目的表,索引列都层次不齐,因此还是要手动列出相关索引的表,建立arr方便之后遍历
这里传入tableArr,后面三个参数是根据业务需求制定的,我这里是要实现补录startTime和endTime期间旧表内的数据,queryType指的是索引类型,我这里的话queryType设置如下
注意这里新增和更新的两个方法的一些细微不同: 批量新增会remove掉map中的id,为了不起自增id冲突
private void migrationCreateTable(String[] tableArr, String startTime, String endTime, int queryType) {
for (int i = 0; i < tableArr.length; i++) {
List<Map<String, Object>> list = dataFromService.getListMap(tableArr[i], new HashMap<>(), startTime, endTime, queryType);
Map<String, Object> fieldsMap;
if (list.size() > 0) {
for (Map<String, Object> e :
list) {
e.remove("id");
}
fieldsMap = list.get(0);
dataToService.pageInsertDataByTableName(tableArr[i], humpToUnderline(fieldsMap), list);
}
}
}
批量更新会做驼峰转下划线,为的是和mysql表中的字段一一对应
private void migrationUpdateTable(String[] tableArr, String startTime, String endTime, int queryType) {
for (int i = 0; i < tableArr.length; i++) {
List<Map<String, Object>> list = dataFromService.getListMap(tableArr[i], new HashMap<>(), startTime, endTime, queryType);
Map<String, Object> fieldsMap;
if (list.size() > 0) {
for (Map<String, Object> e:
list) {
humpToUnderline(e);
}
fieldsMap = list.get(0);
dataToService.pageUpdateDataByTableName(tableArr[i], humpToUnderline(fieldsMap), list);
}
}
}
如果mybatis-config没有配置,可以手动使用如下驼峰转下划线的方法(注意这里一定要用LinkedHashMap,之前用HashMap做插入SQL报错了,其实很好理解,就是字段没有一一对应,所以要确保字段顺序一致性)StrUtil用的是hutool
private LinkedHashMap<String, Object> humpToUnderline(Map<String, Object> map) {
LinkedHashMap<String, Object> transitionMap = new LinkedHashMap<>(16);
map.forEach((k, v) -> transitionMap.put(StrUtil.toUnderlineCase(k), v));
return transitionMap;
}
其实就是走dataFromService和dataToService的两个方法,分别做查旧表和批量插入/更新新表
1.dataFromService,分装成map后进入相应的Dao方法,@TargetDataSource是我自定义切换数据源的注解,看我上一篇博客即可
@TargetDataSource(connName = "dbTwo")
public List<Map<String, Object>> getListMap(String tableName, Map<String, Object> fieldsMap, String startTime,String endTime,int queryType){
Map<String, Object> map = new HashMap<>();
map.put("tableName", tableName);
map.put("fieldsMap", fieldsMap);
map.put("startTime", startTime);
map.put("endTime", endTime);
if (queryType==0){
return dataMigrationDao.queryByCreateDateList(map);
}else if (queryType==1){
return dataMigrationDao.queryByCreateTimeList(map);
}else if (queryType==3){
return dataMigrationDao.queryByModifyDateList(map);
}else if (queryType==4){
return dataMigrationDao.queryByUpdateTimeList(map);
}
return new ArrayList<>();
}
随便选一个进去,sql很简单没啥好说的,数据量大的话要分页
List<Map<String, Object>> queryByCreateDateList(@Param("map") Map<String,Object> map);
<select id="queryByCreateDateList" resultType="java.util.Map">
SELECT *
from ${map.tableName} where create_date > #{map.startTime} and create_date < #{map.endTime}
</select>
2.dataToService,重点来了,一共两个方法:pageInsertDataByTableName和pageUpdateDataByTableName,且都是分页操作
批量插入
@Transactional
public void pageInsertDataByTableName(String tableName, Map<String, Object> fieldsMap, List<Map<String, Object>> mapList) {
for (int i = 0, j = 0, amount = 1000; i < mapList.size(); i += amount) {
j = i + amount;
if (j < mapList.size()) {
insertBatchList(tableName, fieldsMap, mapList.subList(i, mapList.size()));
} else {
insertBatchList(tableName, fieldsMap, mapList.subList(i, mapList.size()));
break;
}
}
}
private void insertBatchList(String tableName, Map<String, Object> fieldsMap, List<Map<String, Object>> list) {
Map<String, Object> map = new HashMap<>();
map.put("tableName", tableName);
map.put("fieldsMap", fieldsMap);
map.put("list", list);
dataMigrationDao.insertBatchTableListDynamic(map);
}
然后是dao,按照我的写法即可实现批量插入,一定要注意字段顺序和count一致,不然会报sql语法错误
int insertBatchTableListDynamic(@Param("map") Map<String,Object> map);
<insert id="insertBatchTableListDynamic" parameterType="java.util.HashMap">
insert into
${map.tableName}
(
<foreach collection="map.fieldsMap" index="key" item="value"
separator=",">
`${key}`
</foreach>
)
values
<foreach collection="map.list" item="line" separator=",">
(
<foreach collection="line" index="key" item="value"
separator=",">
#{value}
</foreach>
)
</foreach>
</insert>
批量更新同理
@Transactional
public void pageUpdateDataByTableName(String tableName, Map<String, Object> fieldsMap, List<Map<String, Object>> mapList) {
for (int i = 0, j = 0, amount = 1000; i < mapList.size(); i += amount) {
j = i + amount;
if (j < mapList.size()) {
updateBatchList(tableName, fieldsMap, mapList.subList(i, mapList.size()));
} else {
updateBatchList(tableName, fieldsMap, mapList.subList(i, mapList.size()));
break;
}
}
}
private void updateBatchList(String tableName, Map<String, Object> fieldsMap, List<Map<String, Object>> list) {
Map<String, Object> map = new HashMap<>();
map.put("tableName", tableName);
map.put("fieldsMap", fieldsMap);
map.put("list", list);
dataMigrationDao.updateBatchTableListDynamic(map);
}
dao如下,按找我的写法即可,<choose> 标签根据实际需求修改
int updateBatchTableListDynamic(@Param("map") Map<String,Object> map);
<update id="updateBatchTableListDynamic" parameterType="java.util.HashMap">
<foreach collection="map.list" item="line" index="index" open="" close="" separator=";">
update ${map.tableName}
<trim prefix="set" suffixOverrides=",">
<foreach collection="line.entrySet()" item="value" index="key" separator=",">
<choose>
<when test="key != 'complaint_date' and key != 'close_date' and key != 'create_date' and key != 'modify_date' and key != 'handle_date'
and key != 'create_time' and key != 'update_time'">
<if test="value != null and value !=''">
${key}= #{value}
</if>
</when>
<otherwise>
<if test="value!= null ">
${key}= #{value}
</if>
</otherwise>
</choose>
</foreach>
</trim>
where id = #{line.id}
</foreach>
</update>
这里遇到了不少坑,这里做了下整理
批量update一直报语法错误,解决办法如下: mybatis批量更新报错
还有时间问题,datetime类型的字段是不能<if> 标签里直接!='' 的 mybatis invalid comparison: java.sql.Timestamp and java.lang.String
我就比较直接所有进行比较了,不清楚mybatis是否可以判断该字段是否是datetime的语法,如果有更好的办法可以改进
其他问题: 解决mybatis #{}无法自动添加引号的错误 mybatis传入List实现批量更新的坑
|