I have created two transaction managers(one for READ requests and another for READ-WRITE requests) and two session factories for the same.
Important snippets are as follows:
<tx:annotation-driven />
<bean id="txManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="SessionFactory" />
</bean>
<bean id="txManagerRead"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="SessionFactory" />
<qualifier value="READ"/>
</bean>
<bean id="txManagerWrite"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="SessionFactory1" />
<qualifier value="WRITE"/>
</bean>
<bean id="AbstractSessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"
abstract="true">
<property name="annotatedClasses">
<list>
.....
....
</list>
</property>
<property name="exposeTransactionAwareSessionFactory">
<value>true</value>
</property>
</bean>
<bean id="SessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"
parent="AbstractSessionFactory">
<property name="hibernateProperties">
<props>
.........
.........
</props>
</property>
</bean>
<bean id="SessionFactory1"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"
parent="AbstractSessionFactory">
<property name="hibernateProperties">
<props>
.........
.........
</props>
</property>
</bean>
I am getting following exception :
Caused by: org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
at org.springframework.orm.hibernate3.SpringSessionContext.currentSession(SpringSessionContext.java:63)
at org.hibernate.impl.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:574)
at com.amazon.im.dao.hibernate.GenericDAOImpl.getSession(GenericDAOImpl.java:53)
at com.amazon.im.dao.hibernate.CategoryDAOImpl.findAllActiveCategories(CategoryDAOImpl.java:54)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
Method which calls DAO is as follows:
#Transactional(rollbackFor = { DependencyException.class, ServiceException.class }, readOnly = true, value="READ")
#Operation("getActiveCategories")
public GetActiveCategoriesResponse getActiveCategories() throws ServiceException, DependencyException
{ .....
......
......
}
Can anyone point me to the part I am doing wrong?
Try using #Transactional("Tx_Manager_Name") as per this . Your configuration seems to be correct since multiple Txn managers are supported through Qualifiers as well .
Your configuration is wrong. Hibernate and even spring allows you to create only one session factory per app. Check out this link : Spring + Hibernate session lifecycle. You can create session per transaction.
Also check this link : http://docs.jboss.org/hibernate/core/3.5/api/org/hibernate/SessionFactory.html
Related
Hi i'm trying to save into two database simultaneously but I always get an error.
Exception in thread "main" org.hibernate.HibernateException: No Session found for current thread
here's my code :
#Transactional(rollbackFor=Exception.class, propagation=Propagation.REQUIRES_NEW, readOnly=false)
public void save(ArsenalPlayer domain1, ArsenalPlayer2 domain2)
throws Exception {
dao1.save(domain1);
dao2.save(domain2);
}
dao1 uses a sessionFactory attached to datasource1
dao2 uses a sessionFactory attached to datasource2
and here's my configuration
DataSource
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver" />
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/arsenal" />
<property name="user" value="root" />
<property name="password" value="ahmids" />
</bean>
SessionFactory
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">update</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
<property name="annotatedClasses">
<list>
<value>com.gongfu4.bean.ArsenalPlayer</value>
</list>
</property>
</bean>
DataSource2
<bean id="dataSource2" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver" />
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/arsenal2" />
<property name="user" value="root" />
<property name="password" value="ahmids" />
</bean>
SessionFactory2
<bean id="sessionFactory2"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource2" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">update</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
<!-- <prop key="hibernate.current_session_context_class">thread</prop> -->
</props>
</property>
<property name="annotatedClasses">
<list>
<value>com.gongfu4.bean.ArsenalPlayer2</value>
</list>
</property>
</bean>
applicationContext
<context:component-scan base-package="com.gongfu4" />
<context:annotation-config />
<import resource="dataSource.xml" />
<import resource="dataSource2.xml" />
<import resource="hibernate.xml" />
<import resource="hibernate2.xml" />
<tx:annotation-driven />
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="myTx"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory2" />
</bean>
and here are my dao classes :
DAO1
#Autowired
SessionFactory sessionFactory;
public void save(ArsenalPlayer domain) {
sessionFactory.getCurrentSession().save(domain);
}
DAO2
#Autowired
SessionFactory sessionFactory2;
public void save(ArsenalPlayer2 domain) {
sessionFactory2.getCurrentSession().merge(domain);
}
Is there something wrong with my configuration?
You cannot use those two transaction managers like that. You have two data sources, two transaction managers and, from your code, I understand that you want the two save operations to be performed in the same transaction. The question is "a transaction from which transaction manager?".
The way you have the code and the configuration, Spring will use the "default" transaction manager because <tx:annotation-driven/> by default will search for a transaction manager bean with the id "transactionManager" and the bean with this id is the one for data source dataSource. But your code is not working and this is the expected behavior. Spring will open a Hibernate session using sessionFactory and the dao1.save(domain1); call will succeed because this is the correct Hibernate session for the correct dataSource. But when the dao2.save(domain2); method is called you will have the same session from dao1 call but be used for a db operation for the second database.
As I see it you have two options:
Use a JTA transaction manager to coordinate the two datasources. With a JTA the two save operations will be atomic. If one fails, then both are rolled back.
Perform the two save(domain) operations in two different transactions properly configuring the #Transactional annotation to use the correct transaction manager. In this case, the two save operations will not be atomic. If one save fails then only that one will be rolled-back. See below an exempt taken from the Spring reference documentation here:
public class TransactionalService {
#Transactional("order")
public void setSomething(String name) { ... }
#Transactional("account")
public void doSomething() { ... }
}
<tx:annotation-driven/>
<bean id="transactionManager1" class="org.springframework.jdbc.DataSourceTransactionManager">
...
<qualifier value="order"/>
</bean>
<bean id="transactionManager2" class="org.springframework.jdbc.DataSourceTransactionManager">
...
<qualifier value="account"/>
</bean>
Your current configuration doesn't support global transactions (XA) and therefore you can't span one Transaction on two different databases.
For this reason you need two Hiberante transaction managers, one for each session factory. Then you need to instruct the transactional service which transaction manager should be used.
So instead of:
#Transactional(rollbackFor=Exception.class, propagation=Propagation.REQUIRES_NEW, readOnly=false)
public void save(ArsenalPlayer domain1, ArsenalPlayer2 domain2)
throws Exception {
dao1.save(domain1);
dao2.save(domain2);
}
you should have:
<bean id="txManager1" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory1" />
</bean>
<bean id="txManager2" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory2" />
</bean>
#Transactional(rollbackFor=Exception.class, propagation=Propagation.REQUIRES_NEW, readOnly=false, value="txManager1")
public void save(ArsenalPlayer domain1)
throws Exception {
dao1.save(domain1);
}
#Transactional(rollbackFor=Exception.class, propagation=Propagation.REQUIRES_NEW, readOnly=false, value="txManager2")
public void save(ArsenalPlayer domain2)
throws Exception {
dao2.save(domain2);
}
This is odd but I may be missing something here
I am using quartz to initialize the spring config xml. When I trigger the quartz job first time, the transaction gets rolled back when a Exception occurs. But when the same job is triggered again, the transaction is not rolled back for the same Exception.
I can see from the log that when the job is triggered first time, the datasource (jndi lookup of Sybase Jconnect 7 from Websphere datasources) is initialized (not second time).
Service Layer
#Transactional(value="transactionManager", propagation=Propagation.REQUIRES_NEW, rollbackFor=Exception.class)
public void saveInfo(
List<Foo> saveData) throws Exception {
try{
myDAO.saveInfo(saveData); //data to rollback on exception. Works First time only :(
}
catch(Exception ex){
logBO.createActivityLog("Error Saving Data");
throw ex;
}
}
Spring-config.xml
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory">
<ref local="sessionFactory"/>
</property>
</bean>
I have played around with all propagation settings REQUIRED/REQUIRES_NEW/without propagation.
DAO method does a bulk update (jdbcTemplate)
EDIT
During the first time, this line is logged
J2CA0086W: Shareable connection MCWrapper id 4c384c38 Managed
connection WSRdbManagedConnectionImpl#6f5a6f5a
State:STATE_TRAN_WRAPPER_INUSE from resource mydatasource was used
within a local transaction containment boundary.
try this :
#TransactionConfiguration(transactionManager = "txManager",defaultRollback = true)
#Transactional
hibernate and datasource code :
<!-- Hibernate Related Configuration. -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="org.postgresql.Driver"/>
<property name="url" value="jdbc:postgresql://192.168.1.9:5432/dbname"/>
<property name="username" value="postgres"/>
<property name="password" value="pwd"/>
<property name="validationQuery" value="SELECT 1"/>
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="packagesToScan" value="com.domain"/>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.generate_statistics">true</prop>
</props>
</property>
</bean>
and txManager :
<!-- Transaction Manager -->
<bean id="txManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<tx:annotation-driven transaction-manager="txManager" />
don't forget to add line :
<property name="packagesToScan" value="com.domain"/>
in sessionfactory, due to this so many times problem occur.
How would I setup container-managed datasources and embedded Active MQ resources to JTATransactionManager for global Transactions?
I am using Tomcat 6 and installed Atomikos in it to support JTA. I use Hibernate for ORM. Here is my configuration:
<bean id="AtomikosTransactionManager"
class="com.atomikos.icatch.jta.UserTransactionManager"
init-method="init" destroy-method="close">
<!-- when close is called, should we force
transactions to terminate or not? -->
<property name="forceShutdown" value="false" />
</bean>
<bean id="AtomikosUserTransaction"
class="com.atomikos.icatch.jta.UserTransactionImp">
<property name="transactionTimeout" value="300" />
</bean>
<jee:jndi-lookup expected-type="javax.sql.DataSource" id="dataSource" jndi-name="jdbc/EDITSOLUTIONS"/>
<bean id="sessionFactory" class="org.springframework.orm.hibernate.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mappingResources"/>
<list>
<value>../../src/editsolutions.hibernate.cfg.xml</value>
</list>
<property name="hibernateProperties">
<value>
hibernate.dialect=${hibernate.dialect}
</value>
</property>
</bean>
<bean id="activemq" class="org.apache.activemq.camel.component.ActiveMQComponent">
<property name="connectionFactory">
<bean class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="failover://tcp://localhost:61616"/>
</bean>
</property>
</bean>
<bean id="jmsConnectionFactory"class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="failover://tcp://localhost:61616"/>
</bean>
<bean name="txManager" class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionManager" ref="AtomikosTransactionManager" />
<property name="userTransaction" ref="AtomikosUserTransaction" />
</bean>
Spring documentation says that JTA Transaction Manager need not be told about resources. That's what I have done.
I have the following outstanding questions:
I am not sure whether Atomikos is integrated properly or not?
Is it OK to get the datasource from <jee:jndi-lookup>?
Is Hiberante configuration correct with respect to JTATransactionManager?
As it is embedded in JVM not managed by container, would JTATransactionManager be able to recognize ActimeMQ ?
Try with this very useful link: http://www.atomikos.com/Documentation/SpringIntegration
Remember to configure the datasource in this way:
<bean id="dataSourceA" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close">
<qualifier value="jmsRecoveryDatabaseSchema"/>
<property name="uniqueResourceName"><value>XADS1</value></property>
<property name="xaDataSourceClassName">
<value>oracle.jdbc.xa.client.OracleXADataSource</value>
</property>
<property name="xaProperties">
<props>
<prop key="URL">${jdbc.url}</prop>
<prop key="user">${jdbc.username}</prop>
<prop key="password">${jdbc.password}</prop>
</props>
</property>
<property name="poolSize"><value>1</value></property>
<property name="testQuery" value="SELECT 1 FROM DUAL"/>
</bean>
I hope it helps!
I am using Hibernate4,Spring3 and JSF2 for a small application and Weblogic 10.3.6 as Apps server.
In order to enable JPA2 I have added the following in commEnv.cmd
#rem Enable JPA 2.0 functionality on WebLogic Server
set PRE_CLASSPATH=%BEA_HOME%\modules\javax.persistence_1.1.0.0_2-0.jar;
%BEA_HOME%\modules\com.oracle.jpa2support_1.0.0.0_2-1.jar
When I run my application I am getting null pointer exception at the following line. How can I resolve this?
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
My DAO
#Named
public class RequestDAOImpl implements RequestDAO {
protected EntityManager entityManager;
public void getRequest(RequestQueryData data){
Map<String, String> filters = data.getFilters();
int start = data.getStart();
int end = data.getEnd();
String sortField = data.getSortField();
QuerySortOrder order = data.getOrder();
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Request> c = cb.createQuery(Request.class);
Root<Request> emp = c.from(Request.class);
c.select(emp);
...... other code
applicationContext.xml
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="view">
<bean class="org.primefaces.spring.scope.ViewScope" />
</entry>
</map>
</property>
</bean>
<context:component-scan base-package="net.test" />
<!-- Data Source Declaration -->
<bean id="DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass" value="oracle.jdbc" />
<property name="jdbcUrl"
value="jdbc:oracle:thin:#server:1521:ORCL" />
<property name="user" value="scott" />
<property name="password" value="tiger" />
<property name="maxPoolSize" value="10" />
<property name="maxStatements" value="0" />
<property name="minPoolSize" value="5" />
</bean>
<!-- Session Factory Declaration -->
<bean id="SessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="DataSource" />
<property name="annotatedClasses">
<list>
<value>net.test.model.Request</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.query.factory_class">org.hibernate.hql.internal.classic.ClassicQueryTranslatorFactory
</prop>
</props>
</property>
</bean>
<!-- Enable the configuration of transactional behavior based on annotations -->
<tx:annotation-driven transaction-manager="txManager" />
<!-- Transaction Manager is defined -->
<bean id="txManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="SessionFactory" />
</bean>
Update 1
applicationContext.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!-- Spring view scope customized -->
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="view">
<bean class="org.primefaces.spring.scope.ViewScope" />
</entry>
</map>
</property>
</bean>
<context:component-scan base-package="net.test" />
<!-- Data Source Declaration -->
<bean id="DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass" value="oracle.jdbc" />
<property name="jdbcUrl"
value="jdbc:oracle:thin:#server:1521:ORCL" />
<property name="user" value="scott" />
<property name="password" value="tiger" />
<property name="maxPoolSize" value="10" />
<property name="maxStatements" value="0" />
<property name="minPoolSize" value="5" />
</bean>
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
<!-- JPA Entity Manager Factory -->
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="DataSource" />
<property name="packagesToScan" value="net.test.model" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
</bean>
</property>
</bean>
<bean id="defaultLobHandler" class="org.springframework.jdbc.support.lob.DefaultLobHandler" />
<!-- Session Factory Declaration -->
<bean id="SessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="DataSource" />
<property name="annotatedClasses">
<list>
<value>net.test.model.Request</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.query.factory_class">org.hibernate.hql.internal.classic.ClassicQueryTranslatorFactory
</prop>
</props>
</property>
</bean>
<!-- Enable the configuration of transactional behavior based on annotations
<tx:annotation-driven transaction-manager="txManager" />-->
<!-- Transaction Manager is defined
<bean id="txManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="SessionFactory" />
</bean>-->
<!-- Transaction Config -->
<bean id="transactionManager"
class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<bean id="txManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="SessionFactory" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
<tx:annotation-driven transaction-manager="txManager"/>
<context:annotation-config/>
<bean id="hibernateStatisticsMBean" class="org.hibernate.jmx.StatisticsService">
<property name="statisticsEnabled" value="true" />
<property name="sessionFactory" value="#{entityManagerFactory.sessionFactory}" />
</bean>
<bean name="ehCacheManagerMBean"
class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" />
<bean id="mbeanServer" class="org.springframework.jmx.support.MBeanServerFactoryBean">
<property name="locateExistingServerIfPossible" value="true" />
</bean>
<bean id="jmxExporter" class="org.springframework.jmx.export.MBeanExporter" lazy-init="false">
<property name="server" ref="mbeanServer" />
<property name="registrationBehaviorName" value="REGISTRATION_REPLACE_EXISTING"/>
<property name="beans">
<map>
<entry key="SpringBeans:name=hibernateStatisticsMBean" value-ref="hibernateStatisticsMBean" />
<entry key="SpringBeans:name=ehCacheManagerMBean" value-ref="ehCacheManagerMBean" />
</map>
</property>
</bean>
</beans>
Your EntityManager doesn't appear to be wired into your DAO. Add #Autowired, #PersistenceContext or a ref in your XML. Note that to just use #Autowired, you'll have to specify EntityManager as a bean.
Another possibility: if your DAO isn't also specified as a bean (either in the XML or using one of the various #Component annotatons (probably #Repository), Spring won't know to wire things in, either.
Update:
There's a couple different solutions here. Before those, though, make sure that you have
<mvc:annotation-driven />
In one of your XMLs. This will enable the spring annotations and save you a lot of headache from editing XMLs. Note that you'll also need to update the xmlns and schemaLocation in the <beans> tag.
xmlns:mvc="http://www.springframework.org/schema/mvc"
and
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
in the schemaLocation.
It looks like you're now specifying an EntityManagerFactory. That's a good start. You can now specify an EntityManager, too.
<bean id="entityManager" class="org.springframework.orm.jpa.support.SharedEntityManagerBean">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
I can't vouch for your XML and EntityManagerFactory settings, though. You seem to have an </property> floating out there in the middle of nowhere.
I'm not sure how you're accessing your DAO. If it's already a bean and within the component-scan, you're good. If not, make sure to annotate your DAO class with #Repository and make sure its package is within the component-scan. Of course, if you don't already have it specified as a bean, that implies that you're possibly instantiating it elsewhere -- this is absolutely not how you want to be using your DAO. Should this be the case, I strongly recommend reading up on Spring's dependency injection.
Now you need to wire in your EntityManager. This can be done in two ways.
The first way requires that you specified it as a bean in your XML. If you've done that, just annotate your EntityManager field.
#Autowired
protected EntityManager entityManager;
Alternatively, since you're specifying a DataSource in your XML, you SHOULD be able to reference it by using #PersistenceContext and passing it a value of the ID.
#PersistenceContext(name="DataSource")
protected EntityManager entityManager;
I've never really used the latter method, but I've seen it done that way. I normally specify an EntityManager bean in the XML and use #Autowired, as described in the former method.
I am using Spring and Hibernate with Jta Transactions, I have 2 databases, and I have a problem in a transactional method.
In this method I insert a lot of objects but I throws an exception to rollback the insertions, here the code works as I expected because the objects dont appear into the database.
But if I add a line in the method that get the objects of the same table, the objects are committed into the database.
I think that when I make a SELECT the objects are auto-committed, because the exception its thrown again and the objects persists into the database.
My xml and code:
dao.xml
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" value="classpath:configuracion_dao.properties" />
</bean>
<bean name="productosDAO" class="practica1.hibernate.HibernateProductosDAOImpl"
parent="abstractPracticaBean">
<property name="sessionFactory" ref="hibernateSessionFactory" />
</bean>
<bean name="tercerosDAO" class="${tercerosDAO.classname}" parent="abstractPracticaBean">
<property name="dataSource" ref="dataSourceDatos" />
</bean>
<bean name="auditoriaDAO" class="practica1.hibernate.HibernateAuditoriaDAOImpl" parent="abstractPracticaBean">
<property name="sessionFactory" ref="hibernateSessionFactory2" />
</bean>
<bean id="hibernateSessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSourceDatos" />
<property name="mappingResources">
<list>
<value>hibernate-mappings.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<value>
hibernate.dialect=org.hibernate.dialect.HSQLDialect
</value>
</property>
</bean>
<bean id="hibernateSessionFactory2"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSourceAuditoria" />
<property name="mappingResources">
<list>
<value>hibernate-mappings-auditoria.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<value>
hibernate.dialect=org.hibernate.dialect.HSQLDialect
</value>
</property>
</bean>
<bean name="dataSourceDatos" class="org.enhydra.jdbc.standard.StandardXADataSource">
<property name="driverName" value="org.apache.derby.jdbc.EmbeddedDriver" />
<property name="url" value="jdbc:derby:/tmp/datos.db;create=true" />
<property name="transactionManager" value="#{txManager.transactionManager}" />
</bean>
<jdbc:initialize-database data-source="dataSourceDatos"
ignore-failures="ALL">
<jdbc:script location="classpath:practica1/sql/creacion_derby.sql" />
<jdbc:script location="classpath:practica1/sql/datos.sql" />
</jdbc:initialize-database>
<bean name="dataSourceAuditoria" class="org.enhydra.jdbc.standard.StandardXADataSource">
<property name="driverName" value="org.apache.derby.jdbc.EmbeddedDriver" />
<property name="url" value="jdbc:derby:/tmp/auditoria.db;create=true" />
<property name="transactionManager" value="#{txManager.transactionManager}" />
</bean>
<jdbc:initialize-database data-source="dataSourceAuditoria"
ignore-failures="ALL">
<jdbc:script location="classpath:practica1/sql/creacion_auditoria_derby.sql" />
</jdbc:initialize-database>
<bean id="txManager"
class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionManager" value="#{jotm.transactionManager}" />
<property name="userTransaction" value="#{jotm.userTransaction}" />
</bean>
<bean id="jotm" class="org.objectweb.jotm.Jotm" destroy-method="stop">
<constructor-arg value="true" />
<constructor-arg value="false" />
</bean>
<tx:annotation-driven transaction-manager="txManager" />
bo.xml
<bean name="tercerosBO" class="practica1.impl.TercerosBOImpl"
parent="abstractPracticaBean" autowire="constructor">
</bean>
<bean name="productosBO" class="practica1.impl.ProductosBOImpl"
parent="abstractPracticaBean">
<property name="productosDAO" ref="productosDAO" />
<property name="auditoriaDAO" ref="auditoriaDAO" />
</bean>
aplicacion.xml
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames" value="mensajes" />
</bean>
<bean id="abstractPracticaBean" class="practica1.impl.AbstractPracticaBean" abstract="true">
<property name="messageSource" ref="messageSource"></property>
</bean>
<import resource="bo.xml" />
<import resource="dao.xml" />
Transactional method:
#Transactional
#Override
public void actualizaPrecio(double porcentaje) {
internalActualizaPrecio(porcentaje);
}
private void internalActualizaPrecio(double porcentaje) {
auditoriaDAO.insertAuditoria(getMessageSource().getMessage(
"mensaje.actualizar_productos", new Object[] { porcentaje },
null));
int i = 0;
auditoriaDAO.getAuditorias(); // Without this line its works like I expected
List<Producto> productos = productosDAO.getProductos();
for (Producto producto : productos) {
i++;
if (i > 3)
throw new UnsupportedOperationException(
"Error para que veamos las transacciones");
producto.setPrecio(producto.getPrecio().multiply(
new BigDecimal(porcentaje).divide(new BigDecimal(100))));
productosDAO.updateProducto(producto);
}
}
I realised that if I use auditoriaDAO.getAuditorias() the rollback only affects to Producto but if I use productoDAO.getProductos() the rollback only affects to Auditoria...
You may be mixing up flush and commit here: a SELECT statement usually flushes all previous SQL statements, in order to fetch up-to-date data (regarding the previous changes you made in the tx). It may be possible that before such a SELECT statement is done (the following DAO calls are made to the 2nd sessionFactory if I'm not mistaken), the exception exits the method without a flush. Hence no modification in database.
So the question is: are you sure you're rollbacking the tx effectively? I see you've annotated a private method: the proxy-based mechanism of Spring AOP don't handle that! You must annotate a public method and call it from outside the annotated method's class, due to this very proxy-based mechanism. See the "Method visibility and #Transactional" block in the documentation.
Another lead: you have 2 sessionFactories, so I assume you're using XA transactions/datasources: are you sure this part of the conf is OK?
Please check auditoriaDAO and productosDAO and search for other transactional annotation. I think a new transaction is created somewhere and the UnsupportedException rollbacks only the last transaction, and the parent transaction is committed. Hope I helped!
I have found two example. Please check it.
JOTM transactions in spring and hibernate
Access Multiple Database Using Spring 3, Hibernate 3 and Atomikos