I am having problems with transactions using Hibernate's implementation of JPA (I am folowing Camel Tracer example)
I am using Hibernate JPA implementation:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>4.3.5.Final</version>
</dependency>
If I include a persistence.xml in my META-INF folder, everything works OK:
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
version="1.0">
<persistence-unit name="tracer" transaction-type="RESOURCE_LOCAL">
<class>org.apache.camel.processor.interceptor.jpa.JpaTraceEventMessage</class>
<properties>
<property name="hibernate.dialect" value="..."/>
<property name="hibernate.connection.driver_class" value="..."/>
<property name="hibernate.connection.url" value="..."/>
<property name="hibernate.hbm2ddl.auto" value="create"/>
<property name="hibernate.connection.username" value="..." />
<property name="hibernate.connection.password" value="..."/>
</properties>
</persistence-unit>
</persistence>
I want to use a Java way, so I remove the persistence.xml and create the following beans:
#Configuration
#EnableTransactionManagement
public class AppConfiguration
{
#Bean public EntityManagerFactory entMngFac()
{
EntityManagerFactory emf = Persistence.createEntityManagerFactory("tracer");
return emf;
}
#Bean public DataSource ds()
{
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("...");
dataSource.setUrl("...");
dataSource.setUsername( "..." );
dataSource.setPassword( "..." );
return dataSource;
}
#Bean public PlatformTransactionManager ptm(EntityManagerFactory emf, DataSource ds)
{
JpaTransactionManager jpat = new JpaTransactionManager();
jpat.setDataSource(ds);
jpat.setEntityManagerFactory(emf);
return jpat;
}
#Bean public TransactionTemplate tranTemp(PlatformTransactionManager ptm)
{
TransactionTemplate tt = new TransactionTemplate();
tt.setTransactionManager(ptm);
return tt;
}
}
After saving, when Camel attempts to flush the JPATracer object it has created, I get the following exception:
javax.persistence.TransactionRequiredException: no transaction is in progress
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.checkTransactionNeeded(AbstractEntityManagerImpl.java:1171)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.flush(AbstractEntityManagerImpl.java:1332)
at org.apache.camel.component.jpa.JpaProducer$1.doInTransaction(JpaProducer.java:86)
Which is a little odd as the "no transaction is in progress" error is coming from the "doInTransaction" method.
My thoughts are that Camel is starting a transaction, and then Hibernate is trying to flush the object and is unaware of the transaction that Camel has started? So there is some mix of up of transactions somewhere, but I can't figure out where.
Try with the following EntityManagerFactory configuration (note the packagesToScan call)
#Bean
public EntityManagerFactory entityManagerFactory() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(ds());
em.setPackagesToScan(new String[] { "org.apache.camel.processor.interceptor.jpa" });
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
return em.getObject();
}
Related
I'm trying to get jOOQ transactions to work, except using code instead of XML configuration. I think it should be as simple as
// inside #Configuration annotated class
#Bean
public DataSource makeTransactionAware(DataSource fromConnectionPool) {
return new TransactionAwareDataSourceProxy(fromConnectionPool);
}
except, I need to tell Spring that the injected DataSource is not the same one as returned from the method. How do I do that?
Bean custom naming & qualifier should do the work for you.
https://www.baeldung.com/spring-qualifier-annotation
#Bean(name = "txAwareDS")
public DataSource makeTransactionAware(#Qualifier("dataSource") DataSource fromConnectionPool) {
return new TransactionAwareDataSourceProxy(fromConnectionPool);
}
Example code is just a starting point.
From the Spring configuration file shared
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close" >
<!-- These properties are replaced by Maven "resources" -->
<property name="url" value="${db.url}" />
<property name="driverClassName" value="${db.driver}" />
<property name="username" value="${db.username}" />
<property name="password" value="${db.password}" />
</bean>
<bean id="transactionAwareDataSource"
class="org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy">
<constructor-arg ref="dataSource" />
</bean>
will be configured as
#Configuration
#PropertySource("classpath:config.properties")
public class DataSourceConfig{
#Value("${db.username}")
private String username;
#Value("${db.password}")
private String password;
#Value("${db.url}")
private String url;
#Bean
public DataSource dataSource() {
BasicDataSource basicDataSource = new BasicDataSource();
basicDataSource.setDriverClassName("<driver class name>"); // Spring will load the driver discovered if not specified explicitly.
basicDataSource.setUsername(username);
basicDataSource.setPassword(password);
basicDataSource.setUrl(url);
return basicDataSource;
}
#Bean
public TransactionAwareDataSourceProxy transactionAwareDataSource(DataSource dataSource) {
return new TransactionAwareDataSourceProxy(dataSource)
}
}
Hope this helps
Good day!
My problem is when i transferred configuration for another DB from xml to #Bean my transactions is lost.... dont rollback and not work.
I see this when in DB after first insert created row (!), but in this method(transaction) start second insert i take Exception and row after first inset stay on DB!
This xml
<bean name="sqlSessionFactoryYarus" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="configLocation" value="/WEB-INF/MapperConfigYarus.xml" />
<property name="dataSource" ref="dataSourceYarus" />
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="ru.project.crm.mapper_yarus"/>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryYarus" />
</bean>
<bean id="transactionManagerYarus" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSourceYarus" />
<qualifier value="yarus"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManagerYarus" />
this code (dataSource there is no this not to waste space)
#Component
#Scope("singleton")
#DependsOn("springApplicationContextHolder")
public class YarusConnectionConfig {
#Bean
public SqlSessionFactory sqlSessionFactoryYarus() throws Exception {
SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean();
sqlSessionFactory.setDataSource(dataSourceYarus());
sqlSessionFactory.setConfigLocation(new ClassPathResource("../MapperConfigYarus.xml"));
return sqlSessionFactory.getObject();
}
#Bean
public MapperScannerConfigurer yarusMapper() throws Exception {
MapperScannerConfigurer msc = new MapperScannerConfigurer();
msc.setSqlSessionFactoryBeanName("sqlSessionFactoryYarus");
msc.setBasePackage("ru.project.crm.mapper_yarus");
return msc;
}
#Bean
public DataSourceTransactionManager transactionManagerYarus() throws Exception {
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(dataSourceYarus());
return dataSourceTransactionManager;
}
}
And
All paces when i want to Transactional annotate #Transactional(value = "transactionManagerYarus")
And if i build project with xml Transactional works fine
BUT if build with #Bean its dont work...
Plesae Help me!
I use
1) Spring 4.3
2) MyBatis
3) Postgesql
4) Java 8
Also we find solution.
Problem was in dataSource
#Bean(destroyMethod = "close", name = "dataSourceYarus")
public ComboPooledDataSource dataSourceYarus() {
ComboPooledDataSource cpds = new ComboPooledDataSource();
//config connection
}
It is my bean, and i call this bean like method, for example
new DataSourceTransactionManager(dataSourceYarus());
I did not attach any importance to this, because in all example this is true.
BUT in xml configuration caused this like "Bean" on his name, i replace call in java-config to
new DataSourceTransactionManager(context.getBean("dataSourceYarus"))
and.... this work for me!
Because if call this method, every time creating new pool then transaction was over
I have a Java web application running on Tomcat 7 - jdk1.7
This application uses Spring 4.1.5.RELEASE and Hibernate 4.2.2.Final
My problem is a OutOfMemoryException of the Heap space on building section factory
This is my static method that opens a SessionFactory
public class GenericDAO {
public static SessionFactory sessionFactory = null;
public static ServiceRegistry serviceRegistry = null;
Transaction tx = null;
public static SessionFactory createSessionFactory() {
Configuration configuration = new Configuration();
configuration.configure();
serviceRegistry = new ServiceRegistryBuilder().applySettings(
configuration.getProperties()). buildServiceRegistry();
sessionFactory = configuration.buildSessionFactory(serviceRegistry);
return sessionFactory;
}
}
And this is an example of DAO
public class SpecificDAO extends GenericDAO {
public int save(MyObject item) {
Session session = createSessionFactory().openSession();
try {
tx = session.beginTransaction();
session.save(item);
tx.commit();
return item.getId();
} catch (HibernateException e) {
if (tx != null) tx.rollback();
e.printStackTrace();
} finally {
session.close();
}
return -1;
}
}
The error occurs at the line containing
sessionFactory = configuration.buildSessionFactory(serviceRegistry);
The problem doesn't occur immediately at the deploy, but after 2 o 3 days of usage
This is my Hibernate.cfg.xml
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="connection.url">jdbc:sqlserver://192.168.XX.XXX:1433;databaseName=DatabaseName</property>
<property name="connection.driver_class">com.microsoft.sqlserver.jdbc.SQLServerDriver</property>
<property name="connection.username">username</property>
<property name="connection.password">password</property>
<mapping class="it.company.client.project.hibernate.MyObject"/>
<!-- DB schema will be updated if needed -->
<!-- <property name="hbm2ddl.auto">update</property> -->
</session-factory>
</hibernate-configuration>
You have to create the session factory only once as it is a heavy weight object, refer to the hibernate documentation for its details.
Here is the sample code from the doc on how it should be created:
import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
public class HibernateUtil {
private static final SessionFactory sessionFactory = buildSessionFactory();
private static SessionFactory buildSessionFactory() {
try {
// Create the SessionFactory from hibernate.cfg.xml
return new Configuration().configure().buildSessionFactory(
new StandardServiceRegistryBuilder().build() );
}
catch (Throwable ex) {
// Make sure you log the exception, as it might be swallowed
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
}
It is better idea to flush and clear the session after used, you can use both
session.flush();
session.clear();
for more information link1 and link2
You are creating a SessionFactory object for every save() call.
i.e you are creating a new SessionFactory repeatedly for every save() call but not closing the existing SessionFactory objects in memory.
How many times save() is called ? the same no of SessionFactory will be in memory, which causes the memory leak.
SessionFactory are heavy weight objects, so you'd create at application initialization. You can create a SingleTon to instantiate SessionFactory.
Avoid instantiation of SessionFactory object on every DAO action. It is very slow and causes memory leaks. Better explained in this answer
If you're using Spring anyway, better to delegate to Spring work with SessionFactory, transactions and handling SQL exceptions. For example, your save() method will reduce to one line of code sessionFactory.getCurrentSession().save(item); Manual transaction open/commit/rollback should be replaced with #Transactional attribute. Also, usually better place transactions on whole service method, not on every single DAO method, but it depends of business logic.
Here example how to configure spring context for work with Hibernate (just first article for google)
I slightly adopted this example for current question
#Repository
public class SpecificDAO {
private SessionFactory sessionFactory;
#Autowired
public SpecificDAO(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
#Transactional(propagation=Propagation.REQUIRED)
public int save(MyObject item) {
try{
sessionFactory.getCurrentSession().save(item);
}catch (HibernateException e) {
return -1;
}
}
}
Spring configuration
<context:annotation-config/>
<context:component-scan base-package="it.company.client.project"/>
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.microsoft.sqlserver.jdbc.SQLServerDriver"/>
<property name="url" value="jdbc:sqlserver://192.168.XX.XXX:1433;databaseName=DatabaseName"/>
<property name="username" value="username"/>
<property name="password" value="password"/>
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="annotatedClasses">
<list>
<value>it.company.client.project.hibernate.MyObject</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.SQLServerDialect</prop>
<prop key="hibernate.connection.provider_class">org.hibernate.connection.DatasourceConnectionProvider</prop>
<prop key="hibernate.show_sql">false</prop>
<!--prop key="hibernate.hbm2ddl.auto">update</prop-->
</props>
</property>
</bean>
<tx:annotation-driven />
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
I'm currently trying to use the #PostLoad Annotation for some processing after my model has been loaded from the database. But at the moment it looks like that my method won't get triggered. I don't use the EntityManager so I'm looking for a way to enable this event bahavior.
My configuration looks like this:
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.provider_class">com.zaxxer.hikari.hibernate.HikariConnectionProvider
</property>
<property name="hibernate.connection.tinyInt1isBit">true</property>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/testing</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password"></property>
<property name="hibernate.connection.pool_size">1</property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
<property name="hibernate.hbm2ddl.auto">update</property>
<!-- OUTPUT STUFF -->
<property name="hibernate.show_sql">true</property>
<property name="hibernate.use_sql_comments">false</property>
<property name="hibernate.format_sql">false</property>
<!-- SESSION CONTEXT -->
<property name="hibernate.current_session_context_class">thread</property>
<!-- CONNECTION POOL hikariCP -->
<property name="hibernate.hikari.maximumPoolSize">25</property>
<property name="hibernate.hikari.idleTimeout">30000</property>
<property name="hibernate.hikari.dataSource.url">jdbc:mysql://localhost:3306/testing</property>
<property name="hibernate.hikari.dataSource.user">root</property>
<property name="hibernate.hikari.dataSource.password"></property>
<property name="hibernate.hikari.dataSource.cachePrepStmts">true</property>
<property name="hibernate.hikari.dataSource.prepStmtCacheSize">250</property>
<property name="hibernate.hikari.dataSource.prepStmtCacheSqlLimit">2048</property>
<property name="hibernate.hikari.dataSource.useServerPrepStmts">true</property>
<!-- Include the mapping entries -->
<mapping class="at.adtime.core.v1.model.User"/>
<mapping class="at.adtime.core.v1.model.Test"/>
</session-factory>
</hibernate-configuration>
Update:
I have a method called afterUserLoad which looks like this:
#PostLoad
public void afterUserLoad() {
ArrayList<String> computedIds = new ArrayList<String>();
for (Test test : this.Test) {
computedIds.add(test.getId());
}
this.setTestIds(computedIds);
}
It should load the Test List and put only the ids in an ArrayList.
#PostLoad handling is implemented at least since 5.2.17 version in Hibernate.
According with my research, #PostLoad, #PrePersist and other kinds of these JPA annotations do not work for Hibernate, instead of them, we can use Interceptors. I am using Hibernate-JPA and Spring with xml based configuration. With Spring I get a SessionFactory object, when i set the properties for this object I inject a MyInterceptor instance that extends EmptyInterceptor class, then you can overide several methods like postLoad, onSave, etc. When you implement this code, the overrided methods are executed according the life cycle of the operation. I have not tested the postLoad method because onSave method worked for me.
Update
Spring Java based configuration #JeffersonTavares:
Spring configuration class:
#Configuration
public class SpringConfig {
#Bean
public BasicDataSource basicDataSource(){ // org.apache.commons.dbcp2.BasicDataSource Object
BasicDataSource basicDataSource = new BasicDataSource();
basicDataSource.setDriverClassName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
basicDataSource.setUrl("jdbc:sqlserver://172.125.25.7:1433;databaseName=LIST_FIRMAS;selectMethod=direct;");
basicDataSource.setUsername("user");
basicDataSource.setPassword("password");
return basicDataSource;
}
#Bean
public MyInterceptor myInterceptor(){ // Your interceptor
return new MyInterceptor();
}
#Bean(name = "sessionFactory")
public SessionFactory sessionFactory() throws IOException{
LocalSessionFactoryBean localSessionFactoryBean = new LocalSessionFactoryBean();
localSessionFactoryBean.setDataSource(basicDataSource()); // DataSource
localSessionFactoryBean.setAnnotatedClasses(
com.isapeg.timbrado.model.SalMinimo.class,
com.isapeg.timbrado.model.DeduccionCnom12.class,
com.isapeg.timbrado.model.PercepcionCnom12.class); // JPA entity classes ....
localSessionFactoryBean.setEntityInterceptor(myInterceptor()); // Setting your Interceptor
Properties properties = new Properties();
properties.put("hibernate.dialect", "org.hibernate.dialect.SQLServer2012Dialect");
properties.put("hibernate.current_session_context_class", "thread");
properties.put("hibernate.show_sql", "false");
localSessionFactoryBean.setHibernateProperties(properties); // Setting Hibernate Properties
localSessionFactoryBean.afterPropertiesSet(); // Session Factory initialization
return localSessionFactoryBean.getObject(); // Returning of SessionFactory Object
}
// other beans ....
}
Something to test session factory object:
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
SessionFactory sessionFactory = (SessionFactory) ctx.getBean("sessionFactory");
if (sessionFactory == null) {
System.out.println("SessionFactory obj is null");
} else {
Session session = sessionFactory.openSession();
System.out.println("Session obj: " + session);
System.out.println("isOpen: " + session.isOpen() + " isConnected: " + session.isConnected());
session.close();
}
Update 2
How to implement an Interceptor here.
I'm creating SessionFactory and I have my datasource as object in code where I'm creating SessionFactory, but i cannot set datasource to Hibernate Configuration object. So how can I set my datasource to my SessionFactory?
Configuration configuration = new Configuration();
Properties properties = new Properties();
properties.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQLInnoDBDialect");
configuration.setProperties(properties);
configuration.setProperty("packagesToScan", "com.my.app");
SessionFactory sessionFactory = configuration.configure().buildSessionFactory();
If you happen to have your DataSource stored in JNDI, then simply use:
configuration.setProperty(
"hibernate.connection.datasource",
"java:comp/env/jdbc/yourDataSource");
But if you use a custom data source provider like Apache DBCP or BoneCP and you don't want to use a dependency injection framework like Spring, then you may inject it on the StandardServiceRegistryBuilder before creating the SessionFactory:
//retrieve your DataSource
DataSource dataSource = ...;
Configuration configuration = new Configuration()
.configure();
//create the SessionFactory from configuration
SessionFactory sf = configuration
.buildSessionFactory(
new StandardServiceRegistryBuilder()
.applySettings(configuration.getProperties())
//here you apply the custom dataSource
.applySetting(Environment.DATASOURCE, dataSource)
.build());
Note that if you use this approach, you don't need to put the connection parameters in your hibernate.cfg.xml anymore. Here's an example of a compatible hibernate.cfg.xml file when using approach from above:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLInnoDBDialect</property>
<property name="show_sql">false</property>
<!-- your mappings to classes go here -->
</session-factory>
</hibernate-configuration>
Code above tested on Hibernate 4.3.
To supply JDBC connections to Session, you need an implementation of ConnectionProvider.
By default, Hibernate uses DatasourceConnectionProvider which obtains a DataSource instance from JNDI.
To use a custom DataSource instance, use InjectedDataSourceConnectionProvider and inject the DataSource instance into it.
There is TODO note on InjectedDataSourceConnectionProvider
NOTE :
setDataSource(javax.sql.DataSource)
must be called prior to
configure(java.util.Properties).
TODO : could not find where
setDataSource is actually called.
Can't this just be passed in to
configure???
As per the note, call setDataSource() method from configure() method.
public class CustomConnectionProvider extends InjectedDataSourceConnectionProvider {
#Override
public void configure(Properties props) throws HibernateException {
org.apache.commons.dbcp.BasicDataSource dataSource = new BasicDataSource();
org.apache.commons.beanutils.BeanUtils.populate( dataSource, props );
setDataSource(dataSource);
super.configure(props);
}
}
You can also extend UserSuppliedConnectionProvider.
According to the contract of ConnectionProvider
Implementors should provide a public
default constructor.
Hibernate will invoke this constructor if custom ConnectionProvider is set through Configuration instance.
Configuration cfg = new Configuration();
Properties props = new Properties();
props.put( Environment.CONNECTION_PROVIDER, InjectedDataSourceConnectionProvider.class.getName() );
cfg.addProperties(props);
Luiggi Mendoza's answer is why my search sent me here, but I figure I should give my version because I spent quite some time looking around for how to do this - it sets it up with the Spring in-memory database for testing, a SessionContext and the hbm.xml in case you're not using annotations:
/**
* Instantiates a H2 embedded database and the Hibernate session.
*/
public abstract class HibernateTestBase {
private static EmbeddedDatabase dataSource;
private static SessionFactory sessionFactory;
private Session session;
#BeforeClass
public static void setupClass() {
dataSource = new EmbeddedDatabaseBuilder().
setType(EmbeddedDatabaseType.H2).
addScript("file:SQLResources/schema-1.1.sql").
addScript("file:SQLResources/schema-1.2.sql").
build();
Configuration configuration = new Configuration();
configuration.addResource("hibernate-mappings/Cat.hbm.xml");
configuration.setProperty("hibernate.dialect",
"org.hibernate.dialect.Oracle10gDialect");
configuration.setProperty("hibernate.show_sql", "true");
configuration.setProperty("hibernate.current_session_context_class",
"org.hibernate.context.internal.ThreadLocalSessionContext");
StandardServiceRegistryBuilder serviceRegistryBuilder =
new StandardServiceRegistryBuilder();
serviceRegistryBuilder.applySetting(Environment.DATASOURCE, dataSource);
serviceRegistryBuilder.applySettings(configuration.getProperties());
StandardServiceRegistry serviceRegistry =
serviceRegistryBuilder.build();
sessionFactory = configuration.buildSessionFactory(serviceRegistry);
sessionFactory.openSession();
}
#AfterClass
public static void tearDown() {
if (sessionFactory != null) {
sessionFactory.close();
}
if (dataSource != null) {
dataSource.shutdown();
}
}
#Before
public final void startTransaction() {
session = sessionFactory.getCurrentSession();
session.beginTransaction();
}
#After
public final void rollBack() {
session.flush();
Transaction transaction = session.getTransaction();
transaction.rollback();
}
public Session getSession() {
return session;
}
}
and you'll need these:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.184</version>
<scope>test</scope>
</dependency>
If your datasource is bounded at the JNDI tree:
configuration.setProperty("hibernate.connection.datasource", "java:comp/env/jdbc/test");
Otherwise, if you have a DataSource object in code, which you want to use:
java.sql.Connection conn = datasource.getConnection();
Session session = sessionFactory.openSession(conn);
I would recommend the first one, to let Hibernate handle the connection lifecycle as needed. At the second approach, make sure that you close the connection when it's no longer needed.
I don't think you can. The Hibernate API will let you configure the JDBC properties so that it can manage the connections itself, or you can give it a JNDI DataSource location so it can go and fetch it, but I don't think you can give it a DataSource.
On the off-chance that you're using Spring, it's easier - use LocalSessionFactoryBean to configure Hibernate, and inject your DataSource into that. Spring performs the necessary magic in the background.
If you are using Spring framework, then use LocalSessionFactoryBean for injecting your data source to Hibernate SessionFactory.
<beans>
<bean id="YourClass"
class="com.YourClass.
<property name="sessionFactory">
<ref bean="DbSessionFactory" />
</property>
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName">
<value>org.postgresql.Driver</value>
</property>
<property name="url">
<value>jdbc:postgresql://localhost/yourdb</value>
</property>
<property name="username">
<value>postgres</value>
</property>
<property name="password">
<value>postgres</value>
</property>
</bean>
<bean id="DbSessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource">
<ref local="dataSource"/>
</property>
<property name="mappingResources">
<list>
<value>conf/hibernate/UserMapping.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect"> org.hibernate.dialect.PostgreSQLDialect </prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
<prop key="hibernate.cache.use_second_level_cache"> true </prop>
<prop key="hibernate.cache.use_query_cache">true</prop>
</props>
</property>
</bean>
</beans>
If you've implemented a class with javax.sql.DataSource, Hibernate's DataSource can be set by configuring properties.
import javax.sql.DataSource;
public class HibernateDataSource implements DataSource {
...
}
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
public class MyHibernateCfg {
public void initialize() {
HibernateDataSource myDataSource = new HibernateDataSource();
Configuration cfg = new Configuration();
// this is how to configure hibernate datasource
cfg.getProperties().put(Environment.DATASOURCE, myDataSource);
...
}
}
import org.hibernate.cfg.Configuration;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.SessionFactory;
import org.hibernate.Session;
public class TableClass {
public void initialize() {
MyHibernateCfg cfg = new MyHibernateCfg();
Configuration conf = cfg.getCfg();
ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(conf.getProperties()).build();
SessionFactory sessionFactory = conf.buildSessionFactory(serviceRegistry);
Session sessionFactory.openSession();
...
}
}
I used LocalContainerEntityManagerFactoryBean to create EntityManagerFactory instance at the configuration class.
If it is required to set another DataSource, than it is possible to update it with entity manager factory instance at runtime:
#Service("myService")
public class MyService
{
....
#Autowired
private LocalContainerEntityManagerFactoryBean emf;
....
public void replaceDataSource(DataSource dataSource)
{
emf.setDataSource(dataSource);
emf.afterPropertiesSet();
}
....
}
It works with Hibernate 5.2.9 Final.