spring boot定时获取系统时间全表扫描数据库对比删除任务------含自动配置执行时间以及遇到的错误
在公司实习了三个月,一直没有实际性的开发任务,上周五接到两个开发小任务,十分激动!
任务一、定时任务,能够实现定时执行扫描数据库并根据表中过期时间删除过期数据,定时时间在配置文件可配置。
大概要求为:
1.使用springboot框架;
2.执行时间使用配置文件;
3.软删除使用update标记;
4.对比时间为系统时间。
下面进入开发:
代码结构如下:
毕竟是新手学习式开发,也是第一次使用springboot框架,比较注重细节。从实现框架跑通,到查询用例、注解定时用例、获取时间戳用例,再到更新删除实现,最后实现配置文件定时。
数据库:
application.yml配置:
server:
port: 8082
spring:
jackson:
serialization:
write-dates-as-timestamps: true
profiles:
active: a
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/ocsp
username: root
password: root
jpa:
hibernate:
ddl-auto: update
show-sql: true
启动主类Application.java:
package com.xdja.timingDemo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
@EnableScheduling //定时注解开启
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
实体类entity:
import com.fasterxml.jackson.annotation.JsonFormat;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import java.util.Date;
@Entity
@Table(name="t_cert")
public class Cert {
@Id
private Integer cert_sn ;
@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date expire_date_time ;
//时间戳
private Long time ;
//用来标记
private Integer status;
Dao(接口 )层:
package com.xdja.timingDemo.dao;
import com.xdja.timingDemo.entity.Cert;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.Date;
import java.util.List;
import java.util.Optional;
@Repository
public interface CertDao extends JpaRepository<Cert,Integer> {
//通过ID查询
Optional<Cert> findById(Integer Id);
//全表查询
List<Cert> findAll();
//自定义sql查询
@Query(value = "select expire_date_time from Cert where time < '1609344000'")
List<Date> findAllDate();
//指定时间删除
@Modifying //DML操作需添加该注解
@Query(value = "delete from Cert where time < '1609344000'")
void deleteTime();
//指定时间更新(软删除)
@Modifying
@Query(value = "update Cert set status = 0 where time < '1550645979'")
void updateStatus();
//获取系统时间比较更新(软删除)
@Modifying
@Query(value = "update Cert set status = 0 where time < :time ")
void updateStatusByTime(@Param("time") Long time);
}
Service层:
package com.xdja.timingDemo.service;
import com.xdja.timingDemo.dao.CertDao;
import com.xdja.timingDemo.entity.Cert;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.transaction.Transactional;
import java.util.Date;
import java.util.List;
import java.util.Optional;
@Service
@Transactional
public class CertService {
@Autowired
private CertDao certDao;
public Optional<Cert> findById(Integer Id){
return certDao.findById(Id);
}
public List<Cert> findAll(){
return certDao.findAll();
}
public List<Date> findAllDate(){
return certDao.findAllDate();
}
public void deleteTime(){
certDao.deleteTime();
}
public void updateStatus(){
certDao.updateStatus();
}
public void updateStatusByTime(Long time){
/*System.out.println(time);*/
certDao.updateStatusByTime(time);
}
}
实体类有两部分,用于测试和铺垫的在CertController.Java,开发任务在TaskService.Java。
CertController.Java:
package com.xdja.timingDemo.controller;
import com.xdja.timingDemo.entity.Cert;
import com.xdja.timingDemo.service.CertService;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Optional;
@RestController
public class CertController {
private static final Logger log = LoggerFactory.getLogger(CertController.class);
@Autowired
CertService certService;
//通过Id查询一条
@GetMapping(value = "/cert/{Id}")
public Optional<Cert> findById(@PathVariable("Id") Integer Id) {
return certService.findById(Id);
}
//查询全表
@GetMapping(value = "/cert/findAll")
public List<Cert> findAll() {
return certService.findAll();
}
//查询过期时间
@GetMapping(value = "/cert/findAllDate")
public List<Date> findAllDate() {
return certService.findAllDate();
}
//删除过期时间数据
@GetMapping(value = "/cert/deleteTime")
public void deleteTime() {
certService.deleteTime();
}
//@Scheduled(cron ="${cron}")
public void updateStatus(){
certService.updateStatus();
log.info("更新status成功!");
}
//获取系统时间转换成时间戳测试
/* public static void main(String[] args) throws ParseException{
//设置日期格式
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// new Date()为获取当前系统时间
String s = df.format(new Date());
log.info("当前时间是:"+s);
//转换成时间戳
Date date = df.parse(s);
long ts = date.getTime();
log.info("当前时间的时间戳是:"+ts);
}*/
/*
//定时任务测试
@Scheduled(fixedRate = 10000)
public void logTime(){
log.info("定时任务,现在时间:"+System.currentTimeMillis());
}*/
}
TaskService.Java:
package com.xdja.timingDemo.service;
import com.xdja.timingDemo.controller.CertController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.PropertySource;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
@Component
@PropertySource(value = {"classpath:param.properties"},ignoreResourceNotFound=true)//获取配置文件中的cron
public class TaskService {
private static final Logger log = LoggerFactory.getLogger(CertController.class);
@Autowired
CertService certService;
@Scheduled(cron ="${cron}")
public void updateStatusByTime()throws ParseException {
//设置日期格式
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// new Date()为获取当前系统时间
String s = df.format(new Date());
log.info("当前时间s是------:"+s);
//转换成时间戳
Date date = df.parse(s);
long time = date.getTime()/1000;
log.info("当前时间戳ts是-------"+time);
certService.updateStatusByTime(time);
log.info("更新status成功!");
}
}
所以我们就需要一个配置参数cron的文件param.properties。
param.properties:
cron=0/5 * * * * ?
//5s查询一次
cron表达式可在 http://cron.qqe2.com/在线生成。
在网上查了很久才找到这种传参方式,通过注解
@PropertySource(value = {"classpath:param.properties"},ignoreResourceNotFound=true)
在配置文件param.properties中获取cron,比较适用于实际开发。
此上,基本实现一个定时全表扫描数据库与系统时间相比较更新数据库的任务!
运行结果:
数据库:
数据库中某条数据的时间戳(Long time)小于当前系统时间的时间戳时,该条数据的status的值由1变成0:
期间,遇到了一些问题:
(1)自定义sql语句,DML事务操作需添加注解@Modifying。否则会报错:
nested exception is java.lang.IllegalStateException: org.hibernate.hql.internal.QueryExecutionRequestException: Not supported for DML operations [update com.xdja.timingDemo.entity.Cert set status = 0 where time < :time ]
(2)自定义sql语句的where语句的传值方式,使用 :参数。
value = "update Cert set status = 0 where time < :time "
(3)时间和时间戳的转换,转换后Long型时间戳会自动补0到由10位变成13位。
在这里我使用了/1000,以便对照数据库;
//设置日期格式 SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // new Date()为获取当前系统时间 String s = df.format(new Date()); log.info("当前时间s是------:"+s); //转换成时间戳 Date date = df.parse(s); long time = date.getTime()/1000; log.info("时间戳ts-------"+time);
(4)是如何使用mysql字段获取Long型时间戳。
如下语句,可以将日期转化成10位时间戳1550556315。
SELECT UNIX_TIMESTAMP('2019-02-19 14:05:15')
(5) 注解@Scheduled修饰的方法不能传参数,负责会报错:
Encountered invalid @Scheduled method 'updateStatusByTime': Only no-arg methods may be annotated with @Scheduled
(6)自定义sql语句中from 表名,这个表名为实体类Entity的类名。
@Query(value = "select * from Cert")正确
@Query(value = "select * from t_cert")错误
还有一些细小的错误就不一一列举了。
通过此次开发任务,学到了很多的新知识。以后要更加努力吖。
此篇文章如有错误请指正!!!