I've taken a look at the "FactoryProxy". IMHO it's a good approach but
during a request it seems that it's very likely that the internal
factory delegate gets created many many times. This may not be a
dramatic performance penality but should be avoided. The client of the
"FactoryProxy" even has no chance to avoid the permanent creation of
the underlying FactoryOperations.
Hopefully the existing factories get refactored soon to accept a
DataSource, hence the factories can be created once and be used
accross application code.
I didn't know about the "FactoryProxy" before - in our Spring based
project I've developed the following interface to integrate jOOQ with
Spring and its transactional infrastructure:
import org.jooq.FactoryOperations;
/**
* Instances of this interface are used to create independent jOOQ
factories and
* usually encapsulate mechanisms like e.g. obtaining a new
(transactional)
* connection from a data source and passing the connection to the
created jOOQ
* factory.
* <p/>
* This abstraction is required as jOOQ's factories are <b>not</b>
thread-safe
* and therefore cannot be shared by multiple clients in an
application context.
*
* @author J. Scharf
* @since 1.0
*/
public interface JooqFactoryCreator {
/**
* Get a new jOOQ factory.
*
* @return the jOOQ factory (never null)
*/
public FactoryOperations getJooqFactory();
}
and its implementation "ConfigurableJooqFactoryCreator":
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.sql.Connection;
import javax.sql.DataSource;
import org.jooq.FactoryOperations;
import org.jooq.conf.Settings;
import org.jooq.util.oracle.OracleFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
/**
* Implementation of {@link JooqFactoryCreator} which can be
configured to
* create instances of {@link FactoryOperations} from the specified
class.
* <p/>
* The class is aware of Spring's transaction infrastructure and
obtains new
* connections from the {@code DataSource} be calling
* {@link DataSourceUtils#getConnection(DataSource)}.
*
* @author J. Scharf
* @since 1.0
*/
public class ConfigurableJooqFactoryCreator implements
JooqFactoryCreator {
private final Logger logger =
LoggerFactory.getLogger(this.getClass());
private Class<?> jooqFactoryClazz = OracleFactory.class;
private Constructor<FactoryOperations> factoryConstructor;
private final DataSource dataSource;
private final Settings settings;
/**
* Creates a new factory creator.
*
* @param dataSource
* the data source to obtain a {@link Connection} from
* @param settings
* the settings to configure the jOOQ factory with
*/
public ConfigurableJooqFactoryCreator(DataSource dataSource,
Settings settings) {
Assert.notNull(dataSource, "The dataSource must not be null.");
Assert.notNull(settings, "The settings must not be null.");
this.dataSource = dataSource;
this.settings = settings;
initFactoryConstructor();
}
/**
* Used to cache the looked up {@link Constructor}.
*/
@SuppressWarnings("unchecked")
private void initFactoryConstructor() {
Class[] args = new Class[] { Connection.class, Settings.class };
logger.debug(
"Looking up constructor with arguments {} on class '{}'...",
args, jooqFactoryClazz.getName());
Constructor<FactoryOperations> constructor;
try {
constructor = (Constructor<FactoryOperations>) jooqFactoryClazz
.getConstructor(args);
this.factoryConstructor = constructor;
} catch (NoSuchMethodException ex) {
ReflectionUtils.handleReflectionException(ex);
}
}
/**
* Create a new jOOQ factory. Each method call returns a new
instance.
*
* @return the jOOQ factory (never null)
*/
@Override
public FactoryOperations getJooqFactory() {
logger.debug("Creating new jooq factory of type '{}'...",
jooqFactoryClazz.getName());
Connection connection = DataSourceUtils.getConnection(dataSource);
logger.debug("Fetched connection [{}] from DataSourceUtils.",
connection);
FactoryOperations factory;
try {
factory = factoryConstructor.newInstance(connection, settings);
return factory;
}
/*
* Unfortunately we have to catch each reflection exception
separately,
* because we don't want to catch any other exception not caused by
* newInstance
*/
catch (IllegalArgumentException ex) {
handleReflectionException(ex);
} catch (InstantiationException ex) {
handleReflectionException(ex);
} catch (IllegalAccessException ex) {
handleReflectionException(ex);
} catch (InvocationTargetException ex) {
handleReflectionException(ex);
}
// Unreachable
return null;
}
private void handleReflectionException(Exception ex) {
logger.error("Error creating jooq factory '{}': {}",
jooqFactoryClazz
.getName(), ex.toString());
ReflectionUtils.handleReflectionException(ex);
}
/**
* Set the jOOQ factory clazz to use. Defaults to {@link
OracleFactory}.
*
* @param jooqFactoryClazz
* the factory class (never null)
*/
public void setJooqFactoryClazz(Class<?> jooqFactoryClazz) {
Assert.notNull(jooqFactoryClazz,
"The jooqFactoryClazz must not be null.");
this.jooqFactoryClazz = jooqFactoryClazz;
initFactoryConstructor();
}
}
Maybe this could be helpful for others too. At the moment a new
factory usually will be created by client code each time
"getJooqFactory" gets called.
When I've got some time left I will try to synchronize (like Spring's
DataSourceUtils does it for a Connection) the created factory with the
current Spring-managed transaction (if any) so it only needs to be
created once during a transaction.
Kind regards,
Johannes