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表读写数据。