名 | 类型 | 不是null | 主键 | 备注 |
---|---|---|---|---|
id | int | √ | √ | 主键id |
start_id | int | 被同步表数据,开始id | ||
end_id | int | 被同步表数据,结束id | ||
end_upate_time | timestamp | 同步结束时的时间(被同步表最后一条同步数据创建时间) | ||
sync_type | varchar | 同步类型 | ||
create_time | timestamp | √ | 创建时间 |
CREATE TABLE `sync_record` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键id',
`start_id` int(11) DEFAULT NULL COMMENT '被同步表数据,开始id',
`end_id` int(11) DEFAULT NULL COMMENT '被同步表数据,结束id',
`end_upate_time` timestamp(4) NULL DEFAULT NULL COMMENT '同步结束时的时间(被同步表最后一条同步数据创建时间)',
`sync_type` varchar(3) DEFAULT NULL COMMENT '同步类型',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 COMMENT='同步记录表';
Java抽象类
AbstractSyncByIdService.java(抽象类)
@Service public abstract class AbstractSyncByIdService { private static final Logger logger = LoggerFactory.getLogger(AbstractSyncByIdService.class); @Autowired private SyncDao syncDao; /** * 获取同步的上一个id * * @author HeLiu * @date 2018/7/18 11:20 */ public Integer queryPreviousId(String syncType) { return syncDao.queryPreviousId(syncType); } /** * 异常或者结束时,保存或者更新本次的同步记录 * * @author HeLiu * @date 2018/7/18 11:39 */ protected void saveOrUpdateSyncRecord(Integer startId, Integer endId, String syncType) { boolean exsitFlag = syncDao.queryExsitBySyncType(syncType); //如果存在该同步类型的 同步记录,则只更新endId ; 不存在,则插入该类型的同步记录 if (exsitFlag) { syncDao.updateEndIdBySyncType(syncType, endId); } else { syncDao.saveSyncRecord(syncType, startId, endId); } } /** * 执行同步,同步中的业务逻辑,数据之间的同步先后关系,都在这里编写 * * @author HeLiu * @date 2018/7/18 11:36 */ public void excuteSync(String syncType, String pcId) { logger.info(".......start .excuteSync ..syncType:{}..", EnumSyncType.enumOfByCode(syncType).desc); // 获取开始id Integer previousId = queryPreviousId(syncType); // 每次都会执行方法,判断是否有需要同步的数据 Pair
resultPair = exsitNeedSync(previousId, pcId); while (resultPair.getLeft()) { // 设置最已同步的id 为前一个id Integer syncEndId = previousId; try { // 同步数据,并返回结束时,本次同步数据的id, // 没有异常,则表示同步成功 syncEndId = syncData(resultPair.getRight()); logger.info(".......同步数据id:{}.. .excuteSync ..syncType:{}..", syncEndId, syncType); // 同步成功 最新同步成功的id , 则变成上一个id previousId = syncEndId; resultPair = exsitNeedSync(previousId, pcId); } catch (Exception e) { logger.info(".excuteSync..excetption..previousId:{}...syncType.:{}", previousId, syncType); logger.error("excuteSync..excetption.", e); } finally { // 保存同步记录, // 每次同步成功一条数据,都需要更新最新已同步的id logger.info("..saveOrUpdateSyncRecord..........."); saveOrUpdateSyncRecord(previousId, syncEndId, syncType); } } logger.info(".......end .excuteSync ..syncType:{}..", EnumSyncType.enumOfByCode(syncType).desc); } /** * 根据同步开始id,同步数据, 返回结束时的id, 不同模块,实现不一样,这里抽象出来 * * @author HeLiu * @date 2018/7/18 11:32 */ protected abstract Integer syncData(Object data) throws Exception; /** * 根据同步id ,查询是否有需要同步的数据,true 表示有, false 表示没有 * * @author HeLiu * @date 2018/7/18 16:21 */ public abstract Pair exsitNeedSync(Integer previousId, String pcId); }
AbstractSyncByTimeService.java(抽象类)
@Service public abstract class AbstractSyncByTimeService { private static final Logger logger = LoggerFactory.getLogger(AbstractSyncByTimeService.class); @Autowired private SyncDao syncDao; /** * 获取最后一次的更新时间 * * @author HeLiu * @date 2018/7/18 11:20 */ public String queryPreviousEndUpdateTime(String syncType) { return syncDao.queryPreviousEndUpdateTime(syncType); } /** * 异常或者结束时,保存或者更新本次的同步记录 * * @author HeLiu * @date 2018/7/18 11:39 */ protected void saveOrUpdateSyncRecord(String endUpdateTime, String syncType) { boolean exsitFlag = syncDao.queryExsitBySyncType(syncType); // 如果存在该同步类型的 同步记录,则只更新同步数据的创建时间; 不存在,则插入该类型的同步记录 if (exsitFlag) { syncDao.updateEndUpdateTimeBySyncType(syncType, endUpdateTime); } else { syncDao.saveEndUpdateTimeBySyncType(syncType, endUpdateTime); } } /** * 执行同步,同步中的业务逻辑,数据之间的同步先后关系,都在这里编写 * * @author HeLiu * @date 2018/7/18 11:36 */ public void excuteSync(String syncType, String pcId) { logger.info(".......start .excuteSync ..syncType:{}..", EnumSyncType.enumOfByCode(syncType).desc); // 获取开始同步时间 String endUpdateTime = queryPreviousEndUpdateTime(syncType); // 每次都会执行方法,判断是否有需要同步的数据 Pair
resultPair = exsitNeedSync(endUpdateTime, pcId); while (resultPair.getLeft()) { // 设置已同步的时间 为前一个时间 String syncEndUpdateTime = endUpdateTime; try { // 同步数据,并返回结束时,本次同步数据的创建时间, // 没有异常,则表示同步成功 syncEndUpdateTime = syncData(resultPair.getRight()); logger.info(".......同步数据endUpdateTime:{}.. .excuteSync ..syncType:{}..", syncEndUpdateTime, syncType); // 同步成功 最新同步成功的创建时间 , 则变成上一个创建时间 endUpdateTime = syncEndUpdateTime; resultPair = exsitNeedSync(endUpdateTime, pcId); } catch (Exception e) { logger.info(".excuteSync..excetption..previousId:{}...syncType.:{}", endUpdateTime, EnumSyncType.enumOfByCode(syncType).desc); logger.error("excuteSync..excetption.", e); } finally { // 保存同步记录, // 每次同步成功一条数据,都需要更新最新已同步的创建时间 saveOrUpdateSyncRecord(endUpdateTime, syncType); } } logger.info(".......end .excuteSync ..syncType:{}..", EnumSyncType.enumOfByCode(syncType).desc); } /** * 根据同步开始时间,同步数据, 返回结束时的时间, 不同模块,实现不一样,这里抽象出来 * * @author HeLiu * @date 2018/7/18 11:32 */ protected abstract String syncData(Object data) throws Exception; /** * 根据同步开始时间 ,查询是否有需要同步的数据,true 表示有, false 表示没有 * * @author HeLiu * @date 2018/7/18 16:21 */ public abstract Pair exsitNeedSync(String endUpdateTime, String pcId); }
注意:
- 两者同步逻辑都是一样的一个根据主键id,前提是你的主键id是数字递增类型的不是UUID之类的,另一个根据数据的创建时间,利用时间有先后的原理。这二者同步的区别要区分好。
- 根据你同步数据设置好区分的类别也就是syncType,例如:人员-'1';视频-'2'......,怎么开心怎么来。
- 然后编写你自己的同步数据逻辑层一定要继承该类(AbstractSyncByIdService / AbstractSyncByTimeService),重写抽象类里面的方法,自定义你自己的业务代码,因为不同的同步数据,业务的代码不一样。
- 这两个抽象类一定要仔细看,有详细的注解。
- 两个抽象方法至关重要,一定要理解这两个方法的用处。
SyncDao.java
@Repository public class SyncDao { private static final String name_space = "syncRecord" + SPOT; @Autowired private DaoClient daoClient; /** * 根据同步类型,查询出,原数据表中,开始同步的id * @date 2018/7/18 14:18 */ public Integer queryPreviousId(String syncType){ String sqlId = name_space + "queryPreviousId"; Map
param = new HashMap<>(); param.put("syncType", syncType); return daoClient.queryForObject(sqlId, param, Integer.class); } /** * 判断该种类型的同步信息是否存在 * @author liuao * @date 2018/7/18 15:16 */ public boolean queryExsitBySyncType(String syncType){ String sqlId = name_space + "queryExsitBySyncType"; Map param = new HashMap<>(); param.put("syncType", syncType); int count = daoClient.queryForObject(sqlId, param, Integer.class); return count > 0 ? true : false ; } /** * 根据同步类型更新同步结束时的id * @author liuao * @date 2018/7/18 15:24 */ public int updateEndIdBySyncType(String syncType, Integer endId){ String sqlId = name_space + "updateEndIdBySyncType"; Map param = new HashMap<>(); param.put("syncType", syncType); param.put("endId", endId); return daoClient.excute(sqlId, param); } /** * 根据同步类型更新同步结束时的id * @author liuao * @date 2018/7/18 15:24 */ public int updateEndUpdateTimeBySyncType(String syncType, String endUpdateTime){ String sqlId = name_space + "updateEndUpdateTimeBySyncType"; Map param = new HashMap<>(); param.put("syncType", syncType); param.put("endUpdateTime", endUpdateTime); return daoClient.excute(sqlId, param); } /** * 根据同步类型保存同步结束时的更新时间 * @author liuao * @date 2018/7/18 15:24 */ public int saveEndUpdateTimeBySyncType(String syncType, String endUpdateTime){ String sqlId = name_space + "saveEndUpdateTimeBySyncType"; Map param = new HashMap<>(); param.put("syncType", syncType); param.put("endUpdateTime", endUpdateTime); return daoClient.insertAndGetId(sqlId, param); } /** * 保存同步记录 * @date 2018/7/18 15:28 */ public int saveSyncRecord(String syncType, Integer startId ,Integer endId){ String sqlId = name_space + "saveSyncRecord"; Map param = new HashMap<>(); param.put("syncType", syncType); param.put("startId", startId); param.put("endId", endId); return daoClient.excute(sqlId, param); } /** * 查询出最后一次的更新时间 * @date 2018/8/2 19:48 */ public String queryPreviousEndUpdateTime(String syncType) { String sqlId = name_space + "queryPreviousEndUpdateTime"; Map param = new HashMap<>(); param.put("syncType", syncType); return daoClient.queryForObject(sqlId, param, String.class); } } sql语句:
注意:代码是死的人是活的,灵活使用,不要被代码局限了,这个只是提供一下思路,具体怎么使用可以自己根据实际需求改和优化,深刻理解设计思路和对抽象类的一个灵活使用。
这里的Dao层和sql的写法是jdbctemplate的封装,可以借鉴我的一篇博客——Java基于jdbctemplate数据持久层操作封装
刚开始肯定有点难理解,耐下心仔细看,欢迎相互讨论——QQ:892715310,WX:Miss5202468。