业务背景:一般时序数据会有保存数据周期,例如三个月或者是半年之久,一般方案是定时任务调用dao层删除数据,不方便统一管理,扩展性也不够好,这里通过并发流线程池实现并行执行删除逻辑.
一、接口定义
package com.boot.skywalk.task;
/**
* 通用数据清理任务接口
*/
@FunctionalInterface
public interface ICleanData {
/**
* 清理数据
*/
void cleanData();
}
二、业务层模拟Dao层删除逻辑
@Slf4j
@Component
public class ConfigService implements ICleanData {
@Autowired
private ConfigDao configDao;
@Override
public void cleanData() {
configDao.deleteConfigData();
log.info("ConfigService Clean Success");
}
}
@Slf4j
@Component
public class StatService implements ICleanData {
@Autowired
private StatDao statDao;
@Override
public void cleanData() {
statDao.deleteStatData();
log.info("StatService Clean Success");
}
}
三、清理任务
@Slf4j
@Component
public class CleanDataTask {
/**
* 并发流定时任务清理过期数据,集群部署时候,分布式任务只在一个节点运行即可,XXL-JOB和Quartz中
*/
public void clean(){
// 获取所有需要实现业务清理数据的Bean
List<ICleanData> dataList =CommonBeanUtils.getBeanList(ICleanData.class);
Instant start=Instant.now();
log.info("start clean data");
// 并发流同时执行业务层数据清理任务
dataList.parallelStream().forEach(cleanable->{
// 具体异常在各自实现逻辑中单独捕获
cleanable.cleanData();
});
Instant end=Instant.now();
long costTime = Duration.between(start, end).getSeconds();
log.info("finish clean data,cost time={}", costTime);
}
}
四、测试运行
任务并行执行删除逻辑耗时2秒钟,并发流线程池ForkJoinPool.业务开发只需继承数据清理接口实现各自自己的数据清理逻辑即可,这里可以采用自定义线程池+CountDownLatch来实现,业务线程逻辑更加好控制.
【附录SpringBoot项目时间处理】
建表SQL如下:新增和修改时候自动更新时间
create table user_info (
id int primary key auto_increment comment '主键ID',
user_name varchar(50) not null comment '用户名称',
create_time datetime not null default CURRENT_TIMESTAMP comment '创建时间',
update_time datetime not null default CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP comment '更新时间'
) engine = Innodb default charset = utf8mb4 comment '用户信息表';
新增修改都是自动更新时间.
基于MyBatis-Plus的方式的三层
Mapper层:
package com.boot.skywalk.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.boot.skywalk.entity.UserInfo;
public interface UserInfoMapper extends BaseMapper<UserInfo> {
}
Xml:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.boot.skywalk.mapper.UserInfoMapper">
</mapper>
service
package com.boot.skywalk.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.boot.skywalk.entity.UserInfo;
public interface UserInfoService extends IService<UserInfo> {
}
impl
package com.boot.skywalk.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.boot.skywalk.entity.UserInfo;
import com.boot.skywalk.mapper.UserInfoMapper;
import com.boot.skywalk.service.UserInfoService;
import org.springframework.stereotype.Service;
@Service
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService {
}
controller
package com.boot.skywalk.controller;
import com.boot.skywalk.entity.UserInfo;
import com.boot.skywalk.service.UserInfoService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@Slf4j
@RestController
public class UserInfoController {
@Autowired
private UserInfoService userInfoService;
/**
* 查询全部列表
* @return
*/
@RequestMapping("/boot/users")
public List<UserInfo> getUserInfoList(){
return userInfoService.list();
}
}
查询列表返回数据.
时间处理策略:
①、前段处理展示.
②、SimpleDateFormat格式化或者是DateTimeFormatter格式化来增加字段处理,大型项目有专门的TimeUtil来转换各种时间,项目中的时间是point来存储时间戳然后进行转化.
package com.boot.skywalk.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import javax.persistence.Table;
import java.util.Date;
@Data
@Table(name="user_info")
public class UserInfo {
@TableId(type = IdType.AUTO)
private Integer id;
@TableField("user_name")
private String userName;
@TableField("create_time")
@JsonIgnore// 输出结果时隐藏此字段
private Date createTime;
@TableField("update_time")
@JsonIgnore// 输出结果时隐藏此字段
private Date updateTime;
// 时间格式化后的字段,数据库不存在的字段
@TableField(exist = false)
private String ctime;
// 时间格式化后的字段,数据库不存在的字段
@TableField(exist = false)
private String utime;
}
controller修改.
package com.boot.skywalk.controller;
import com.boot.skywalk.entity.UserInfo;
import com.boot.skywalk.service.UserInfoService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.text.SimpleDateFormat;
import java.util.List;
@Slf4j
@RestController
public class UserInfoController {
// 定义时间格式化对象和定义格式化样式
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@Autowired
private UserInfoService userInfoService;
/**
* 查询全部列表
* @return
*/
@RequestMapping("/boot/users")
public List<UserInfo> getUserInfoList(){
List<UserInfo> list = userInfoService.list();
list.forEach(user->{
user.setCtime(dateFormat.format(user.getCreateTime()));
user.setUtime(dateFormat.format(user.getUpdateTime()));
});
return list;
}
}
③、使用@JsonFormat添加对应注解即可
@TableField("update_time")
//@JsonIgnore// 输出结果时隐藏此字段
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
private Date updateTime;
④、或者在全局配置文件中进行配置
【附录定时任务创建分表】也可以基于并发流创建然后配置完整的告警管理,实现统一接口然后并发流创建.
Xml
<mapper namespace="com.boot.skywalk.mapper.SupportMapper">
<update id="createTable" parameterType="String">
create table ${tableName} (
id int(11) auto_increment primary key,
user_name varchar(20) not null,
user_password varchar(20) not null
);
</update>
</mapper>
package com.boot.skywalk.mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
@Repository
public interface SupportMapper {
/**
* createTable
* @param tableName
* @return int
*/
int createTable(@Param("tableName") String tableName);
}
控制台执行日志:
information_schema.TABLE表中查看
【验证数据库字符串处理时间】
数据库建表字段 为timestamp/datatime,前段时间为字符串处理
{
"name": "Dubbo",
"address": "GuangZhou",
"create_time": "2023-01-14 20:19:24"
}
create table result(
id int(11) auto_increment primary key,
`name` varchar(10) not null,
address varchar(30) not null,
create_time timestamp
);
接口层时间获取json数据,@RequestBody接收,比较简单.
@PostMapping("/insert/resultVo")
public String saveResultVo(@RequestBody CustomResultVo customResultVo){
try {
resultMapper.save(customResultVo);
} catch (Exception e) {
log.info("save error",e);
}
return "Success";
}
这里测试使用jdbcType的DATE类型
<!--数据 -->
<insert id="save" useGeneratedKeys="true" keyProperty="id" parameterType="com.boot.skywalk.vo.CustomResultVo">
insert into result(`name`,`address`,`create_time`) values(#{name,jdbcType=VARCHAR},#{address,jdbcType=VARCHAR},#{createTime,jdbcType=DATE})
</insert>
使用Java的Date接收前端时间json字符串参数
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CustomResultVo {
private int id;
private String name;
private String address;
/**
* 设置时间格式,
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
@JsonProperty("create_time")
private Date createTime;
}
MyBatis的JdbcType枚举,这里转换使用TimeStamp
数据库建表语句
查询数据库,发现只有年月日
XML中修改为TimeStamp
再次查询数据,保存正常。
MyBatis处理MySQL字段类型date与datetime
五、总结归纳
不仅仅是数据清理,也包括一些需要并行的逻辑可以采用并发流的方式来执行,注意是IO密集型还是CPU密集型选择对应的框架和线程池即可.