应用场景:
- 离线应用程序数据同步到服务器端
- 服务器端数据同步到离线应用程序
同步记录表设计:
名 | 类型 | 不是null | 主键 | 备注 |
id | int | √ | √ | 主键id |
start_id | int | | | 被同步表数据,开始id |
end_id | int | | | 被同步表数据,结束id |
end_upate_time | timestamp | | | 同步结束时的时间(被同步表最后一条同步数据创建时间) |
sync_type | varchar | | | 同步类型 |
create_time | timestamp | √ | | 创建时间 |
创建同步记录表sql文件:
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程序实现Mysql数据同步,要对抽象类有深刻的理解,不然会对代码逻辑很懵懂,不懂得同学可以看我这篇博客回忆一下Java基础知识:
Java抽象类
编写同步数据逻辑抽象类代码:
- 根据主键id同步
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<Boolean, Object> 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<Boolean, Object> 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<Boolean, Object> 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<Boolean, Object> 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<String,Object> 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<String,Object> 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<String,Object> 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<String,Object> 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<String,Object> 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<String,Object> 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<String,Object> param = new HashMap<>();
param.put("syncType", syncType);
return daoClient.queryForObject(sqlId, param, String.class);
}
}
sql语句:
<sqltemplate id="queryPreviousId">
<![CDATA[
SELECT
IFNULL (MAX(end_id),0) lastId
FROM SYNC_RECORD
WHERE SYNC_TYPE = :syncType
]]>
</sqltemplate>
<sqltemplate id="queryExsitBySyncType">
<![CDATA[
SELECT
count(id)
FROM SYNC_RECORD
WHERE SYNC_TYPE = :syncType
]]>
</sqltemplate>
<sqltemplate id="updateEndIdBySyncType">
<![CDATA[
UPDATE SYNC_RECORD
SET
END_ID = :endId
WHERE SYNC_TYPE = :syncType
]]>
</sqltemplate>
<sqltemplate id="saveSyncRecord">
<![CDATA[
INSERT INTO SYNC_RECORD
SET
START_ID = :startId ,
END_ID = :endId ,
SYNC_TYPE = :syncType
]]>
</sqltemplate>
<sqltemplate id="updateEndUpdateTimeBySyncType">
<![CDATA[
update SYNC_RECORD
SET
end_upate_time = :endUpdateTime
where SYNC_TYPE = :syncType
]]>
</sqltemplate>
<sqltemplate id="saveEndUpdateTimeBySyncType">
<![CDATA[
INSERT INTO SYNC_RECORD
SET
END_UPATE_TIME = :endUpdateTime ,
SYNC_TYPE = :syncType
]]>
</sqltemplate>
<sqltemplate id="queryPreviousEndUpdateTime">
<![CDATA[
SELECT
IFNULL (MAX(end_upate_time),'2018-01-01 00:00:00') lastId
FROM SYNC_RECORD
WHERE SYNC_TYPE = :syncType
]]>
</sqltemplate>
注意:代码是死的人是活的,灵活使用,不要被代码局限了,这个只是提供一下思路,具体怎么使用可以自己根据实际需求改和优化,深刻理解设计思路和对抽象类的一个灵活使用。