Has anyone tried using JOOQ with the Spring framework or开发者_开发问答 am I breaking new ground?
http://www.jooq.org
Many people are using jOOQ with Spring or Spring Boot
- https://www.baeldung.com/jooq-with-spring
- https://www.baeldung.com/spring-boot-support-for-jooq
There is also a very good tutorial by Petri Kainulainen, explaining every step to set up a project, here:
- Using jOOQ with Spring: Configuration
- Using jOOQ with Spring: Code Generation
- Using jOOQ with Spring: CRUD
- Using jOOQ with Spring: Sorting and Pagination
Here's a blog post about how to use jOOQ with Spring Boot, especially useful when you need the commercial distributions of jOOQ:
- https://blog.jooq.org/how-to-use-jooqs-commercial-distributions-with-spring-boot/
I was looking to use jOOQ as an builder library for providing queries to Spring's JdbcTemplate and related classes. Unfortunately, jOOQ appears to combine two concepts into the same set of classes: SQL generation and query execution. In my case, I want the former but want to let Spring handle the latter. It does work, though. For example, you can do something like this (using jOOQ 2.x API):
Factory create = new Factory(null, SQLDialect.ORACLE);
getJdbcTemplate().query(
create.select(create.field(ID_COL),
create.field(VALUE_COL))
.from(FOO_TABLE)
.where(create.field(ID_COL).equals("ignored"))
.getSQL(),
myRowMapper,
id);
getting spring transactions running with jOOQ is a lot simpler (unless I forgot something):
just wrap your data source into
org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy
optional: to delay opening a jdbc connection until the first actual sql statement happens use
org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy
so as an sample do this to create a jOOQ factory with 'transactions' and 'lazyness' applied
DataSource rawDS = /* your actual data source */
// (optional) make access lazy
final DataSource lazyDS = new LazyConnectionDataSourceProxy(rawDataSource);
// make spring transactions available in plain jdbc context
final DataSource txDS = new TransactionAwareDataSourceProxy(lazyDS);
// create jOOQ factory
Factory jooq = new Factory(txDS, /* dialect */, /* settings */)
// voila!
All you need to do/know to make jOOQ work with spring:
- Get the
java.sql.Connection
bound to the thread by the transaction manager. - Handle transactions properly through exception translation
- Understand that the jOOQ Factory objects (despite the name) are not threadsafe. and thus will require instantiating a new object per use (Do not do this other answer).
So for the first and second case I offer this gist: https://gist.github.com/3669307 which does what Lukas recommends.
For the third case you can either create basically a factory of a factory (which contains the DataSource
) or just instantiate a new Factory
object in each method using the wired DataSource
in your spring component.
@Service
public class MyDaoOrService {
@Autowired
private void DataSource dataSource;
@Transactional
public void doSomeJooq(){
Settings s = new Settings();
//You could instead put this jooq configuration xml
s.getExecuteListeners().add("com.snaphop.jooq.SpringExceptionTranslationExecuteListener");
MyGeneratedFactory f = new MyGeneratedFactory(dataSource, s);
f.select(); //etc
}
}
As for the settings listener you can JOOQ's configuration support to avoid the programmatic creation.
I won't cover how you setup a DataSource
in Spring as that is covered in myriad of other/better places.
Hope this will be helpful for someone....
Spring application context configuration.
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="systemPropertiesModeName">
<value>SYSTEM_PROPERTIES_MODE_OVERRIDE</value>
</property>
<property name="searchSystemEnvironment">
<value type="boolean">true</value>
</property>
</bean>
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource" >
<property name="driverClassName" value="org.h2.Driver"/>
<property name="url"
value="jdbc:h2://${user.home}
${file.separator}tracciabilitaCanarini${file.separator}db${file.separator}basedb"/>
<property name="username" value="sa"/>
<property name="password" value="sa"/>
</bean>
<bean id="datasourceConnection"
class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"
lazy-init="true" depends-on="dataSource">
<property name="targetObject">
<ref bean="dataSource"/>
</property>
<property name="targetMethod">
<value>getConnection</value>
</property>
</bean>
<bean id="publicFactory" class="dbLayer.db.PublicFactory" lazy-init="true"
depends-on="datasourceConnection" >
<constructor-arg index="0" ref="datasourceConnection" />
</bean>
It will auto fill the public factory with the given connection (and yes, it can be a pooled connection, with auto close etc., see DriverManagerDataSource class for more detailed configuration). And now, the publicFactory. Note: no need to modify the original public factory generated by jOOQ.
/**
* This class is generated by jOOQ
*/
package dbLayer.db;
/**
* This class is generated by jOOQ.
*/
@javax.annotation.Generated(value = {"http://www.jooq.org", "2.0.5"},
comments = "This class is generated by jOOQ")
public class PublicFactory extends org.jooq.util.h2.H2Factory {
private static final long serialVersionUID = -1930298411;
/**
* Create a factory with a connection
*
* @param connection The connection to use with objects created from this factory
*/
public PublicFactory(java.sql.Connection connection) {
super(connection);
}
/**
* Create a factory with a connection and some settings
*
* @param connection The connection to use with objects created from this factory
* @param settings The settings to apply to objects created from this factory
*/
public PublicFactory(java.sql.Connection connection, org.jooq.conf.Settings settings) {
super(connection, settings);
}
}
At the end, simply call the factory.
PublicFactory vs = (PublicFactory) SpringLoader.getBean("publicFactory");
SimpleSelectQuery<VersionRecord> sq = vs.selectQuery(dbLayer.db.tables.Version.VERSION);
VersionRecord v = null;
try {
v = sq.fetchAny();
} catch (Exception e) {
log.warn("Seems that version table does not exists!", e);
}
Done!
Assuming you are using Spring to build a webapp, you probably want to be doing something like this:
try {
Connection conn = dataSource.getConnection();
try {
// Do something with JOOQ
// No need to use a JdbcTemplate!
}
finally {
if (conn != null) {
conn.close();
}
}
} catch (SQLException e) {
// your error handling
}
You probably want to be getting a DataSource via Spring's dependency injection, because your web container, Tomcat or whathaveyou, is providing the DataSource and doing connection pooling. In one of your spring config files you would have something like
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/>
The object that the above code is in (or some object that provides this code with the datasource) could have configuration in a spring file to instantiate it with the datasource, like
<bean id="fooService" class="com.fubar.FooServiceImpl">
<constructor-arg ref="dataSource" type="javax.sql.DataSource" />
</bean>
The portion of the string "jdbc/datasource" would correspond to a resource name configured in the web container. This varies, but for Tomcat it might be a context file in conf/Catalina/localhost under Tomcat home, for example,
<?xml version="1.0" encoding="UTF-8"?>
<Context debug="10" reloadable="true" useNaming="true" antiJARLocking="true">
<Resource name="jdbc/datasource" auth="Container" type="javax.sql.DataSource"
maxActive="100" maxIdle="30" maxWait="10000" validationQuery="SELECT 1"
username="foo" password="fubar" driverClassName="org.postgresql.Driver"
url="jdbc:postgresql://localhost/foobase"/>
</Context>
For Java configuration (which is default for Spring Boot) you can use the following code:
/* JOOQ Configuration */
@Bean
public DataSourceConnectionProvider dataSourceConnectionProvider() {
return new DataSourceConnectionProvider(dataSource());
}
@Bean
public DefaultConfiguration defaultConfiguration() {
DefaultConfiguration defaultConfiguration = new DefaultConfiguration();
defaultConfiguration.setConnectionProvider(dataSourceConnectionProvider());
defaultConfiguration.setSQLDialect(SQLDialect.POSTGRES);
return defaultConfiguration;
}
@Bean
public DSLContext dslContext() {
return new DefaultDSLContext(defaultConfiguration());
}
Easiest way,(I have found) to use Spring Transactions with jOOQ, is given here: http://blog.liftoffllc.in/2014/06/jooq-and-transactions.html
Have a look at this answer for better explanation: https://stackoverflow.com/a/24380508/542108
精彩评论