使用Hibernate在CQRS读取模型中进行快速开发
在這篇文章中,我將分享一些在CQRS讀取模型中使用Hibernate工具進行快速開發的技巧。
為什么要休眠?
休眠非常流行。 從外觀上看,它也很容易,而從內部看,它卻相當復雜。 它可以很容易地開始使用,而無需進行深入了解,濫用和發現問題,如果為時已晚。 由于所有這些原因,這幾天真是臭名昭著。
但是,它仍然是一項堅實而成熟的技術。 經過實戰測試,功能強大,文檔完善,并且可以解決許多常見問題。 它可以使您*非常*高效。 如果包括工具和庫,則更多。 最后,只要您知道自己在做什么,它就是安全的。
自動模式生成
使SQL模式與Java類定義保持同步相當麻煩。 在最佳情況下,這是非常繁瑣且耗時的活動。 錯誤的機會很多。
Hibernate帶有模式生成器(hbm2ddl),但其“本機”形式在生產中使用有限。 創建SessionFactory時,它只能驗證架構,嘗試更新或導出架構。 幸運的是,該實用程序可用于自定義編程用途。
我們進一步走了一步,并將其與CQRS預測集成在一起。 運作方式如下:
- 當投影過程線程啟動時,請驗證數據庫模式是否與Java類定義匹配。
- 如果不是,請刪除該架構并重新導出(使用hbm2ddl)。 重新啟動投影,從一開始就重新處理事件存儲。 使投影從一開始就開始。
- 如果匹配,則繼續從當前狀態更新模型。
由于這個原因,在很多時候,您幾乎不必手動輸入帶有表定義的SQL。 它使開發速度大大加快。 這類似于使用hbm2ddl.auto = create-drop 。 但是, 在視圖模型中使用它意味著它實際上不會丟失數據 (這在事件存儲中是安全的)。 而且,它足夠聰明,僅在實際更改架構時才重新創建架構-與創建-放置策略不同。
保留數據并避免不必要的重新啟動不僅會縮短開發周期。 它還可能使其在生產中可用。 至少在某些條件下,請參見下文。
有一個警告:并非所有架構更改都會使Hibernate驗證失敗。 一個示例是更改字段長度–只要是varchar或文本,驗證就可以通過而不受限制。 另一個未發現的變化是可空性。
這些問題可以通過手動重新啟動投影來解決(請參見下文)。 另一種可能性是擁有一個不存儲數據的偽實體,但對其進行了修改以觸發自動重啟。 它可能只有一個名為schemaVersion字段,每次架構更改時, @Column(name = "v_4") schemaVersion @Column(name = "v_4")批注(由開發人員)都會更新。
實作
實施方法如下:
public class HibernateSchemaExporter {private final EntityManager entityManager;public HibernateSchemaExporter(EntityManager entityManager) {this.entityManager = entityManager;}public void validateAndExportIfNeeded(List<Class> entityClasses) {Configuration config = getConfiguration(entityClasses);if (!isSchemaValid(config)) {export(config);}}private Configuration getConfiguration(List<Class> entityClasses) {SessionFactoryImplementor sessionFactory = (SessionFactoryImplementor) getSessionFactory();Configuration cfg = new Configuration();cfg.setProperty("hibernate.dialect", sessionFactory.getDialect().toString());// Do this when using a custom naming strategy, e.g. with Spring Boot:Object namingStrategy = sessionFactory.getProperties().get("hibernate.ejb.naming_strategy");if (namingStrategy instanceof NamingStrategy) {cfg.setNamingStrategy((NamingStrategy) namingStrategy);} else if (namingStrategy instanceof String) {try {log.debug("Instantiating naming strategy: " + namingStrategy);cfg.setNamingStrategy((NamingStrategy) Class.forName((String) namingStrategy).newInstance());} catch (ReflectiveOperationException ex) {log.warn("Problem setting naming strategy", ex);}} else {log.warn("Using default naming strategy");}entityClasses.forEach(cfg::addAnnotatedClass);return cfg;}private boolean isSchemaValid(Configuration cfg) {try {new SchemaValidator(getServiceRegistry(), cfg).validate();return true;} catch (HibernateException e) {// Yay, exception-driven flow!return false;}}private void export(Configuration cfg) {new SchemaExport(getServiceRegistry(), cfg).create(false, true);clearCaches(cfg);}private ServiceRegistry getServiceRegistry() {return getSessionFactory().getSessionFactoryOptions().getServiceRegistry();}private void clearCaches(Configuration cfg) {SessionFactory sf = entityManager.unwrap(Session.class).getSessionFactory();Cache cache = sf.getCache();stream(cfg.getClassMappings()).forEach(pc -> {if (pc instanceof RootClass) {cache.evictEntityRegion(((RootClass) pc).getCacheRegionName());}});stream(cfg.getCollectionMappings()).forEach(coll -> {cache.evictCollectionRegion(((Collection) coll).getCacheRegionName());});}private SessionFactory getSessionFactory() {return entityManager.unwrap(Session.class).getSessionFactory();} }該API看起來過時且繁瑣。 似乎沒有辦法從現有的SessionFactory提取Configuration 。 這只是用來創建工廠并扔掉的東西。 我們必須從頭開始重新創建它。 以上是我們需要的所有內容,以使其與Spring Boot和L2緩存一起正常工作。
重新開始投影
我們還實現了一種手動執行此類重新初始化的方法,在管理控制臺中以按鈕形式顯示。 當有關投影的某些內容發生更改但不涉及修改架構時,它會派上用場。 例如,如果值的計算/格式不同,但仍是文本字段,則可以使用此機制來手動重新處理歷史記錄。 另一個用例是修復錯誤。
生產用途?
在開發過程中,我們一直在成功使用這種機制。 它使我們可以通過僅更改Java類而不用擔心表定義來自由地修改模式。 由于與CQRS結合使用,我們甚至可以維護長期運行的演示或試點客戶實例。 數據始終在事件存儲區中是安全的。 我們可以逐步開發讀取模型架構,并將更改自動部署到正在運行的實例中,而不會丟失數據或手動編寫SQL遷移腳本。
顯然,這種方法有其局限性。 僅在很小的情況下或事件可以足夠快速地處理時,才可以在隨機的時間點重新處理整個事件存儲。
否則,可以使用SQL遷移腳本解決遷移問題,但是它有其局限性。 這通常是冒險且困難的。 可能會很慢。 最重要的是,如果更改較大并且涉及以前未包含在讀取模型中(但事件中可用)的數據,則根本不選擇使用SQL腳本。
更好的解決方案是將投影(帶有新代碼)指向新數據庫。 讓它重新處理事件日志。 當它趕上來時,請測試視圖模型,重定向流量并丟棄舊實例。 提出的解決方案也與此方法完美配合。
翻譯自: https://www.javacodegeeks.com/2015/10/rapid-development-with-hibernate-in-cqrs-read-models.html
總結
以上是生活随笔為你收集整理的使用Hibernate在CQRS读取模型中进行快速开发的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Apache Spark中实现的MapR
- 下一篇: ddos攻击接口(ddos口攻击服务器)