SpringBoot使用Hibernate攔截器實(shí)現(xiàn)時(shí)間自動(dòng)注入的操作代碼
最近項(xiàng)目有個(gè)改動(dòng):另一個(gè)系統(tǒng)根據(jù)更新時(shí)間戳來(lái)拉取本系統(tǒng)數(shù)據(jù)。這就要求基本上所有的數(shù)據(jù)表都要及時(shí)更新時(shí)間戳。以前的方式是在新增數(shù)據(jù)或者修改數(shù)據(jù)時(shí)手動(dòng)調(diào)用setProperty(TimeStamp),因?yàn)闆](méi)有用到這兩個(gè)字段加上每個(gè)人的編碼習(xí)慣不同,有時(shí)候沒(méi)設(shè)置createTime或者updateTime,可能存在遺漏,導(dǎo)致數(shù)據(jù)庫(kù)中有的時(shí)間戳默認(rèn)值0。
前提:本系統(tǒng)時(shí)間戳在數(shù)據(jù)庫(kù)類型為int,長(zhǎng)度為int默認(rèn)值,存儲(chǔ)示例:1665295775,Java類型Integer,單位: /s
因?yàn)闆](méi)怎么使用過(guò)Hibernate框架,就想Hibernate有沒(méi)有MybatisPlus一樣的自動(dòng)填充功能呢?查了一下還真有日期相關(guān)的注解:@Temporal(TemporalType.XXX),但是對(duì)應(yīng)Java的類型為:java.sql.Date,java.sql.Time,java.sql.Timestamp
,數(shù)據(jù)庫(kù)的類型為時(shí)間相關(guān)的類型:date,time,datetime,timestamp
,在不改動(dòng)歷史數(shù)據(jù)表字段類型的情況下,只能尋求其它方式。
Hibernate攔截器:
- Interceptor接口:
允許用戶代碼檢查和/或更改屬性值。檢查發(fā)生在屬性值寫(xiě)入之前和從數(shù)據(jù)庫(kù)中讀取之后。 SessionFactory可能有一個(gè)Interceptor實(shí)例,或者可能為每個(gè)Session指定一個(gè)新實(shí)例。無(wú)論使用哪種方法,如果Session要可序列化,則攔截器必須是可序列化的。這意味著SessionFactory范圍的攔截器應(yīng)該實(shí)現(xiàn)readResolve() 。 Session不能從回調(diào)中調(diào)用(回調(diào)也不能導(dǎo)致集合或代理被延遲初始化)。與其直接實(shí)現(xiàn)此接口,不如繼承EmptyInterceptor并僅覆蓋感興趣的回調(diào)方法。
/** 在刷新期間檢測(cè)到對(duì)象臟時(shí)調(diào)用。攔截器可能會(huì)修改檢測(cè)到的currentState ,這將被傳播到數(shù)據(jù)庫(kù)和持久對(duì)象。 請(qǐng)注意,并非所有刷新都以與數(shù)據(jù)庫(kù)的實(shí)際同步結(jié)束,在這種情況下,新的currentState將傳播到對(duì)象,但不一定(立即)傳播到數(shù)據(jù)庫(kù)。 強(qiáng)烈建議攔截器不要修改previousState 。 */ onFlushDirty(): /** 在保存對(duì)象之前調(diào)用。攔截器可能會(huì)修改狀態(tài),該狀態(tài)將用于 SQL INSERT并傳播到持久對(duì)象。 */ onSave():
那我們?nèi)mptyInterceptor看一下。
- EmptyInterceptor類:
一個(gè)什么都不做的攔截器??捎米鲬?yīng)用程序定義的自定義攔截器的基類 。
參看上面接口的方法描述,我們只需要繼承EmptyInterceptor并重寫(xiě)onSave()和onFlushDirty()兩個(gè)方法,來(lái)分別實(shí)現(xiàn)保存(SQL INSERT)
和更新(SQL UPDATE)
即可。Hibernate在檢測(cè)到save和update操作時(shí)先執(zhí)行自定義邏輯。
代碼實(shí)現(xiàn)
看完了攔截器相關(guān),來(lái)實(shí)現(xiàn)一下代碼,如下:
為了控制每個(gè)DDO的行為,可以設(shè)計(jì)一個(gè)BaseEntityControl接口
/** * 數(shù)據(jù)的頂層接口 */ public interface BaseEntityControl{ /** 可以加入其它邏輯 / /** * 控制是否需要自動(dòng)寫(xiě)入createTime和updateTime * @return */ default boolean writeTimeStamp() { return true; } }
import com.xx.xxx.BaseEntityControl; import java.io.Serializable; /** * Interceptor for entity audits. */ public class AuditInterceptor extends EmptyInterceptor { /** * Serial version UID */ private static final long serialVersionUID = 6892420119984901561L; /** * @see org.hibernate.Interceptor#onFlushDirty(Object, Serializable, Object[], Object[], String[], Type[]) */ @Override public boolean onFlushDirty(final Object entity, final Serializable id, final Object[] currentState, final Object[] previousState, final String[] propertyNames, final Type[] types) { if (entity instanceof BaseEntityControl) { if (!((BaseEntityControl) entity).writeTimeStamp()) { return false; } for (int i = 0; i < propertyNames.length; i++) { if ("updateTime".equals(propertyNames[i])) { currentState[i] = (int) (System.currentTimeMillis() / 1000); return true; } } } return false; } /** * @see org.hibernate.Interceptor#onSave(Object, Serializable, Object[], String[], Type[]) */ @Override public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) { if (entity instanceof BaseEntityControl) { if (!((BaseEntityControl) entity).writeTimeStamp()) { return false; } boolean crtModify = false; boolean uptModify = false; int i = 0; while (true) { if (i >= propertyNames.length) { if (crtModify || uptModify) { return true; } break; } if ("createTime".equals(propertyNames[i])) { crtModify = true; state[i] = (int) (System.currentTimeMillis() / 1000L); } if ("updateTime".equals(propertyNames[i])) { uptModify = true; state[i] = (int) (System.currentTimeMillis() / 1000L); } ++i; } } return false; } }
將自定義Interceptor配置到session factory
在session factory配置類中將
public class xxxConfig { ... public FactoryBean<SessionFactory> getSessionFactory() { LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean(); sessionFactoryBean.setEntityInterceptor(new AuditInterceptor()); ... } ... }
對(duì)比測(cè)試
4.0.9:加入攔截器之前
4.3.1:加入攔截器之后
單個(gè)對(duì)象20個(gè)property情況下:
到此這篇關(guān)于SpringBoot使用Hibernate攔截器實(shí)現(xiàn)時(shí)間自動(dòng)注入的操作代碼的文章就介紹到這了,更多相關(guān)SpringBoot使用Hibernate攔截器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java調(diào)用WebService服務(wù)的四種方法總結(jié)
WebService是一種跨編程語(yǔ)言、跨操作系統(tǒng)平臺(tái)的遠(yuǎn)程調(diào)用技術(shù),已存在很多年了,很多接口也都是通過(guò)WebService方式來(lái)發(fā)布的,下面這篇文章主要給大家介紹了關(guān)于java調(diào)用WebService服務(wù)的四種方法,需要的朋友可以參考下2021-11-11LoggingEventAsyncDisruptorAppender類執(zhí)行流程源碼解讀
這篇文章主要介紹了LoggingEventAsyncDisruptorAppender類執(zhí)行流程源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12詳解IDEA啟動(dòng)多個(gè)微服務(wù)的配置方法
這篇文章主要介紹了詳解IDEA啟動(dòng)多個(gè)微服務(wù)的配置方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-01-01使用純Java實(shí)現(xiàn)一個(gè)WebSSH項(xiàng)目的示例代碼
這篇文章主要介紹了使用純Java實(shí)現(xiàn)一個(gè)WebSSH項(xiàng)目,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03javax.management.InvalidApplicationException的問(wèn)題解決
javax.management.InvalidApplicationException是與Java Management Extensions (JMX) API相關(guān)的一個(gè)常見(jiàn)異常,本文主要介紹了javax.management.InvalidApplicationException的問(wèn)題解決,感興趣的可以了解一下2024-08-08使用多個(gè)servlet時(shí)Spring security需要指明路由匹配策略問(wèn)題
這篇文章主要介紹了使用多個(gè)servlet時(shí)Spring security需要指明路由匹配策略問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08Java實(shí)現(xiàn)手寫(xiě)一個(gè)線程池的示例代碼
線程池技術(shù)想必大家都不陌生把,相信在平時(shí)的工作中沒(méi)有少用,而且這也是面試頻率非常高的一個(gè)知識(shí)點(diǎn),那么大家知道它的實(shí)現(xiàn)原理和細(xì)節(jié)嗎?本文就來(lái)通過(guò)手寫(xiě)一個(gè)簡(jiǎn)單的線程池框架,去掌握線程池的基本原理,感興趣的可以學(xué)習(xí)一下2022-10-10