在面向对象程序设计过程中,程序员常常会遇到这种情况:设计一个系统时知道了算法所需的关键步骤,而且确定了这些步骤的执行顺序,但某些步骤的具体实现还未知,或者说某些步骤的实现与具体的环境相关。
例如,去银行办理业务一般要经过以下4个流程:取号、排队、办理具体业务、对银行工作人员进行评分等,其中取号、排队和对银行工作人员进行评分的业务对每个客户是一样的,可以在父类中实现,但是办理具体业务却因人而异,它可能是存款、取款或者转账等,可以延迟到子类中实现。
定义:
定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。
项目中mongodb有多个日志类的表数据过多,不仅查询影响性能、还增加了存储成本,决定这些表只保留6个月的数据,大于6个月在2年零6个月内的数据放在备份表。超过两年零6个月的数据直接删除。
这里拿两个表举例,分别是:
nativeDO
successDO
备份表名称定义为:
nativeDO_history
successDO_history
这些表移动数据的步骤是固定的,分别为:原始表两年零6个月的直接删除->把原始表6个月外两年零6个月内的移动到历史表->删除历史表两年零6个月后的数据。我们把这个固定的步骤抽象出来。
抽象类(Abstract Class):负责给出一个算法的轮廓和骨架。它由一个模板方法和若干个基本方法构成。
@Slf4j
public abstract class HistoricalDataAbstractClass<T> {
/**
* 两年零6个月的直接删除
*
* @param timeYear
* @param time
* @return
*/
protected abstract void delete(Long timeYear, Long time);
/**
* 6个月外两年零6个月内的移动到历史表
* @param pageSize
* @param time
* @return
*/
protected abstract PageResult<T> mobileData(Integer pageSize, Long time);
/**
* 删除历史表两年零6个月后的数据
* @param timeYear
* @param time
*/
protected abstract void deleteByHistoryTable(Long timeYear, Long time);
public final void historicalData(String jobName) {
Integer pageSize = 2000;
// 6个月前的时间戳,今日 2023-08-18 00:00:00那么结果就是2023-02-18 00:00:00
long time =
DateUtil.offset(
DateUtil.parse(DateUtil.today()),
DateField.MONTH,
-BaseConstant.SAVE_DATA_FOR_SEVERAL_MONTHS)
.getTime();
// 2年6个月前的时间戳
long timeYear =
DateUtil.offset(
DateUtil.parse(DateUtil.formatDate(new Date(time))),
DateField.YEAR,
-BaseConstant.SAVE_DATA_FOR_SEVERAL_YEAR)
.getTime();
try {
//两年零6个月的直接删除
delete(timeYear, time);
//6个月外两年零6个月内的移动到历史表
PageResult<T> page = mobileData(pageSize, time);
Long total = page.getTotal();
while (total > 0) {
page = mobileData(pageSize, time);
total = page.getTotal();
//停止
if (page.getList().size() < pageSize) {
break;
}
}
//删除历史表两年零6个月后的数据
deleteByHistoryTable(timeYear, time);
} catch (Exception e) {
log.error("historicalData " + jobName + " error", e);
}
}
}
具体子类(Concrete Class):实现抽象类中所定义的抽象方法。
这是一个操作nativeDO表的具体子类
@Slf4j
@Service
public class NativeHookRecordHistoricalDataServiceImpl
extends HistoricalDataAbstractClass<NativeHookRecordDO>{
@Resource private INativeHookRecordDbService nativeHookRecordDbService;
/**
* 两年零6个月的直接删除
* @param timeYear
* @param time
*/
@Override
protected void delete(Long timeYear, Long time) {
log.info(
"historicalData NativeHookRecordHistoricalDataJob Data processing time point:{}", time);
nativeHookRecordDbService.createHistoricalDataCollection();
DeleteResult nativeHookRecordDeleteResult = nativeHookRecordDbService.deleteByTime(timeYear);
log.info(
"historicalData NativeHookRecordHistoricalDataJob nativeHookRecordDeleteResult:{},time:{},Delete before this time:{}",
nativeHookRecordDeleteResult,
time,
timeYear);
}
/**
* 删除历史表两年零6个月后的数据
* @param timeYear
* @param time
*/
@Override
protected void deleteByHistoryTable(Long timeYear, Long time) {
DeleteResult nativeHookRecordHistoryDeleteResult =
nativeHookRecordDbService.deleteByHistoryTable(timeYear);
log.info(
"historicalData NativeHookRecordHistoricalDataJob nativeHookRecordHistoryDeleteResult:{},time:{},Delete before this time:{}",
nativeHookRecordHistoryDeleteResult,
time,
timeYear);
}
/**
* 6个月外两年零6个月内的移动到备份表
* @param pageSize
* @param time
* @return
*/
@Override
protected PageResult<NativeHookRecordDO> mobileData(Integer pageSize, Long time) {
PageResult<NativeHookRecordDO> page =
nativeHookRecordDbService.findPagByTime(time, 1, pageSize);
nativeHookRecordDbService.deleteById(page.getList());
nativeHookRecordDbService.insertMultiple(page.getList());
return page;
}
}
这是一个操作successDO表的具体子类
@Slf4j
@Service
public class SuccessHookAddFieldServiceImpl
extends HistoricalDataAbstractClass<SuccessHookDO>{
@Resource private SuccessHookDbServiceImpl successHookDbService;
/**
* 两年零6个月的直接删除
* @param timeYear
* @param time
*/
@Override
protected void delete(Long timeYear, Long time) {
log.info("historicalData SuccessHookHistoricalDataJob Data processing time point:{}", time);
successHookDbService.createHistoricalDataCollection();
// 超过2年零6个月的数据直接删除
DeleteResult successHookDeleteResult = successHookDbService.deleteByTime(timeYear);
log.info(
"historicalData SuccessHookHistoricalDataJob successHookDeleteResult:{},time:{},Delete before this time:{}",
successHookDeleteResult,
time,
timeYear);
}
/**
* 删除历史表两年零6个月后的数据
* @param timeYear
* @param time
*/
@Override
protected void deleteByHistoryTable(Long timeYear, Long time) {
DeleteResult successHookHistoryDeleteResult =
successHookDbService.deleteByTimeAndCollectionName(timeYear);
log.info(
"historicalData SuccessHookHistoricalDataJob successHookHistoryDeleteResult:{},time:{},Delete before this time:{}",
successHookHistoryDeleteResult,
time,
timeYear);
}
/**
* 6个月外两年零6个月内的移动到备份表
* @param pageSize
* @param time
* @return
*/
@Override
protected PageResult<SuccessHookDO> mobileData(Integer pageSize, Long time) {
PageResult<SuccessHookDO> page = successHookDbService.findPagByTime(time, 1, pageSize);
successHookDbService.deleteById(page.getList());
successHookDbService.insertMultiple(page.getList());
return page;
}
}
最后我们通过定时任务来调用
/**
* 这个定时任务用于nativeDO表6个月的数据保存再nativeDO表, 6个月外两年零6一个月内的保存在nativeDO_history表
* 两年零6一个月外的直接删除
*/
@Slf4j
@Data
@Component
@EqualsAndHashCode(callSuper = true)
public class NativeHookRecordHistoricalDataJob extends QuartzJobBean {
@Resource private NativeHookRecordHistoricalDataServiceImpl nativeHookRecordHistoricalDataService;
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
try {
log.info("historicalData NativeHookRecordHistoricalDataJob start.");
nativeHookRecordHistoricalDataService.historicalData("NativeHookRecordHistoricalDataJob");
log.info("historicalData NativeHookRecordHistoricalDataJob end.");
} catch (Exception e) {
log.error("historicalData NativeHookRecordHistoricalDataJob error.", e);
}
}
}
/**
* 这个定时任务用于successDO表6个月的数据保存再successDO表, 6个月外两年零6一个月内的保存在successDO_history表
* 两年零6一个月外的直接删除
*/
@Slf4j
@Data
@Component
@EqualsAndHashCode(callSuper = true)
public class SuccessHookHistoricalDataJob extends QuartzJobBean {
@Resource private SuccessHookAddFieldServiceImpl successHookAddFieldService;
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
log.info("historicalData SuccessHookHistoricalDataJob start.");
try {
successHookAddFieldService.historicalData("SuccessHookHistoricalDataJob");
log.info("historicalData SuccessHookHistoricalDataJob end.");
} catch (Exception e) {
log.error("historicalData SuccessHookHistoricalDataJob error.", e);
}
}
}
优点:
缺点: