關於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;
}