java同步mysql数据
同步表数据和修改java程序的过程,将这些记录下来:
- 业务说明
- 解决方案
- 表信息
- 同步程序
- 遇到的问题
业务说明
由于业务变更,系统内涉及到使用aid的程序都要求替换为bid,一共涉及多张表的数据需要清洗,下面以其中一张表rule来讲述。
解决方案
1.创建相同表结构rules
2.同步表rule到表rules
3.将应用程序中使用表rule替换为表rules
表信息
rule 表结构如下:
字段 | 说明 |
id | 主键,自动增长列 |
rule_id | 规则编号 |
create_id | 创建人Id |
create_time | 创建时间 |
update_id | 更新人Id |
update_time | 更新时间 |
rule_id字段存的值需要替换,有aid替换为bid,具体数据如下:
aid | bid |
201409C2000000003 | KS1201506CG230000049 |
201311CM010000171 | KS1201506CG230000050 |
创建表rules
为了不影响线上环境,创建了表rules,结构同表rule,好处在于清理数据时不影响线上正常业务数据。
rules 表结构如下:
字段 | 说明 |
id | 主键,自动增长列 |
rule_id | 规则编号 |
create_id | 创建人Id |
create_time | 创建时间 |
update_id | 更新人Id |
update_time | 更新时间 |
同步程序
业务逻辑如下:
* 1.同步数据,一次读取表rule的1000条记录,且替换aid为bid,写入表rules
* 2.判断新增or更新
* 3.如果rule.id在rules表不存在,则insert
* 4.如果rule.id在rules表存在,且rules.update_time<>rules.update_time,则update
程序如下:
public void synchronizeCouponRulesDddc(){
logger.info("synchronizeCouponRulesDddc---开始");
Map<String, Object> param = new HashMap<String, Object>();
Map<String, Object> param2 = new HashMap<String, Object>();
int total = couponOldForNewMapper.synchronizeCouponRulesCount();
if (total > 0) {
int startIndex = 0, batchSize = 1000;
while (startIndex < total) {
param.put("startIndex", startIndex);
param.put("batchSize", batchSize);
List<com.feiniu.coupon.entity.newCoupon.CouponRule> couponRulesList = couponOldForNewMapper.synchronizeCouponRules(param);
Set<Integer> idsSet = new HashSet<Integer>();
//插入数据导关联表
if (CollectionUtils.isNotEmpty(couponRulesList)) {
for (com.feiniu.coupon.entity.newCoupon.CouponRule couponRules : couponRulesList) {
String type = couponRules.getType();
if ("3".equals(type) || "4".equals(type) || "12".equals(type) || "13".equals(type)) {
String typeSeq = couponRules.getTypeSeq();
couponRules.setTypeSeq(this.replaceSmId(typeSeq));
}
idsSet.add(couponRules.getId());
}
}
String idsStr ="'"+ StringUtils.join(idsSet, "','") +"'";
param2.put("ids", idsStr);
List<com.feiniu.coupon.entity.newCoupon.CouponRule> couponRulesDddcList = couponOldForNewMapper.synchronizeCouponRulesDddc(param2);
List<com.feiniu.coupon.entity.newCoupon.CouponRule> updateList = new ArrayList<com.feiniu.coupon.entity.newCoupon.CouponRule>();
if (CollectionUtils.isNotEmpty(couponRulesDddcList)) {
for (com.feiniu.coupon.entity.newCoupon.CouponRule couponRules : couponRulesList) {
for (com.feiniu.coupon.entity.newCoupon.CouponRule couponRulesDddc : couponRulesDddcList) {
if (couponRules.getId().equals(couponRulesDddc.getId())
&& couponRules.getUpdateTime().getTime() != couponRulesDddc.getUpdateTime().getTime()) {
updateList.add(couponRules);
}
}
}
}
List<com.feiniu.coupon.entity.newCoupon.CouponRule> insertList = new ArrayList<com.feiniu.coupon.entity.newCoupon.CouponRule>();;
if (CollectionUtils.isNotEmpty(couponRulesDddcList)) {
Set<Integer> dddcSet = new HashSet<Integer>();
for (com.feiniu.coupon.entity.newCoupon.CouponRule couponRulesDddc : couponRulesDddcList) {
dddcSet.add(couponRulesDddc.getId());
}
for (com.feiniu.coupon.entity.newCoupon.CouponRule couponRules : couponRulesList) {
if (!dddcSet.contains(couponRules.getId())) {
insertList.add(couponRules);
}
}
} else {
insertList = couponRulesList;
}
if (CollectionUtils.isNotEmpty(insertList)) {
for (com.feiniu.coupon.entity.newCoupon.CouponRule insertCouponRules : insertList) {
couponOldForNewMapper.insertCouponRulesDddc(insertCouponRules);
}
}
if (CollectionUtils.isNotEmpty(updateList)) {
for (com.feiniu.coupon.entity.newCoupon.CouponRule updateCouponRules : updateList) {
couponOldForNewMapper.updateCouponRulesDddc(updateCouponRules);
}
}
startIndex += batchSize;
logger.info("synchronizeCouponData.synchronizeCouponRulesDddc---"+startIndex);
}
}
}
遇到的问题
1.原因是数据库里面有char类型的字段其内容为空格
Caused by: java.lang.StringIndexOutOfBoundsException: String index out of range: 0
at java.lang.String.charAt(String.java:658)
at org.apache.ibatis.type.CharacterTypeHandler.getNullableResult(CharacterTypeHandler.java:37)
at org.apache.ibatis.type.CharacterTypeHandler.getNullableResult(CharacterTypeHandler.java:26)
at org.apache.ibatis.type.BaseTypeHandler.getResult(BaseTypeHandler.java:55)
at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.getPropertyMappingValue(DefaultResultSetHandler.java:393)
at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.applyPropertyMappings(DefaultResultSetHandler.java:367)
at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.getRowValue(DefaultResultSetHandler.java:341)
at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.handleRowValuesForSimpleResultMap(DefaultResultSetHandler.java:294)
at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.handleRowValues(DefaultResultSetHandler.java:269)
at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.handleResultSet(DefaultResultSetHandler.java:239)
at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.handleResultSets(DefaultResultSetHandler.java:153)
at org.apache.ibatis.executor.statement.PreparedStatementHandler.query(PreparedStatementHandler.java:60)
at org.apache.ibatis.executor.statement.RoutingStatementHandler.query(RoutingStatementHandler.java:73)
2.mybaitis批量insert使用$ 符号
字段存放的值包含单引号和’$’前后的单引号冲突导致程序报错,使用#可以避免问题。
<insert id="insertBatchCouponRulesDddc" useGeneratedKeys="true" parameterType="java.util.List">
insert into coupon.coupon_rules_dddc(id,
coupon_id,
type,
type_seq,
type_seq_name,
is_use,
create_id,
create_time,
update_id,
update_time)
values
<foreach collection="list" item="item" index="index" separator="," >
'${item.id}',
'${item.couponId}',
'${item.type}',
'${item.typeSeq}',
'${item.typeSeqName}',
'${item.isUse}',
'${item.createId}',
'${item.createTime}',
'${item.updateId}',
'${item.updateTime}')
</foreach>
</insert>
3.重点关注rules.id自动增长列,存在情况如下:
1)线上rule.id从1增长到了1000000;
2)这时候同步数据rule到rules,rules.id数据为1到1000000,创建自动增长列从1000000开始;
3)部署应用java程序,在此期间rule新增20000数据,rule.id增长到1020000;
4)部署应用程序后读写rules表,此时要自动同步rule新增的20000记录,这样会造成rules.id冲突,造成同步rule的数据会和新增rules表数据争用。
5)这里我们为了保险起见,在设置rules.id从1500000开始增长,这样即使rule有数据增加,也不会影响rules表读写数据。