javascript
将jOOQ与Spring结合使用:配置
我遇到了由ORM引起的性能問(wèn)題。 盡管我不得不承認(rèn)大多數(shù)這些問(wèn)題確實(shí)是由您造成的,但是我開(kāi)始認(rèn)為在只讀操作中使用ORM是不值得的。
我開(kāi)始尋找實(shí)現(xiàn)這些操作的替代方法。
這就是我遇到j(luò)OOQ的方式 ,它指出:
jOOQ從您的數(shù)據(jù)庫(kù)生成Java代碼,并允許您通過(guò)其流暢的API構(gòu)建類(lèi)型安全的SQL查詢(xún)。
這看起來(lái)非常有趣,因此我決定給jOOQ一炮而紅,并與您分享我的發(fā)現(xiàn)。
這篇博客文章是我的“將jOOQ與Spring結(jié)合使用”系列的第一部分,它描述了如何獲取所需的依賴(lài)關(guān)系并配置應(yīng)用程序的應(yīng)用程序上下文。
讓我們開(kāi)始吧。
使用Maven獲取所需的依賴(lài)關(guān)系
我們的應(yīng)用程序的依賴(lài)項(xiàng)是:
- Spring Framework 3.2.6。 此時(shí),我們的示例使用aop , bean , core , context , context-support , jdbc和tx模塊。
- cglib 3.1。
- BoneCP 0.8.0。 我們使用BoneCP作為示例應(yīng)用程序的連接池。
- jOOQ 3.2.2。
- H2 1.3.174。 我們使用H2作為示例應(yīng)用程序的數(shù)據(jù)庫(kù)。
如果您想獲得有關(guān)Spring Framework模塊的更多信息,請(qǐng)閱讀Spring Framework Reference Documentation的1.2節(jié) 。
該示例應(yīng)用程序使用Spring Framework 3.2.6而不是4.0的原因是,目前spring-test-dbunit與Spring Framework 4.0不兼容 。
pom.xml文件的相關(guān)部分如下所示:
<dependency><groupId>org.springframework</groupId><artifactId>spring-aop</artifactId><version>3.2.6.RELEASE</version> </dependency> <dependency><groupId>org.springframework</groupId><artifactId>spring-beans</artifactId><version>3.2.6.RELEASE</version> </dependency> <dependency><groupId>org.springframework</groupId><artifactId>spring-core</artifactId><version>3.2.6.RELEASE</version> </Dependency> <dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>3.2.6.RELEASE</version> </dependency> <dependency><groupId>org.springframework</groupId><artifactId>spring-context-support</artifactId><version>3.2.6.RELEASE</version> </dependency> <dependency><groupId>org.springframework</groupId><artifactId>spring-expression</artifactId><version>3.2.6.RELEASE</version> </dependency> <dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>3.2.6.RELEASE</version> </dependency> <dependency><groupId>org.springframework</groupId><artifactId>spring-tx</artifactId><version>3.2.6.RELEASE</version> </dependency><dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.1</version> </dependency><dependency><groupId>com.jolbox</groupId><artifactId>bonecp</artifactId><version>0.8.0.RELEASE</version> </dependency><dependency><groupId>org.jooq</groupId><artifactId>jooq</artifactId><version>3.2.2</version> </dependency><dependency><groupId>com.h2database</groupId><artifactId>h2</artifactId><version>1.3.174</version> </dependency>- 此博客文章的示例應(yīng)用程序還具有其他依賴(lài)性。 通過(guò)查看pom.xml文件,可以看到完整的依賴(lài)項(xiàng)列表。
讓我們繼續(xù)研究如何將jOOQ拋出的異常轉(zhuǎn)換為Spring DataAccessExceptions 。
將jOOQ異常轉(zhuǎn)換為Spring DataAccessExceptions
為什么我們要將jOOQ拋出的異常轉(zhuǎn)換為Spring DataAccessExceptions ?
這樣做的一個(gè)原因是,我們希望我們的集成工作與Spring Framework的DAO支持相同。 這種支持的重要組成部分是一致的異常層次結(jié)構(gòu) :
Spring提供了從技術(shù)特定的異常(如SQLException)到其自己的異常類(lèi)層次結(jié)構(gòu)(以DataAccessException作為根異常)的便捷轉(zhuǎn)換。 這些異常包裝了原始異常,因此永遠(yuǎn)不會(huì)冒任何可能丟失任何錯(cuò)誤信息的風(fēng)險(xiǎn)。
換句話說(shuō),如果我們希望我們的應(yīng)用程序是“好公民”,那么確保我們的配置將jOOQ拋出的異常轉(zhuǎn)換為Spring DataAccessExceptions是有意義的。
我們可以按照以下步驟創(chuàng)建提供此功能的組件:
JOOQToSpringExceptionTransformer類(lèi)的源代碼如下所示:
import org.jooq.ExecuteContext; import org.jooq.SQLDialect; import org.jooq.impl.DefaultExecuteListener; import org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator; import org.springframework.jdbc.support.SQLExceptionTranslator; import org.springframework.jdbc.support.SQLStateSQLExceptionTranslator;public class JOOQToSpringExceptionTransformer extends DefaultExecuteListener {@Overridepublic void exception(ExecuteContext ctx) {SQLDialect dialect = ctx.configuration().dialect();SQLExceptionTranslator translator = (dialect != null)? new SQLErrorCodeSQLExceptionTranslator(dialect.name()): new SQLStateSQLExceptionTranslator();ctx.exception(translator.translate("jOOQ", ctx.sql(), ctx.sqlException()));} }這不是我的主意 。 我從亞當(dāng)·澤爾(Adam Zell)的要旨(Gist)中得到了這個(gè)想法。
補(bǔ)充閱讀:
- jOOQ用戶手冊(cè)的第4.2.5節(jié)“自定義ExecuteListeners”
- Spring框架參考文檔的第14.2.3節(jié)“ SQLExceptionTranslator”
我們的工作還沒(méi)有完成。 讓我們放在一起,通過(guò)配置示例應(yīng)用程序的應(yīng)用程序上下文來(lái)完成我們的工作。
配置應(yīng)用程序上下文
本節(jié)說(shuō)明如何使用Java配置來(lái)配置應(yīng)用程序的應(yīng)用程序上下文。
讓我們開(kāi)始創(chuàng)建一個(gè)屬性文件,其中包含示例應(yīng)用程序的配置。
實(shí)際應(yīng)用程序的構(gòu)建過(guò)程基于Maven配置文件。 這確保了我們可以在不同的環(huán)境中使用不同的配置。 您可以閱讀我的博客文章“ 使用Maven創(chuàng)建配置文件特定的配置文件”,以獲取有關(guān)此內(nèi)容的更多信息。
創(chuàng)建屬性文件
我們可以按照以下步驟創(chuàng)建屬性文件:
application.properties文件如下所示:
#Database Configuration db.driver=org.h2.Driver db.url=jdbc:h2:target/jooq-example db.username=sa db.password=#jOOQ Configuration jooq.sql.dialect=H2#DB Schema db.schema.script=schema.sql- SQLDialect枚舉的Javadoc指定jOOQ支持的數(shù)據(jù)庫(kù)方言列表。
讓我們繼續(xù)研究如何使用Java配置來(lái)配置應(yīng)用程序的應(yīng)用程序上下文。
創(chuàng)建配置類(lèi)
我們可以按照以下步驟配置應(yīng)用程序的應(yīng)用程序上下文:
PersistenceContext類(lèi)的源代碼如下所示:
import com.jolbox.bonecp.BoneCPDataSource; import org.jooq.SQLDialect; import org.jooq.impl.DataSourceConnectionProvider; import org.jooq.impl.DefaultConfiguration; import org.jooq.impl.DefaultDSLContext; import org.jooq.impl.DefaultExecuteListenerProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import org.springframework.core.env.Environment; import org.springframework.core.io.ClassPathResource; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy; import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy; import org.springframework.jdbc.datasource.init.DataSourceInitializer; import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator; import org.springframework.transaction.annotation.EnableTransactionManagement;import javax.sql.DataSource;@Configuration @ComponentScan({"net.petrikainulainen.spring.jooq.todo"}) @EnableTransactionManagement @PropertySource("classpath:application.properties") public class PersistenceContext {@Autowiredprivate Environment env;@Bean(destroyMethod = "close")public DataSource dataSource() {BoneCPDataSource dataSource = new BoneCPDataSource();dataSource.setDriverClass(env.getRequiredProperty("db.driver"));dataSource.setJdbcUrl(env.getRequiredProperty("db.url"));dataSource.setUsername(env.getRequiredProperty("db.username"));dataSource.setPassword(env.getRequiredProperty("db.password"));return dataSource;}@Beanpublic LazyConnectionDataSourceProxy lazyConnectionDataSource() {return new LazyConnectionDataSourceProxy(dataSource());}@Beanpublic TransactionAwareDataSourceProxy transactionAwareDataSource() {return new TransactionAwareDataSourceProxy(lazyConnectionDataSource());}@Beanpublic DataSourceTransactionManager transactionManager() {return new DataSourceTransactionManager(lazyConnectionDataSource());}@Beanpublic DataSourceConnectionProvider connectionProvider() {return new DataSourceConnectionProvider(transactionAwareDataSource());}@Beanpublic JOOQToSpringExceptionTransformer jooqToSpringExceptionTransformer() {return new JOOQToSpringExceptionTransformer();}@Beanpublic DefaultConfiguration configuration() {DefaultConfiguration jooqConfiguration = new DefaultConfiguration();jooqConfiguration.set(connectionProvider());jooqConfiguration.set(new DefaultExecuteListenerProvider(jooqToSpringExceptionTransformer()));String sqlDialectName = env.getRequiredProperty("jooq.sql.dialect");SQLDialect dialect = SQLDialect.valueOf(sqlDialectName);jooqConfiguration.set(dialect);return jooqConfiguration;}@Beanpublic DefaultDSLContext dsl() {return new DefaultDSLContext(configuration());}@Beanpublic DataSourceInitializer dataSourceInitializer() {DataSourceInitializer initializer = new DataSourceInitializer();initializer.setDataSource(dataSource());ResourceDatabasePopulator populator = new ResourceDatabasePopulator();populator.addScript(new ClassPathResource(env.getRequiredProperty("db.schema.script")));initializer.setDatabasePopulator(populator);return initializer;} }如果要使用XML配置文件配置應(yīng)用程序上下文,則示例應(yīng)用程序也具有有效的XML配置文件 。
學(xué)分:
- 此配置基于jOOQ用戶手冊(cè)的3.4.3節(jié) 。
- Sergey Epik發(fā)布的消息幫助我弄清楚了如何向jOOQ配置添加自定義ExecuteListeners。
- Peter Ertl給了我一個(gè)使用LazyConnectionDataSourceProxy的想法。
我們?nèi)绾沃来伺渲糜行?#xff1f; 這是一個(gè)好問(wèn)題。 我們將在下一節(jié)中討論。
這真的有效嗎?
當(dāng)我開(kāi)始研究如何確保用jOOQ創(chuàng)建的數(shù)據(jù)庫(kù)查詢(xún)參與Spring管理的事務(wù)時(shí),我注意到這不是一個(gè)容易解決的問(wèn)題 。
這篇博客文章的示例應(yīng)用程序具有一些集成測(cè)試,這些測(cè)試可以確保事務(wù)(提交和回滾)在非常簡(jiǎn)單的場(chǎng)景中正常工作。 但是,使用此博客文章中描述的解決方案時(shí),必須考慮兩件事:
1.使用jOOQ創(chuàng)建的所有數(shù)據(jù)庫(kù)查詢(xún)必須在事務(wù)內(nèi)執(zhí)行。
TransactionAwareDataSourceProxy類(lèi)的Javadoc指出:
委托DataSourceUtils自動(dòng)參與線程綁定的事務(wù),例如由DataSourceTransactionManager管理。 返回的Connection上的getConnection調(diào)用和close調(diào)用將在事務(wù)中正常運(yùn)行,即始終在事務(wù)Connection上運(yùn)行。 如果不在事務(wù)內(nèi),則將應(yīng)用正常的DataSource行為。
換句話說(shuō),如果您執(zhí)行多個(gè)復(fù)雜的操作而沒(méi)有事務(wù),則jOOQ將為每個(gè)操作使用不同的連接。 這可能會(huì)導(dǎo)致競(jìng)賽條件錯(cuò)誤。
在閱讀Ben Manes撰寫(xiě)的這篇評(píng)論時(shí),我注意到了這個(gè)問(wèn)題。
2. Javadoc不建議使用TransactionAwareDataSourceProxy 。
TransactionAwareDataSourceProxy類(lèi)的Javadoc具有如下部分:
該代理允許數(shù)據(jù)訪問(wèn)代碼與普通JDBC API一起使用,并且仍然參與Spring管理的事務(wù),這與J2EE / JTA環(huán)境中的JDBC代碼類(lèi)似。 但是,如果可能,即使沒(méi)有目標(biāo)DataSource的代理,也可以使用Spring的DataSourceUtils , JdbcTemplate或JDBC操作對(duì)象來(lái)獲取事務(wù)參與,從而避免了首先定義此類(lèi)代理的麻煩。
這是一個(gè)模糊的評(píng)論,因?yàn)樗鼪](méi)有解釋為什么我們不應(yīng)該使用它。 Adam Zell建議 ,由于該類(lèi)使用反射,因此使用反射可能會(huì)導(dǎo)致性能問(wèn)題。
如果您遇到性能問(wèn)題,則可能要使用
亞當(dāng)·澤爾的要點(diǎn) 。
摘要
現(xiàn)在,我們已經(jīng)成功配置了示例應(yīng)用程序的應(yīng)用程序上下文。 本教程教了四件事:
- 我們了解了如何使用Maven獲得所需的依賴(lài)關(guān)系。
- 我們學(xué)習(xí)了如何將jOOQ引發(fā)的異常轉(zhuǎn)換為Spring DataAccessExceptions 。
- 我們學(xué)習(xí)了如何配置使用jOOQ和Spring的應(yīng)用程序的應(yīng)用程序上下文。
- 當(dāng)我們使用此博客文章中描述的方法時(shí),我們快速考慮了必須考慮的事項(xiàng)。
本教程的下一部分描述了我們可以使用jOOQ的代碼生成支持 。
Github上提供了此博客文章的示例應(yīng)用程序。
補(bǔ)充閱讀:
Vlad Mihalcea也寫(xiě)了有關(guān)jOOQ的文章 。 查看他的博客!
翻譯自: https://www.javacodegeeks.com/2014/01/using-jooq-with-spring-configuration.html
總結(jié)
以上是生活随笔為你收集整理的将jOOQ与Spring结合使用:配置的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: linux开发常用命令(linux开发
- 下一篇: linux的应用程序后缀(linux的应