关于Java中集成mysql(springboot)处理数据创建时间和最后更新时间的总结
在服务端开发中,经常会遇到需要记录数据库记录的创建时间与更新时间,往常的时候只是把别人的代码粘贴过来用了,也没有实际整理过;近几天通过网上搜索及实际测试整理出一套结论
网上目前主要流传有4种解决方案,但是有一些方案确实并不合适,以下是自己整理的以下实测结果及最终推荐方案,请参考指正
1 在数据库表设计上直接解决
`createTime` datetime DEFAULT CURRENT_TIMESTAMP,
`updateTime` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
--datetime(3):括号内的数字表示保留的毫秒位数。
实测结果:
在数据库中创建一测试表,并按上述脚本建立创建时间和最后更新时间
因不推荐此方案,所以就不摆操作截图了,直说结果总结:
1.该方案在entity对象中如果不设置createTime和updateTime属性是可以的,数据库可以自动更新两个时间
2.但是在实际业务中我们很有可能会需要查询出两个时间,用于了解数据的创建和更新情况,或者用于排序;但是问题就在这里了,如果在entity对象中增加了这两个参数,并且不设置值的话,就会导致数据库中这两个字段的值为空
2 有人提出用@DynamicUpdate和@DynamicInsert
先说一下@DynamicUpdate和@DynamicInsert是干什么的,什么作用?
@DynamicInsert属性:设置为true,设置为true,表示insert对象的时候,生成动态的insert语句,如果这个字段的值是null就不会加入到insert语句当中.默认false。
比如希望数据库插入日期或时间戳字段时,在对象字段为空的情况下,表字段能自动填写当前的sysdate。
@DynamicUpdate属性:设置为true,设置为true,表示update对象的时候,生成动态的update语句,如果这个字段的值是null就不会被加入到update语句中,默认false。
比如只想更新某个属性,但是却把整个对象的属性都更新了,这并不是我们希望的结果,我们希望的结果是:我更改了哪些字段,只要更新我修改的字段就够了。
举例说明
看下面打印的sql语句就会立刻明白,使用这两个注解的效果
@DynamicInsert注解下Hibernate日志打印SQL
Hibernate: insert into Cat (cat_name, id) values (?, ?)
反之
Hibernate: insert into Cat (create_time, update_time, cat_name, id) values (?, ?, ?, ?)
@DynamicUpdate注解下Hibernate日志打印SQL:
说明:如果字段有更新,Hibernate才会对该字段进行更新
Hibernate: update Cat set update_time=? where id=?
反之Cat实体类去掉@DynamicUpdate
说明:不管字段有没有更新,Hibernate都会对该字段进行更新
Hibernate: update Cat set update_time=?, cat_name=? where id=?
@MappedSuperclass 这个注解表示在父类上面的,用来标识父类。
基于代码复用和模型分离的思想,在项目开发中使用JPA的@MappedSuperclass注解将实体类的多个属性分别封装到不同的非实体类中。例如,数据库表中都需要id来表示编号,id是这些映射实体类的通用的属性,交给jpa统一生成主键id编号,那么使用一个父类来封装这些通用属性,并用@MappedSuperclas标识。
注意:
1.标注为@MappedSuperclass的类将不是一个完整的实体类,他将不会映射到数据库表,但是他的属性都将映射到其子类的数据库字段中。
2.标注为@MappedSuperclass的类不能再标注@Entity或@Table注解,也无需实现序列化接口。
@MappedSuperclass
public abstract class BaseEntity{
@Id
@GeneratedValue
@Column(length=20)
public Integer getId() {
return this.id;
}
public void setId(Integer id) {
this.id = id;
}
}
以上解释参考原文链接:https://blog.csdn.net/Janson_Lin/article/details/95059297
实验结论:
用@DynamicUpdate和@DynamicInsert实现最后更新时间其实是用了一种讨巧的方案,并且更新的时候也会存在问题;这里就不多做描述了,在此学习一下这两个注解也是很不错的
3 Spring Data JPA 的时间注解:@CreatedDate 和 @LastModifiedDate
选择 Spring Data JPA 框架开发时,常用在实体和字段上的注解有@Entity
、@Id
、@Column
等。在表设计规范中,通常建议保留的有两个字段,一个是更新时间,一个是创建时间。Spring Data JPA 提供了相应的时间注解,只需要两步配置,就可以帮助开发者快速实现这方面的功能。
- 在实体类上加上注解
@EntityListeners(AuditingEntityListener.class)
,在相应的字段上添加对应的时间注解@LastModifiedDate
和@CreatedDate
注意:日期类型可以用
Date
也可以是Long
@Entity
@EntityListeners(AuditingEntityListener.class)
public class User {
/**
* 自增主键
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
/**
* 更新时间
*/
@LastModifiedDate
@Column(nullable = false)
private Long updateTime;
/**
* 创建时间
*/
@CreatedDate
@Column(updatable = false, nullable = false)
private Date createTime;
// 省略getter和setter
2.在Application启动类中添加注解 @EnableJpaAuditing
@EnableJpaAuditing
@SpringBootApplication
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}
测试总结:该方案确实有效,但是配置相对繁琐;还需要在启动类上加注解
4 Hibernate 注解(最终推荐)
Hibernate 也提供了类似上述时间注解的功能实现,这种方法只需要一步配置,更改为注解 @UpdateTimestamp
和 @CreationTimestamp
即可(参考如下):
请注意:在执行更新的时候需要给UpdateTime进行一次赋值;(其实我的目标是想不赋值自动更新的,如果大家有好的解决方案请指点)
User aa=userRepository.findByUserName("a");
aa.setUpdateTime(new Date());
userRepository.save(aa);
@Entity
@Table(name = "java2mysql")
@Data
public class Java2Mysql implements Serializable {
private static final long serialVersionUID = -1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
@Column(name = "name")
private String name;
@Column(name = "update_time")
@Temporal(TemporalType.TIMESTAMP)
@UpdateTimestamp
private Date updateTime;
@Column(name = "create_time",updatable = false)
@Temporal(TemporalType.TIMESTAMP)
@CreationTimestamp
private Date createTime;
在此补充两个注解的解释
@Column
# @Column注解
用来标识实体类中属性与数据表中字段的对应关系
# 属性详解:
name
定义了被标注字段在数据库表中所对应字段的名称;
unique
表示该字段是否为唯一标识,默认为false。如果表中有一个字段需要唯一标识,则既可以使用该标记,也可以使用@Table标记中的@UniqueConstraint。
nullable
表示该字段是否可以为null值,默认为true。
insertable
表示在使用“INSERT”脚本插入数据时,是否需要插入该字段的值。
updatable
表示在使用“UPDATE”脚本插入数据时,是否需要更新该字段的值。insertable和updatable属性一般多用于只读的属性,例如主键和外键等。这些字段的值通常是自动生成的。
columnDefinition(大多数情况,几乎不用)
表示创建表时,该字段创建的SQL语句,一般用于通过Entity生成表定义时使用。(也就是说,如果DB中表已经建好,该属性没有必要使用。)
table
表示当映射多个表时,指定表的表中的字段。默认值为主表的表名。
length
表示字段的长度,当字段的类型为varchar时,该属性才有效,默认为255个字符。
precision和scale
precision属性和scale属性表示精度,当字段类型为double时,precision表示数值的总长度,scale表示小数点所占的位数。
# @Column可以标注在属性前或getter方法前
@Column标注在属性前(建议使用这一种方式)
@Temporal
数据库的字段类型有date、time、datetime
而Temporal注解的作用就是帮Java的Date类型进行格式化,一共有三种注解值:
第一种:@Temporal(TemporalType.DATE)——>实体类会封装成日期“yyyy-MM-dd”的 Date类型。
第二种:@Temporal(TemporalType.TIME)——>实体类会封装成时间“hh-MM-ss”的 Date类型。
第三种:@Temporal(TemporalType.TIMESTAMP)——>实体类会封装成完整的时间“yyyy-MM-dd hh:MM:ss”的 Date类型。
注解方式有两种:
写在字段上:
@Temporal(TemporalType.TIMESTAMP)
private Date birthday;
写在 getXxx方法上:
@Temporal(TemporalType.DATE)
@Column(name = "birthday", length = 10)
public Date getBirthday() {
return this.birthday;
}