We have an application which uses a Spring container on the server which uses EJB3 entities and Hibernate to persist data in a PostgreSQL database.
I'd like to add another connection to a separate database using a separate EntityManager, but the vendor of the DBMS (Trifox Vortex) does not have a DataSource type driver.
Can the Hibernate EntityManager use a simple JDBC-compliant driver to implement EJB3 JPA persistence of Entities?
Do I need to have a specific Hibernate Dialect for the DBMS I'm connecting to?
For what it's worth, here's the XML definition of our current Entity Manager Factory in Spring:
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="UniWorks-EntityPersistenceUnit"/>
<property name="dataSource" ref="dataSource"/>
<property name="jpaDialect">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/>
</property>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="${db.showsql}"/>
<property name="generateDdl" value="${db.generate}"/>
<property name="databasePlatform" value="org.hibernate.dialect.PostgreSQLDialect"/>
</bean>
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.hbm2ddl.auto">${db.hbm2ddl}</prop>
</props>
</property>
</bean>
UPDATE/PROGRESS: 21/3/13
After much to-ing and fro-ing with the Vendor, I managed to get their JDBC driver working with a simple Java test program.
Unfortunately, their JDBC driver is obviously not sufficient for Hibernate to get enough information as Spring fails to create the EntityManagerFactory with the following stack-trace:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in URL [bundle://222.0:0/META-INF/spring/cobol.xml]: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: [PersistenceUnit: UniWorks-CobolPersistenceUnit] Unable to build EntityManagerFactory
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1455)[58:org.springframework.beans:3.1.1.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:519)[58:org.springframework.beans:3.1.1.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)[58:org.springframework.beans:3.1.1.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:294)[58:org.springframework.beans:3.1.1.RELEASE]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:225)[58:org.springframework.beans:3.1.1.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:291)[58:org.springframework.beans:3.1.1.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)[58:org.springframework.beans:3.1.1.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:567)[58:org.springframework.beans:3.1.1.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:913)[59:org.springframework.context:3.1.1.RELEASE]
at org.springframework.osgi.context.support.AbstractDelegatedExecutionApplicationContext.access$1600(AbstractDelegatedExecutionApplicationContext.java:69)[90:org.springframework.osgi.core:1.2.1]
at org.springframework.osgi.context.support.AbstractDelegatedExecutionApplicationContext$4.run(AbstractDelegatedExecutionApplicationContext.java:355)[90:org.springframework.osgi.core:1.2.1]
at org.springframework.osgi.util.internal.PrivilegedUtils.executeWithCustomTCCL(PrivilegedUtils.java:85)[90:org.springframework.osgi.core:1.2.1]
at org.springframework.osgi.context.support.AbstractDelegatedExecutionApplicationContext.completeRefresh(AbstractDelegatedExecutionApplicationContext.java:320)[90:org.springframework.osgi.core:1.2.1]
at org.springframework.osgi.extender.internal.dependencies.startup.DependencyWaiterApplicationContextExecutor$CompleteRefreshTask.run(DependencyWaiterApplicationContextExecutor.java:132)[91:org.springframework.osgi.extender:1.2.1]
at java.lang.Thread.run(Thread.java:662)[:1.6.0_24]
Caused by: javax.persistence.PersistenceException: [PersistenceUnit: UniWorks-CobolPersistenceUnit] Unable to build EntityManagerFactory
at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:677)[80:com.springsource.org.hibernate:3.3.2.GA]
at org.hibernate.ejb.HibernatePersistence.createContainerEntityManagerFactory(HibernatePersistence.java:132)[80:com.springsource.org.hibernate:3.3.2.GA]
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:268)[64:org.springframework.orm:3.1.1.RELEASE]
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:310)[64:org.springframework.orm:3.1.1.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1514)[58:org.springframework.beans:3.1.1.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1452)[58:org.springframework.beans:3.1.1.RELEASE]
... 14 more
Caused by: org.hibernate.HibernateException: Unable to access java.sql.DatabaseMetaData to determine appropriate Dialect to use
at org.hibernate.dialect.resolver.DialectFactory.determineDialect(DialectFactory.java:141)[80:com.springsource.org.hibernate:3.3.2.GA]
at org.hibernate.dialect.resolver.DialectFactory.buildDialect(DialectFactory.java:97)[80:com.springsource.org.hibernate:3.3.2.GA]
at org.hibernate.cfg.SettingsFactory.buildSettings(SettingsFactory.java:117)[80:com.springsource.org.hibernate:3.3.2.GA]
at org.hibernate.cfg.Configuration.buildSettingsInternal(Configuration.java:2119)[80:com.springsource.org.hibernate:3.3.2.GA]
at org.hibernate.cfg.Configuration.buildSettings(Configuration.java:2115)[80:com.springsource.org.hibernate:3.3.2.GA]
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1339)[80:com.springsource.org.hibernate:3.3.2.GA]
at org.hibernate.cfg.AnnotationConfiguration.buildSessionFactory(AnnotationConfiguration.java:867)[80:com.springsource.org.hibernate:3.3.2.GA]
at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:669)[80:com.springsource.org.hibernate:3.3.2.GA]
... 19 more
Caused by: java.sql.SQLException: getDatabaseMajorVersion: Not supported by VORTEXjdbc.
at vortex.sql.vortexDbMetaData.getDatabaseMajorVersion(vortexDbMetaData.java:362)
at org.hibernate.dialect.resolver.DialectFactory.determineDialect(DialectFactory.java:131)[80:com.springsource.org.hibernate:3.3.2.GA]
... 26 more
So it looks like I might have to write my own dialect sub-class. Anybody give me any pointers as to where to start looking, or what existing sub-class to extend?
This should work (SimpleDriverDataSource + org.springframework.orm.jpa.vendor.Database.DEFAULT):
<bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
<property name="driverClass" value="org.h2.Driver"/> <!-- put your driver here -->
<property name="url" value="jdbc:h2:mem:test"/> <!-- put your jdbc url here -->
<property name="username" value="sa"/>
<property name="password" value=""/>
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="persistenceUnitName" value="TestPersistenceUnit"/>
<property name="jpaDialect">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/>
</property>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="false"/>
<property name="database" value="DEFAULT"/>
</bean>
</property>
</bean>
Hibernate can use connection pools other than a DataSource. So you could use c3p0 or proxool or dbcp or even your own connection pool. Hibernate does have a built in connection pool, though it is only meant for testing/prototyping; it would allow you to just pass in the driver name, url, username, password and it would set up some (very) simple pooling.
There are a few different options to achieve this. First, Hibernate always handles obtaining and releasing connections through a unified contract : org.hibernate.engine.jdbc.connections.spi.ConnectionProvider. So one option is to pass in your own ConnectionProvider implementation.
Another option is to pass in the driver, url, username, password and a ConnectionProvider to use. For the driver, url, username and password both Hibernate and JPA define mechanisms to do that. For JPA you'd use either persistence.xml or the settings javax.persistence.jdbc.driver, javax.persistence.jdbc.url, javax.persistence.jdbc.user and javax.persistence.jdbc.password. As far as the ConnectionProvider, by default Hibernate will choose the non-supported, not-intended-for-production one. But to tell Hibernate a different one to use, you would use the hibernate.connection.provider_class setting. Hibernate has built-in support for integrating with both c3p0 and proxool as back end connection pools (Hibernate will set up and manage the c3p0/proxool pool for you and use that pool for connection management).
As the other answer points out, another option is to use a DataSource wrapper around your driver and connection info.
Sorry, I do not know Spring so I do not know all the Spring-specific ways to specify persistence.xml or JPA settings or Hibernate settings.
Related
we use java SSH, recently we encountered the following problem frequently,not sure what happened, i searched so many times and no such shiro related scenario occured,we use shiro as authenticaiton framework, and customized the sessionDAO including session operations like "doCreate,doUpdate etc.",even config like this in applicaitonContext.xml:
<tx:method name="do*" propagation="REQUIRES_NEW" />
the trace:
2018-01-22 18:02:03.482 [http-nio-8080-exec-762] INFO org.apache.struts2.rest.RestActionInvocation - Executed action [//order/order!index!xhtml!200] took 574 ms (execution: 149 ms, result: 425 ms)
org.springframework.dao.TransientDataAccessResourceException: PreparedStatementCallback; SQL [update sessions set session=? where session_id=?]; Connection is read-only. Queries leading to data modification are not allowed; nested exception is java.sql.SQLException: Connection is read-only. Queries leading to data modification are not allowed
at org.springframework.jdbc.support.SQLStateSQLExceptionTranslator.doTranslate(SQLStateSQLExceptionTranslator.java:108)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:73)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:649)
at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:870)
at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:931)
at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:941)
at com.shopping.web.authentication.dao.impl.ShiroSessionDao.doUpdate(ShiroSessionDao.java:48)
at org.apache.shiro.session.mgt.eis.CachingSessionDAO.update(CachingSessionDAO.java:277)
at org.apache.shiro.session.mgt.eis.CachingSessionDAO$$FastClassBySpringCGLIB$$2a5e5afd.invoke(<generated>)
anyone could please help?
thanks a lot.
db configuration in applicationContext.xml:
<!-- C3P0 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="driverClass" value="${jdbc.driverClass}"></property>
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
<property name="initialPoolSize" value="${jdbc.initPoolSize}"></property>
<property name="maxPoolSize" value="${jdbc.maxPoolSize}"></property>
<property name="maxIdleTime" value="${cpool.maxIdleTime}"/>
</bean>
<!-- Spring jdbcTempate used for authentication -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--SessionFactory -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="configLocation" value="classpath:hibernate.cfg.xml"></property>
<property name="mappingLocations" value="classpath:com/shopping/web/entities/*.hbm.xml"></property>
<property name="packagesToScan">
<list>
<value>com.shopping</value>
</list>
</property>
</bean>
we use jdbctemplate together with hibernate5 with the same session managment.
in db.proerties:
jdbc.user=shopping
jdbc.password=123456
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql://192.168.2.221:3306,192.168.2.222:3306,192.168.2.200:3306/shopping?useUnicode=true&characterEncoding=utf-8
jdbc.initPoolSize=5
jdbc.maxPoolSize=10
cpool.maxIdleTime=25200
after hard work on this problem, we got the root cause:
we use mysql cluster and c3p0 connection pool on server side to store data as well as shiro session,sometimes the web site need to reconnect to mysql cluster if there is no available connection to use in the pool, once reconnect, the default mode is read only, but the shiro session need update operation in database, this cause the problem.
the solution is: change the mysql connection url as below:
jdbc.jdbcUrl=jdbc:mysql://server1:3306,server2:3306,server3:3306/database?autoReconnectForPools=true&autoReconnect=true&failOverReadOnly=false&useUnicode=true&characterEncoding=utf-8
and autoReconnectForPools=true&autoReconnect=true&failOverReadOnly=false is the key to solution
I'm a newbie with Java and I need to create a console application that is going to connect with 4 databases (access, vfp, mysql and sqlserver).
I started with hibernate.cfg.xml files and managed to configure them, one for each database. Then I realized that jpa was a better solution. So I changed all my hibernate files to a persistence.xml file with 4 persistence-unit.
The databases are working well, but to use them I have to create a lot of code. This is an example:
EntityManagerFactory dbPersistence =
Persistence.createEntityManagerFactory("oneOfMyDatabases");
EntityManager em = dbPersistence.createEntityManager();
Query query = em.createQuery("from ProductEntity").setMaxResults(10);
for (Object o : query.getResultList()) {
ProductEntity c = (ProductEntity) o;
System.out.println("Product " + c.getName());
}
cgPersistence.close();
I need to update one of the databases with data from the other databases.
It's a pain to create all the code like this, so I was thinking about creating repositories but I can't see how to create them with different entityManagers for each database.
I tried to inject the managers with google guice without success, but I couldn't handle how to close or where to close the persistence connection.
Finally, I've found Spring Data and it seems to be what I need, but I don't really understand how to use it.
I need some guide to get it working because I've read tons of tutorials and each of them appear to be different:
· Can I use the same persistence.xml or do I need another configuration file? I've seen that Spring Data has jpa compatibility but I'm not sure how it works.
· Is Spring Context the IOC Container of Spring Framework? Do I need it to work with Spring Data?
· What else do I need?
Thank you in advance
Each database is going to be represented by a different data source. For every data source you need a different session factory/entity manager factory.
If you want to save in more than one data source in a single transaction you then need XA transactions, therefore a Java EE or a stand-alone transaction manager, like Bitronix or Atomikos.
You have 4 different entity manager factories, you also need specific repositories for each of those:
<jpa:repositories base-package="your.company.project.repository.access" entity-manager-factory-ref="accessEntityManagerFactory"/>
<jpa:repositories base-package="your.company.project.repository.sqlserver" entity-manager-factory-ref="sqlserverEntityManagerFactory"/>
Then your application doesn't have to care which repository it uses.
The JpaTransactionManager requires one entityManagerFactory, but since you have 4 you may end up creating for of those, which I think it's undisirable.
It's better to switch to JTA instead:
<!-- 1. You define the Bitronix config ->
<bean id="btmConfig" factory-method="getConfiguration" class="bitronix.tm.TransactionManagerServices">
<property name="serverId" value="spring-btm"/>
<property name="warnAboutZeroResourceTransaction" value="true"/>
<property name="logPart1Filename" value="${btm.config.logpart1filename}"/>
<property name="logPart2Filename" value="${btm.config.logpart2filename}"/>
<property name="journal" value="${btm.config.journal:disk}"/>
</bean>
<!-- 2. You define all your data sources ->
<bean id="dataSource" class="bitronix.tm.resource.jdbc.PoolingDataSource" init-method="init"
destroy-method="close">
<property name="className" value="${jdbc.driverClassName}"/>
<property name="uniqueName" value="dataSource"/>
<property name="minPoolSize" value="0"/>
<property name="maxPoolSize" value="5"/>
<property name="allowLocalTransactions" value="false"/>
<property name="driverProperties">
<props>
<prop key="user">${jdbc.username}</prop>
<prop key="password">${jdbc.password}</prop>
<prop key="url">${jdbc.url}</prop>
</props>
</property>
</bean>
<!-- 3. For each data source you create a new persistenceUnitManager and you give its own specific persistence.xml ->
<bean id="persistenceUnitManager" depends-on="transactionManager"
class="org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager">
<property name="persistenceXmlLocation" value="classpath*:META-INF/persistence.xml"/>
<property name="defaultDataSource" ref="dataSource"/>
<property name="dataSourceLookup">
<bean class="org.springframework.jdbc.datasource.lookup.BeanFactoryDataSourceLookup"/>
</property>
</bean>
<!-- JpaDialect must be configured for transactionManager to make JPA and JDBC share transactions -->
<bean id="jpaDialect" class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/>
<!-- 4. For each data source you create a new entityManagerFactory ->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="persistenceUnit"/>
<property name="persistenceUnitManager" ref="persistenceUnitManager"/>
<property name="jpaDialect" ref="jpaDialect"/>
</bean>
<!-- 5. You have only one JTA transaction manager ->
<bean id="jtaTransactionManager" factory-method="getTransactionManager"
class="bitronix.tm.TransactionManagerServices" depends-on="btmConfig, dataSource"
destroy-method="shutdown"/>
<!-- 6. You have only one Spring transaction manager abstraction layer ->
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionManager" ref="jtaTransactionManager"/>
<property name="userTransaction" ref="jtaTransactionManager"/>
</bean>
If this proves too much work for your current problem, you can give a try to having 4 JPA transaction managers and see how it works.
If your schema is same across all the databases, then the best approach is to use hibernate,because hibernate provides portability between multiple databases. once you create the hbm files and pojo classes, the only thing you need to change is the dialect and your datasource.
The transactionmanager support can be provided by spring.
Example of spring configuration file:
<bean id="jpaDialect" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory"ref="entityManagerFactory"/>
<property name="jpaDialect"ref="jpaDialect"/>
</bean>
<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
....
</bean>
and the persistence.xml jpa file:
<persistence-unit name="EmployeeService">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
</persistence-unit>
As you can see the jpa provider-related information is set 3 times. In transaction manager bean, entity manager factory bean and in the persistence unit configuration:
<property name="jpaDialect"ref="jpaDialect"/>
...
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
...
<provider>org.hibernate.ejb.HibernatePersistence</provider>
But actually in my project I configured only the persistence unit with provider. And it worked.
So my question is what's the difference between provider, dialect and vendor options?
Must I set all of them or, I can skip some of them?
Can I set, for example as a vendor for EntityMangerFactory - Hibernate, as a dialect in transaction manager - Eclipse and as a provider in the persistence unit configuration - something else, TopLink, for example.
It's no clear to me. Please explain.
Will try to explain it to you line by line:
<bean id="jpaDialect" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
//Should ideally be
<bean id="jpaDialect" class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/>
This bean defines the jpaDialect that you are going to use. JpaDialect is an interface encapsulates certain functionality that standard JPA 1.0 does not offer, such as access to the underlying JDBC Connection. This strategy is mainly intended for standalone usage of a JPA provider; most of its functionality is not relevant when running with JTA transactions.
Also allows for the provision of value-added methods for portable yet more capable EntityManager and EntityManagerFactory subinterfaces offered by Spring.
Since you have provided the class as class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>, this allows Spring to plug in vendor-specific behavior into Spring's EntityManagerFactory creators and it serves as single configuration point for all vendor-specific properties.It's a custom implementation of spring's own JpaVendorAdapter.
For the second bean where you have declared:
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory"ref="entityManagerFactory"/>
<property name="jpaDialect"ref="jpaDialect"/>
</bean>
You tell 'Spring' to configure a transactionManager whose properties are entityManagerFactory and jpaDialect. Since these properties have to specific to hibernate these are set according. The entityManagerFactory and jpaDialect are now set specifically to hibernate (or Vendor).
As for the third bean
<property name="jpaDialect"ref="jpaDialect"/>
...
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
...
<provider>org.hibernate.ejb.HibernatePersistence</provider>
The <provider> tells spring to use the hibernate provider and the class org.hibernate.ejb.HibernatePersistence is Hibernate EJB3 persistence provider implementation.
In short, you need to configure these in order to tell spring which ORM's functionality should be used.
The reason that your application worked with configuring just persistence and provider is because the vendor adapter is automatically passed the persistence provided i.e. HibernatePersistence via the getPersistenceProvider in JpaVendorAdapter.
Tinker around the documentation to understand how these classes are interlinked.
EDIT : As pointed out by #TheKojuEffect , the first bean should ideally be in the form of :
<bean id="jpaDialect" class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/>
Thanks. Missed the vendorAdapter.
You can refer :
HibernateJpaDialect
HibernateVendorAdapter
HibernatePersistence
Hope it helps. :)
I'm Spring(yfying) my application that uses Hibernate + C3P0 for the connection pool. I'm using a managed hibernate context for specific reasons. I'm using a utility class "HibernateUtil" for Session handling. For the first migration to Spring I am creating an ApplicationContext and retrieving a SessionFactory bean in HibernateUtil replacing the code that used to build the SessionFactory. When I create a session factory bean completely from my old hibernate.cfg.xml in Spring everything works as expected:
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="configLocation" value="classpath:config/hibernate.cfg.xml"></property>
</bean>
hibernate.cfg.xml
<hibernate-configuration>
<session-factory>
<property name="connection.username">user</property>
<property name="connection.url">jdbc:mysql://localhost:3306/mydb?zeroDateTimeBehavior=convertToNull</property>
<property name="dialect">org.hibernate.dialect.MySQL5Dialect</property>
<property name="connection.password">pass</property>
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.max_fetch_depth">3</property>
<property name="hibernate.current_session_context_class">org.hibernate.context.ManagedSessionContext</property>
<property name="hibernate.transaction.auto_close_session">false</property>
<property name="hibernate.cache.region.factory_class">
net.sf.ehcache.hibernate.SingletonEhCacheRegionFactory</property>
<property name="hibernate.cache.use_query_cache">true</property>
<property name="hibernate.cache.use_second_level_cache">true</property>
<property name="hibernate.show_sql">true</property>
<property name="query.substitutions">true 1, false 0, yes 'Y', no 'N'</property>
<!-- configuration pool via c3p0-->
<property name="connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
<property name="connection.isolation">2</property>
<property name="hibernate.c3p0.acquire_increment">3</property>
<property name="hibernate.c3p0.idle_test_period">120</property> <!-- seconds -->
<property name="hibernate.c3p0.max_size">100</property>
<property name="hibernate.c3p0.max_statements">50</property>
<property name="hibernate.c3p0.min_size">3</property>
<property name="hibernate.c3p0.timeout">1800</property>
<!-- mapping files -->
.......
If I externalize the connection pool (I also remove all the connection settings from hibernate.cfg.xml), my transactions do not work properly.
<bean id="myDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="${db.driver}"/>
<property name="jdbcUrl" value="${db.url}"/>
<property name="user" value="${db.user}"/>
<property name="password" value="${db.pass}"/>
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="myDataSource"/>
<property name="configLocation" value="classpath:config/hibernate.cfg.xml"></property>
</bean>
I've tried specifying hibernate.transaction.factory_class and moving hibernate properties to Spring bean configuration instead of using hibernate.cfg.xml all to no avail. I cannot completely switch to Spring Transaction management just yet.
Imho you should try to move everything to Spring. In my experience when it is to mixed up there are a lot of problems. Is there a particular reason why you cannot yet set up a spring managed transaction management using:
<!-- Transaction Management -->
<tx:annotation-driven transaction-manager="txManager" proxy-target-class="true"/>
<bean id="txManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
From your description it is quite dificult to find out where exactly the problem is occuring is it with the transactions or with the sessionFactory?
Another point you might be causing your sessionFactory problems when using the dataSource outside of the hibernateProperties is that the ConnectionProvider implementation Hibernate is using is changing. If you specify a dataSource Hibernate will use DataSourceConnectionProvider(http://docs.jboss.org/hibernate/orm/3.6/javadocs/org/hibernate/connection/DatasourceConnectionProvider.html) whereas with setting the data source in the hibernate config Hibernate will use the DriverManagerConnectionProvider (http://docs.jboss.org/hibernate/orm/3.2/api/org/hibernate/connection/DriverManagerConnectionProvider.html)
The differences between these two might be the cause of your problems.
I have a method, marked as #Transactional.
It consists of several functions, one of them uses JDBC and the second one - Hibernate, third - JDBC.
The problem is that changes, made by Hibernate function are not visible in the last functions, that works with JDBC.
#Transactional
void update() {
jdbcUpdate1();
hibernateupdate1();
jdbcUpdate2(); // results of hibernateupdate1() are not visible here
}
All functions are configured to use the same datasource:
<bean id="myDataSource" class="org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy">
<property name="targetDataSource" ref="targetDataSource"/>
</bean>
<bean id="targetDataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close" lazy-init="true" scope="singleton">
<!-- settings here -->
</bean>
myDataSource bean is used in the code. myDataSource.getConnection() is used to work with connections in jdbc functions and
getHibernateTemplate().execute(new HibernateCallback() {
public Object doInHibernate(Session session) throws HibernateException, SQLException {
...
}
});
is used in hibernate function.
Thanks.
First, avoid using JDBC when using hibernate.
Then, if you really need it, use to Session.doWork(..). If your hibernate version does not yet have this method, obtain the Connection from session.connection().
You can use JDBC and Hibernate in the same transaction if you use the right Spring setup:
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<bean id="myDao" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager" ref="transactionManager"/>
<property name="target">
<bean class="MyDaoImpl">
<property name="dataSource" ref="dataSource"/>
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
</property>
<property name="transactionAttributes">
<props>
<prop key="get*">PROPAGATION_SUPPORTS,readOnly</prop>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
This assumes that the JDBC portion of your DAO uses JdbcTemplate. If it doesn't you have a few options:
Use DataSourceUtils.getConnection(javax.sql.DataSource) to get a connection
Wrap the DataSource you pass to your DAO (but not necessarily the one you pass to the SessionFactory) with a TransactionAwareDataSourceProxy
The latter is preferred since it hidse the DataSourceUtils.getConnection inside the proxy datasource.
This is of course the XML path, it should be easy to convert this to annotation based.
The problem is, the operations on Hibernate engine does not result in immediate SQL execution. You can trigger it manually calling flush on Hibernate session. Then the changes made in hibernate will be visible to the SQL code within the same transaction. As long as you do DataSourceUtils.getConnection to get SQL connection, because only then you'll have them run in the same transaction...
In the opposite direction, this is more tricky, because you have 1nd level cache (session cache), and possibly also 2nd level cache. With 2nd level cache all changes made to database will be invisible to the Hibernate, if the row is cached, until the cache expires.