Question

Has anyone tried using JOOQ with the Spring framework or am I breaking new ground?

http://www.jooq.org

Was it helpful?

Solution

Yes, many people have (by now). And the jOOQ manual includes a tutorial about how to get started using jOOQ, Spring, Spring-TX and BoneCP:

There is also a very good tutorial by Petri Kainulainen, explaining every step to set up a project, here:

Here's a blog post about how to use jOOQ with Spring Boot, especially useful when you need the commercial distributions of jOOQ:

OTHER TIPS

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);

All you need to do/know to make jOOQ work with spring:

  1. Get the java.sql.Connection bound to the thread by the transaction manager.
  2. Handle transactions properly through exception translation
  3. 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.

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!

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

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top