原文連接html
最近在用 Python
的 SQLAlchemy
庫時(一個相似於 Hibernate
的 ORM
框架),發現它的 Events
事件還挺好用。java
簡單說就是當某張表的數據發生變化(曾、刪、改)時會有一個事件回調,這樣一些埋點之類的需求均可以實如今這裏,同時和業務代碼徹底解耦,維護起來也很方便。git
例如當訂單狀態發生變化須要發異步通知這樣的需求也能夠利用這個實現。github
根據我以前使用 Mybatis
的經驗,好像沒怎麼注意有這個功能,查閱了下發現 Hibernate
是支持的,只是我用得也少,因此也沒怎麼在乎。sql
逐漸偏離主題。。。
說這些的主要緣由是我打算爲以前寫的 cicada (輕量的 http 框架)加一個數據庫操做包,也實現相似的功能。數據庫
最終的使用效果以下:oracle
初版本還比較粗糙,但功能都具有。
第一步:須要實現一個初始化接口,該接口會在應用初始化的時候執行。框架
緊接着咱們須要定義一個 Model
:異步
@Data @OriginName("user") @ToString public class User extends Model { @PrimaryId private Integer id ; private String name ; private String password ; @FieldName(value = "city_id") private Integer cityId ; private String description ; }
它所對應的表結構以下:ide
CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(50) DEFAULT NULL, `password` varchar(100) DEFAULT NULL, `description` varchar(100) DEFAULT NULL, `roleId` int(11) DEFAULT NULL COMMENT '角色ID', `city_id` int(11) DEFAULT NULL, PRIMARY KEY (`id`) )
當須要查詢數據時:
即可以這樣訪問數據庫。
當須要更新數據時:
在初始化 DBHandle
時指定一個回調接口(也就是這裏的 UserUpdateListener
),即可以在修改數據的時候拿到本次修改的數據實體。
@Slf4j public class UserUpdateListener implements DataChangeListener { @Override public void listener(Object obj) { log.info("user update data={}", obj.toString()); } }
同時咱們能夠在控制檯看到數據修改時的回調結果:
這樣就實現了文初所提到的功能,即可以實現一些數據變化後須要執行的業務邏輯。
下面重點來看看這個功能的實現過程;其實經過生成 DBHandle
(數據庫增刪改的接口)實例的 API
即可以看出些端倪。
DBHandle handle = (DBHandle) new HandleProxy(DBHandle.class).getInstance(new UserSaveListener());
DBHandel
雖然是個接口,可是它並非使用一個實現類來實現的,而是經過代理生成。
那經過代理生成比直接實例化實現類有啥好處呢?
舉個例子,好比如今你想買一個新手機。
第一種方式能夠直接在官方旗艦店買一個標配的手機,沒有額外的東西只有一個手機。
固然你也能夠在某些第三方經銷商那裏購買帶套餐的,好比套餐一
在標配的基礎上多了保護殼、貼膜
之類的附加屬性。
這個經銷商就相似於咱們這裏的代理類,他能夠在原有實現的基礎上新增一些東西,至於新增什麼全看你本身的須要了。
而之因此叫動態代理,也是由於這個代理類是在程序運行過程當中動態建立的,在編譯過程當中並不能肯定這個類的全限定名。
下面來看看這個代理類是如何生成的:
主要利用 JDK
自帶的 API
實現的,具體參數能夠直接參考官方文檔:
https://docs.oracle.com/javase/8/docs/technotes/guides/reflection/proxy.html
總之這樣即可以建立一個 DBHandler
接口的代理對象,而真正的代理過程是在 InvocationHandler#invoke()
函數中實現的:
這裏的實現也是很是簡單,在實現完代理對象的業務邏輯後便回調咱們傳入的事件接口,其中的參數即是當前的數據庫 Model
實體對象。
不過須要注意的是,這個事件回調和業務線程是同一個,因此寫在這裏的邏輯建議都爲異步(Hibernate 和 SQLAlchemy 都存在這個狀況)。
以上即是整個動態代理實現 ORM
監聽機制的全過程,其實能夠看出並無它名稱那樣看起來高大上,固然自己實現也比較簡單。
同時也不止這一種實現方式,例如:
etc..
他們的具體實現及優劣就不在本文探討了,感興趣的後續我會將這個功能用這幾種方式實現一遍。
同時動態代理的應用也不止於此,好比:
RPC
中無感知的遠程調用。Spring
中的 AOP
、攔截器等。後續會繼續完善這個 ORM
庫,甚至能夠獨立出來做爲一個小巧的數據庫工具也何嘗不可。
相關源碼見此處:
https://github.com/TogetherOS/cicada
你的點贊與分享是對我最大的支持