前言
最近在用 Python
的 SQLAlchemy
庫時(一個類似於 Hibernate
的 ORM
框架),發現它的 Events
事件還挺好用。
簡單說就是當某張表的數據發生變化(曾、刪、改)時會有一個事件回調,這樣一些埋點之類的需求都可以實現在這裏,同時和業務代碼完全解耦,維護起來也很方便。
例如當訂單狀態發生變化需要發異步通知這樣的需求也可以利用這個實現。
根據我之前使用 Mybatis
的經驗,好像沒怎麼注意有這個功能,查閱了下發現 Hibernate
是支持的,只是我用得也少,所以也沒怎麼在意。
逐漸偏離主題。。。
說這些的主要原因是我打算爲之前寫的 cicada (輕量的 http 框架)加一個數據庫操作包,也實現類似的功能。
示例
最終的使用效果如下:
第一版本還比較粗糙,但功能都具備。
第一步:需要實現一個初始化接口,該接口會在應用初始化的時候執行。
緊接着我們需要定義一個 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 ;
}
它所對應的表結構如下:
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
監聽機制的全過程,其實可以看出並沒有它名稱那樣看起來高大上,當然本身實現也比較簡單。
同時也不止這一種實現方式,例如:
cglib
javassist
ASM
etc..
他們的具體實現及優劣就不在本文探討了,感興趣的後續我會將這個功能用這幾種方式實現一遍。
同時動態代理的應用也不止於此,比如:
RPC
中無感知的遠程調用。Spring
中的AOP
、攔截器等。
後續會繼續完善這個 ORM
庫,甚至可以獨立出來作爲一個小巧的數據庫工具也未嘗不可。
相關源碼見此處:https://github.com/TogetherOS/cicada
你的點贊與分享是對我最大的支持
更多推薦內容
↓↓↓
《爲自己搭建一個分佈式 IM(即時通訊) 系統》