應用場景:
- 離線應用程序數據同步到服務器端
- 服務器端數據同步到離線應用程序
同步記錄表設計:
名 | 類型 | 不是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基礎知識:
編寫同步數據邏輯抽象類代碼:
- 根據主鍵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>
注意:代碼是死的人是活的,靈活使用,不要被代碼侷限了,這個只是提供一下思路,具體怎麼使用可以自己根據實際需求改和優化,深刻理解設計思路和對抽象類的一個靈活使用。
這裏的Dao層和sql的寫法是jdbctemplate的封裝,可以借鑑我的一篇博客——Java基於jdbctemplate數據持久層操作封裝
剛開始肯定有點難理解,耐下心仔細看,歡迎相互討論——QQ:892715310,WX:Miss5202468。