SpringBoot统一清理数据

本文介绍如何通过Spring Boot和并发流技术,利用ICleanData接口实现数据清理,简化定时任务管理,并演示了如何在业务层模拟Dao操作。重点涉及数据库维护策略和时间处理,包括自动时间更新及日期格式化。

业务背景:一般时序数据会有保存数据周期,例如三个月或者是半年之久,一般方案是定时任务调用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

JdbcTemplate获取MySQL源表信息.

    // 查询JDBC的所有表
    public List<String> getAllTableList(){
        List<String> tableList = jdbcTemplate.query("SHOW TABLES", new RowMapper<String>() {
            @Override
            public String mapRow(ResultSet rs, int rowNum) throws SQLException {
                return rs.getString(1);
            }
        });
        return tableList;
    }
    // 查询指定数据库的所有表以及表类型信息
    public List<Table> queryTable(){
        KeyHolder keyHolder = new GeneratedKeyHolder();
        List<Table> query = jdbcTemplate.query("SELECT TABLE_NAME, COLUMN_NAME, DATA_TYPE, COLUMN_KEY, IS_NULLABLE\n" +
                "FROM INFORMATION_SCHEMA.COLUMNS\n" +
                "WHERE TABLE_SCHEMA = 'test'", new TableRowMapper());
        return query;
    }
class TableRowMapper implements RowMapper<Table>{

    @Override
    public Table mapRow(ResultSet rs, int rowNum) throws SQLException {
        Table table = new Table();
        table.setTableName(rs.getString("TABLE_NAME"));
        table.setColumnName(rs.getString("COLUMN_NAME"));
        table.setDateType(rs.getString("DATA_TYPE"));
        table.setColumnKey(rs.getString("COLUMN_KEY"));
        table.setIsNullAble(rs.getString("IS_NULLABLE"));
        return table;
    }
}
class Table{

    private String tableName;
    private String columnName;
    private String dateType;
    private String columnKey;
    private String isNullAble;

    public Table() {
    }

    public String getTableName() {
        return tableName;
    }

    public void setTableName(String tableName) {
        this.tableName = tableName;
    }

    public String getColumnName() {
        return columnName;
    }

    public void setColumnName(String columnName) {
        this.columnName = columnName;
    }

    public String getDateType() {
        return dateType;
    }

    public void setDateType(String dateType) {
        this.dateType = dateType;
    }

    public String getColumnKey() {
        return columnKey;
    }

    public void setColumnKey(String columnKey) {
        this.columnKey = columnKey;
    }

    public String getIsNullAble() {
        return isNullAble;
    }

    public void setIsNullAble(String isNullAble) {
        this.isNullAble = isNullAble;
    }

    @Override
    public String toString() {
        return "Table{" +
                "tableName='" + tableName + '\'' +
                ", columnName='" + columnName + '\'' +
                ", dateType='" + dateType + '\'' +
                ", columnKey='" + columnKey + '\'' +
                ", isNullAble='" + isNullAble + '\'' +
                '}';
    }

 返回JSON信息如下:

 SQL查询语句如下:

SELECT TABLE_NAME, COLUMN_NAME, DATA_TYPE, COLUMN_KEY, IS_NULLABLE
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'test';

SELECT *
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'test'

查询表字段类型: 

写入数据库的datetime类型数据处理

①、使用mybatis的xml.

    <insert id="saveUser" parameterType="com.boot.skywalk.entity.UserInfo">
        insert into user_info(user_name,create_time,update_time) VALUES (#{userName},#{createTime,jdbcType=TIMESTAMP},#{updateTime,jdbcType=TIMESTAMP})
    </insert>

上层传递参数. 

        UserInfo userInfo = new UserInfo();
        userInfo.setId(5);
        userInfo.setUserName("Puck");
        Date startTime = new Date();
        Date endTime = new Date();
        userInfo.setCreateTime(startTime);
        userInfo.setUpdateTime(endTime);
        userInfoService.saveUser(userInfo);

 全局开启日期格式化

spring:    
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT+8

②、使用JdbcTemplate处理

 使用具名参数: 使用 SqlParameterSource作为参数

        UserInfo userInfo = new UserInfo();
        userInfo.setId(7);
        userInfo.setUserName("Boot");
        Date startTime = new Date();
        Date endTime = new Date();
        userInfo.setCreateTime(startTime);
        userInfo.setUpdateTime(endTime);
        // JdbcTemplate的写入datetime,使用参数占位方式
        String sql="insert into user_info(user_name,create_time,update_time) values(:userName,:createTime,:updateTime)";
        SqlParameterSource sqlParameterSource=new BeanPropertySqlParameterSource(userInfo);
        jdbcTemplate.update(sql,sqlParameterSource);

使用Map参数占位

        UserInfo userInfo = new UserInfo();
        userInfo.setId(7);
        userInfo.setUserName("Boot");
        Date startTime = new Date();
        Date endTime = new Date();
        userInfo.setCreateTime(startTime);
        userInfo.setUpdateTime(endTime);
        // JdbcTemplate的写入datetime,使用in方式
        String sql="insert into user_info(user_name,create_time,update_time) values(:user_name,:create_time,:update_time)";
        HashMap paramMap = new HashMap<>();
        paramMap.put("user_name",userInfo.getUserName());
        paramMap.put("create_time",userInfo.getCreateTime());
        paramMap.put("update_time",userInfo.getUpdateTime());
        jdbcTemplate.update(sql,paramMap);
        jdbcTemplate.update(sql,sqlParameterSource);

查看事务提交

五、总结归纳 

不仅仅是数据清理,也包括一些需要并行的逻辑可以采用并发流的方式来执行,注意是IO密集型还是CPU密集型选择对应的框架和线程池即可.

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大道之简

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值