SpringBoot如何统一清理数据
作者:大道之简
业务背景:
一般时序数据会有保存数据周期,例如三个月或者是半年之久,一般方案是定时任务调用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密集型选择对应的框架和线程池即可.
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。