/**
* Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.util.db;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
import javax.sql.DataSource;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Environment;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.orm.hibernate3.HibernateTransactionManager;
import org.springframework.orm.hibernate3.LocalSessionFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.support.AbstractPlatformTransactionManager;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.transaction.support.TransactionTemplate;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.SingletonFactoryBean;
/**
* Factory bean to provide SQL database connectors.
* <p>
* This class provides a simple-to-setup and simple-to-use way to access databases.
* It can be configured for access via JDBC, Hibernate or both.
* The main benefit is simpler configuration, especially if that configuration is in XML.
* <p>
* There are multiple options for some elements.
* Set either the dialect name or the dialect itself.
* Set either the transaction details or the transaction manager itself.
* Set the Hibernate factory bean, the Hibernate settings or the session factory itself.
*/
public class DbConnectorFactoryBean extends SingletonFactoryBean<DbConnector> {
/**
* The name that this source is known by.
*/
private String _name;
/**
* The underlying data source.
*/
private DataSource _dataSource;
/**
* The database type, used to create a helper class.
*/
private String _databaseDialectClass;
/**
* The database dialect.
*/
private DbDialect _databaseDialect;
/**
* Factory bean to create Hibernate.
* This can be used if more control is needed than the properties exposed on this factory bean.
*/
private LocalSessionFactoryBean _hibernateFactoryBean;
/**
* The Hibernate mapping file configuration classes.
*/
private HibernateMappingFiles[] _mappingConfigurations;
/**
* The Hibernate mapping resource locations.
*/
private String[] _mappingResources;
/**
* The Hibernate configuration to show the SQL.
*/
private boolean _hibernateShowSql;
/**
* Set to true if you want to use Hibernate thread-bound auto-create sessions
*/
private boolean _allowHibernateThreadBoundSession;
/**
* The Hibernate session factory.
*/
private SessionFactory _hibernateSessionFactory;
/**
* The transaction isolation level.
* See {@link DefaultTransactionDefinition}.
*/
private String _transactionIsolationLevelName;
/**
* The transaction isolation level.
* See {@link DefaultTransactionDefinition}.
*/
private String _transactionPropagationBehaviorName;
/**
* The transaction timeout in seconds.
* See {@link DefaultTransactionDefinition}.
*/
private int _transactionTimeoutSecs;
/**
* The transaction manager.
* This can be left null, and an appropriate one will be created.
*/
private PlatformTransactionManager _transactionManager;
/**
* Creates an instance.
*/
public DbConnectorFactoryBean() {
}
/**
* Creates an instance based on an existing source.
* <p>
* This copies the name, dialect, data source, session factory and transaction manager.
*
* @param base the base source to copy, not null
*/
public DbConnectorFactoryBean(DbConnector base) {
setName(base.getName());
setDialect(base.getDialect());
setDataSource(base.getDataSource());
setHibernateSessionFactory(base.getHibernateSessionFactory());
setTransactionManager(base.getTransactionManager());
}
//-------------------------------------------------------------------------
public String getName() {
return _name;
}
public void setName(String name) {
_name = name;
}
public DataSource getDataSource() {
return _dataSource;
}
public void setDataSource(DataSource dataSource) {
_dataSource = dataSource;
}
public String getDialectName() {
return _databaseDialectClass;
}
public void setDialectName(String databaseDialectClass) {
_databaseDialectClass = databaseDialectClass;
}
public DbDialect getDialect() {
return _databaseDialect;
}
public void setDialect(DbDialect dialect) {
_databaseDialect = dialect;
}
public LocalSessionFactoryBean getHibernateFactoryBean() {
return _hibernateFactoryBean;
}
public void setHibernateFactoryBean(LocalSessionFactoryBean hibernateFactoryBean) {
_hibernateFactoryBean = hibernateFactoryBean;
}
public HibernateMappingFiles[] getHibernateMappingFiles() {
return _mappingConfigurations;
}
public void setHibernateMappingFiles(HibernateMappingFiles[] mappingConfigurations) {
_mappingConfigurations = mappingConfigurations;
}
public String[] getHibernateMappingResources() {
return _mappingResources;
}
public void setHibernateMappingResources(String[] mappingResources) {
_mappingResources = mappingResources;
}
public boolean isHibernateShowSql() {
return _hibernateShowSql;
}
public void setHibernateShowSql(boolean hibernateShowSql) {
_hibernateShowSql = hibernateShowSql;
}
public boolean isAllowHibernateThreadBoundSession() {
return _allowHibernateThreadBoundSession;
}
public void setAllowHibernateThreadBoundSession(boolean allowHibernateThreadBoundSession) {
_allowHibernateThreadBoundSession = allowHibernateThreadBoundSession;
}
public SessionFactory getHibernateSessionFactory() {
return _hibernateSessionFactory;
}
public void setHibernateSessionFactory(SessionFactory sessionFactory) {
_hibernateSessionFactory = sessionFactory;
}
public String getTransactionIsolationLevelName() {
return _transactionIsolationLevelName;
}
public void setTransactionIsolationLevelName(String transactionIsolationLevelName) {
_transactionIsolationLevelName = transactionIsolationLevelName;
}
public String getTransactionPropagationBehaviorName() {
return _transactionPropagationBehaviorName;
}
public void setTransactionPropagationBehaviorName(String transactionPropagationBehaviorName) {
_transactionPropagationBehaviorName = transactionPropagationBehaviorName;
}
public int getTransactionTimeout() {
return _transactionTimeoutSecs;
}
public void setTransactionTimeout(int transactionTimeoutSecs) {
_transactionTimeoutSecs = transactionTimeoutSecs;
}
public PlatformTransactionManager getTransactionManager() {
return _transactionManager;
}
public void setTransactionManager(PlatformTransactionManager transactionManager) {
_transactionManager = transactionManager;
}
//-------------------------------------------------------------------------
@Override
public DbConnector createObject() {
ArgumentChecker.notNull(getName(), "name");
ArgumentChecker.notNull(getDataSource(), "dataSource");
DbDialect dialect = createDialect();
NamedParameterJdbcTemplate jdbcTemplate = createNamedParameterJdbcTemplate(dialect);
SessionFactory hbFactory = createSessionFactory(dialect);
HibernateTemplate hbTemplate = createHibernateTemplate(hbFactory);
TransactionTemplate transTemplate = createTransactionTemplate(hbFactory);
return new DbConnector(getName(), dialect, getDataSource(), jdbcTemplate, hbTemplate, transTemplate);
}
/**
* Creates the database dialect, using the dialect object, then the string.
*
* @return the dialect, not null
*/
protected DbDialect createDialect() {
DbDialect dialect = getDialect();
if (dialect == null) {
String dialectStr = getDialectName();
ArgumentChecker.notNull(dialectStr, "dialectStr");
if (dialectStr.contains(".") == false) {
dialectStr = "org.opengamma.util." + dialectStr;
}
try {
dialect = (DbDialect) getClass().getClassLoader().loadClass(dialectStr).newInstance();
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
return dialect;
}
/**
* Creates the JDBC template, using the data source.
*
* @param dialect the dialect instance, not null
* @return the JDBC template, not null
*/
protected NamedParameterJdbcTemplate createNamedParameterJdbcTemplate(DbDialect dialect) {
return dialect.getNamedParameterJdbcTemplate(getDataSource());
}
//-------------------------------------------------------------------------
/**
* Creates the Hibernate session factory.
*
* @param dialect the dialect instance, not null
* @return the session factory, may be null
*/
protected SessionFactory createSessionFactory(DbDialect dialect) {
SessionFactory result = getHibernateSessionFactory();
if (result != null) {
return result;
}
LocalSessionFactoryBean factory = getHibernateFactoryBean();
if (factory == null) {
String[] files = createHibernateFiles();
if (files.length == 0) {
return null; // Hibernate not required
}
factory = new LocalSessionFactoryBean();
factory.setMappingResources(files);
factory.setDataSource(getDataSource());
Properties props = new Properties();
props.setProperty("hibernate.dialect", dialect.getHibernateDialect().getClass().getName());
props.setProperty("hibernate.show_sql", String.valueOf(isHibernateShowSql()));
props.setProperty("hibernate.connection.release_mode", "on_close");
if (isAllowHibernateThreadBoundSession()) {
props.setProperty(Environment.CURRENT_SESSION_CONTEXT_CLASS, "thread");
props.setProperty(Environment.TRANSACTION_STRATEGY, "org.hibernate.transaction.JDBCTransactionFactory");
}
factory.setHibernateProperties(props);
factory.setLobHandler(dialect.getLobHandler());
}
try {
factory.afterPropertiesSet();
} catch (Exception ex) {
throw new RuntimeException(ex);
}
return factory.getObject();
}
/**
* Creates the complete list of Hibernate configuration files.
*
* @return the set of Hibernate files, not null
*/
protected String[] createHibernateFiles() {
String[] nameArray = getHibernateMappingResources();
HibernateMappingFiles[] filesArray = getHibernateMappingFiles();
if (nameArray == null && filesArray == null) {
return new String[0];
}
Set<String> config = new HashSet<String>();
if (nameArray != null) {
config.addAll(Arrays.asList(nameArray));
}
if (filesArray != null) {
for (HibernateMappingFiles files : filesArray) {
for (Class<?> cls : files.getHibernateMappingFiles()) {
String hbm = cls.getName().replace('.', '/') + ".hbm.xml";
config.add(hbm);
}
}
}
return (String[]) config.toArray(new String[config.size()]);
}
/**
* Creates the Hibernate template, using the session factory.
*
* @param sessionFactory the Hibernate session factory, may be null
* @return the Hibernate template, not null
*/
protected HibernateTemplate createHibernateTemplate(SessionFactory sessionFactory) {
if (sessionFactory != null) {
return new HibernateTemplate(sessionFactory);
}
return null;
}
//-------------------------------------------------------------------------
/**
* Creates the transaction template.
*
* @param sessionFactory the Hibernate session factory, may be null
* @return the transaction template, not null
*/
protected TransactionTemplate createTransactionTemplate(SessionFactory sessionFactory) {
DefaultTransactionDefinition transDefn = createTransactionDefinition();
PlatformTransactionManager transMgr = createTransactionManager(sessionFactory);
return new TransactionTemplate(transMgr, transDefn);
}
/**
* Creates the transaction definition.
*
* @return the transaction definition, not null
*/
protected DefaultTransactionDefinition createTransactionDefinition() {
DefaultTransactionDefinition transDefn = new DefaultTransactionDefinition();
transDefn.setName(getName());
if (getTransactionIsolationLevelName() != null) {
transDefn.setIsolationLevelName(getTransactionIsolationLevelName());
}
if (getTransactionPropagationBehaviorName() != null) {
transDefn.setPropagationBehaviorName(getTransactionPropagationBehaviorName());
}
if (getTransactionTimeout() != 0) {
transDefn.setTimeout(getTransactionTimeout());
}
return transDefn;
}
/**
* Creates the transaction manager.
*
* @param sessionFactory the Hibernate session factory, may be null
* @return the transaction manager, not null
*/
protected PlatformTransactionManager createTransactionManager(SessionFactory sessionFactory) {
PlatformTransactionManager transMgr = getTransactionManager();
if (transMgr == null) {
AbstractPlatformTransactionManager newTransMgr;
if (sessionFactory != null) {
newTransMgr = new HibernateTransactionManager(sessionFactory);
} else {
newTransMgr = new DataSourceTransactionManager(getDataSource());
}
newTransMgr.setNestedTransactionAllowed(true);
transMgr = newTransMgr;
}
return transMgr;
}
//-------------------------------------------------------------------------
@Override
public String toString() {
return getClass().getSimpleName();
}
}