I'm working in a web app using JSF 2.1 + Hibernate 4.1.7 + Spring 3.2.1 + Spring Security + SQLServer2012. Everything works fine i.e. CRUD operations. But some methods need to work with 2 or more entities (update, add, etc), for example
getEntity1Service().merge(); // line 1
getEntity2Service().create(); // line 2
getEntity3Service().delete(); // line 3
If an error occurs executing line #2 (create entity) I need the merged entity (or update, create) or prior DB function to get rollback so the data on my DB maintain correct
I'm using the OpenSessionInViewFilter
in combination with @Transactional
Spring annotation.
<filter>
<filter-name>hibernateFilter</filter-name>
<filter-class>org.springframework.orm.hibernate4.support.OpenSessionInViewFilter</filter-class>
<init-param>
<param-name>sessionFactoryBeanName</param-name>
<param-value>SessionFactory</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>hibernateFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
And in my GenericDAO
I have
getSessionFactory().getCurrentSession().merge(objeto);
getSessionFactory().getCurrentSession().delete(objeto);
getSessionFactory().getCurrentSession().createQuery(queryString);
What I'm missing? because when the line #1 is executed the data is sent to DB.
Extract from my App LOG
when executing line #1
...
23 05 2013 00:04:46,650 DEBUG [http-apr-8080-exec-345] (e.engine.transaction.internal.jdbc.JdbcTransaction:doCommit:113) - committed JDBC Connection
23 05 2013 00:04:46,650 DEBUG [http-apr-8080-exec-345] (e.engine.transaction.internal.jdbc.JdbcTransaction:releaseManagedConnection:126) - re-enabling autocommit
23 05 2013 00:04:46,651 DEBUG [http-apr-8080-exec-345]
...
when executing line #2
(ork.orm.hibernate4.support.OpenSessionInViewFilter:lookupSessionFactory:188) - Using SessionFactory 'SessionFactory' for OpenSessionInViewFilter 23 05 2013 00:05:27,777 DEBUG [http-apr-8080-exec-349]
(ramework.beans.factory.support.AbstractBeanFactory:doGetBean:246) - Returning cached instance of singleton bean 'SessionFactory' 23 05 2013 00:05:27,777 DEBUG [http-apr-8080-exec-349]
(ork.orm.hibernate4.support.OpenSessionInViewFilter:doFilterInternal:141) - Opening Hibernate Session in OpenSessionInViewFilter 23 05 2013 00:05:27,778 DEBUG [http-apr-8080-exec-349]
(org.hibernate.internal.SessionImpl ::312) - Opened session at timestamp: 13692891277
Thank you in advance.
** thanks for responding, updated question: ****
This is my applicationContext.xml:
<bean id="entity1DAO" class="com.x.dao.generic.GenericDAOHibernateImpl">
<constructor-arg><value>com.x.entities.modules.general.Entity1</value></constructor-arg>
<property name="sessionFactory"><ref bean="SessionFactory"/></property>
</bean>
<bean id="entity2DAO" class="com.x.dao.generic.GenericDAOHibernateImpl">
<constructor-arg><value>com.x.entities.modules.general.Entity2</value></constructor-arg>
<property name="sessionFactory"><ref bean="SessionFactory"/></property>
</bean>
<bean id="entity3DAO" class="com.x.dao.generic.GenericDAOHibernateImpl">
<constructor-arg><value>com.x.entities.modules.general.Entity3</value></constructor-arg>
<property name="sessionFactory"><ref bean="SessionFactory"/></property>
</bean>
<bean id="DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="com.microsoft.sqlserver.jdbc.SQLServerDriver" />
<property name="jdbcUrl" value="jdbc:sqlserver://127.0.0.1:1433;databaseName=db1;user=sa;password=abcde1234" />
<property name="maxPoolSize" value="10" />
<property name="maxStatements" value="0" />
<property name="minPoolSize" value="5" />
</bean>
<bean id="SessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="DataSource" />
<property name="annotatedClasses">
<list>
<value>com.x.entities.modules.configuration.cg.CgEntity1</value>
<value>com.x.entities.modules.configuration.cg.CgEntity2</value>
<value>com.x.entities.modules.configuration.cg.CgEntity3</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">com.x.dao.SqlServer2008Dialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
<prop key="hibernate.id.new_generator_mappings">true</prop>
<prop key="hibernate.format_sql">false</prop>
<prop key="use_sql_comments">true</prop>
</props>
</property>
</bean>
<!--Tells Spring framework to read @Transactional annotation-->
<context:annotation-config/>
<!-- 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>
My Data Access Layer: GenericDAOHibernateImpl.java :
@Transactional(rollbackFor=Exception.class)
public class GenericDAOHibernateImpl <T, PK extends Serializable> implements GenericDAOHibernate<T, PK>, Serializable{
@Override
public void mergeEntity(T object) {
getSessionFactory().getCurrentSession().merge(object);
}
@Override
public void deleteEntity(T object) {
getSessionFactory().getCurrentSession().delete(object);
}
}
My Business Logic Layer> BeanJSF1.java (@ManagedBean):
//Injection to my generic dao:
@ManagedProperty(value = "#{entity1DAO}")
GenericDAOHibernate<Entity1,Integer> entity1Service;
@ManagedProperty(value = "#{entity2DAO}")
GenericDAOHibernate<Entity2,Integer> entity2Service;
@ManagedProperty(value = "#{entity3DAO}")
GenericDAOHibernate<Entity3,Integer> entity3Service;
//other variables and methods
@Transactional(rollbackFor = Exception.class)
public void onUpdatingRowData(RowEditEvent ree) {
try{
getEntity1Service().mergeEntity(ree.getObject());//this get committed on DB
getEntity2Service().deleteEntity(object2);//this fires an Exception
} catch (Exception ex) {
Logger.getLogger(BeanJSF1.class.getName()).log(Level.SEVERE, null, ex);
}
}
I have tried using @Transactional in my GenericDAOHibernateImpl only, GenericDAOHibernateImpl and Bussines Layer class both, but I get the same result all the time
*THIRD QUESTION UPDATE***
Ok, I have added the service layer
DAO Layer:
public interface IGenericDAOHibernate <T, PK extends Serializable>{ ...
public class GenericDAOHibernate <T, PK extends Serializable> implements IGenericDAOHibernate<T, PK>, Serializable{ ...
Service layer:
public interface IGenericDAOHibernateService <T, PK extends Serializable>{ ...
@Transactional(rollbackFor = Exception.class)
public class GenericDAOHibernateImpl <T, PK extends Serializable> implements IGenericDAOHibernateService<T,PK>{
public IGenericDAOHibernate genericDAOHibernate; ...
ApplicationContext.xml:
<bean id="GenericDAOService" class="com.x.services.generic.GenericDAOHibernateImpl"><property name="genericDAOHibernate" ref="GenericDAOHibernate" /></bean>
<bean id="GenericDAOHibernate" class="com.x.dao.generic.GenericDAOHibernate">
<property name="sessionFactory" ref="SessionFactory" />
</bean>
JSF Managed Bean:
@ManagedBean
@ViewScoped
public class BJsfBeanX extends BCommon implements Serializable {
@ManagedProperty(value = "#{GenericDAOService}")
IGenericDAOHibernateService genericService; ...
public void onUpdatingDataRow(RowEditEvent ree) throws Exception {
try{
getGenericService().updateEntity(entity1); //line1: where updateEntity go throw layers and execute on DAO Layer: getSessionFactory().getCurrentSession().merge(object);
//this line at DAO class does not execute commit on data base, the commit is executed when the control is back to the managedBean method line1
getGenericService().updateEntity(entity2);//line2: this throw Exception, but there is nothing to do rollback `cause the entity1 (line 1) has been already committed
}catch
}
I have also tried using the @Transactional on service layer Interface / service layer Class but the commit is still happening when control comes back to JSF managedBean.
Scenario 2. *When removed the @Transactional from service layer and using it on the JSF managed bean method:*
@Transactional(rollbackFor = Exception.class)
public void onUpdatingDataRow(RowEditEvent ree) throws Exception {
the change on DB is not committed by the service layer any more, BUT the problem is that all the flow get finished (control comes back to client side) but the commit to the DB never occurs! Please see my THIRD QUESTION UPDATE