关于Java中集成mysql(springboot)处理数据创建时间和最后更新时间的总结

关于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 提供了相应的时间注解,只需要两步配置,就可以帮助开发者快速实现这方面的功能。

  1. 在实体类上加上注解 @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;
    }

MySQL数据库自动添加创建时间、更新时间(mysql5.7及更高版本推荐使用该方法)

一、MySQL5.7及以上版本(也适用于MySQL8.0+)方法

下列给出了三种不同时期修改表的SQL,便于各位看官快捷开发(CV大法)

1、新建表
自动获取创建时间:timestamp not null default CURRENT_TIMESTAMP

自动获取更新时间:timestamp not null default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP

create table test(
  id integer not null auto_increment primary key,
  name varchar(20) not null ,
  create_time timestamp not null default CURRENT_TIMESTAMP COMMENT '创建时间',
  update_time timestamp not null default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP COMMENT '更新时间');

2、修改表字段 (快捷复制)
ALTER TABLE 表名 MODIFY [修改字段名] <数据类型> [约束条件];

 ALTER TABLE test MODIFY create_time timestamp not null default CURRENT_TIMESTAMP COMMENT '创建时间';
 ALTER TABLE test MODIFY update_time timestamp not null default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP COMMENT '更新时间';

3、新增表字段(快捷复制)
ALTER TABLE <表名> ADD <新字段名> <数据类型> [约束条件];

 ALTER TABLE test ADD create_time timestamp not null default CURRENT_TIMESTAMP COMMENT '创建时间';
 ALTER TABLE test ADD update_time timestamp not null default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP COMMENT '更新时间';

参考原文链接:https://blog.csdn.net/A1924/article/details/117074646

二、我这里用的是Navicat,直接在字段属性修改

这个适用于5.7及更高版本,5.7之前的版本会出现问题~~

当前时间戳:*CURRENT_TIMESTAMP*

一般情况下,创建时间、修改时间都是用datetime类型;
datetime类型在没有默认值的情况下是不会自动赋值的;
在开发阶段,不想传这两个值,但又想不为null,所以就需要用到CURRENT_TIMESTAMP这个参数。

#创建时间:
一般是在创建数据时才会赋值,使用SQL的话,就需要在字段上添加默认值: DEFAULT CURRENT_TIMESTAMP(0) 
使用Navicat的情况下,就需要添加CURRENT_TIMESTAMP为默认值。
#修改时间:
一般是在创建数据、修改数据时才会赋值,使用SQL的话,就需要在字段上添加默认值:
DEFAULT CURRENT_TIMESTAMP(0) ON UPDATE CURRENT_TIMESTAMP(0)
使用Navicat的情况下,就需要添加CURRENT_TIMESTAMP为默认值,并且标记“ 根据当前时间戳更新 ”,
这样才能实现只要数据发生变化就会自动修改时间

创建时间

修改时间

数据表结构

 CREATE TABLE `sys_user`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `create_time` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP(0) COMMENT '创建时间',
  `update_time` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP(0) ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '修改时间',
  PRIMARY KEY (`id`) USING BTREE
) AUTO_INCREMENT = 1
posted @ 2020-04-14 12:16  IT界~挑山工  阅读(4932)  评论(0编辑  收藏  举报