Wie zu verwenden einer Transaktion pro Anfrage Spring + Hibernate + Spring Security + JSF
Arbeite ich in einer web-Anwendung mit JSF 2.1 + Hibernate 4.1.7 + Feder 3.2.1 + Spring Security + SQLServer2012.
Funktioniert alles wunderbar, dh von CRUD-Operationen. Aber einige Methoden arbeiten müssen, um mit 2 oder mehr Personen (aktualisieren, hinzufügen, etc), zum Beispiel
getEntity1Service().merge(); //line 1
getEntity2Service().create(); //line 2
getEntity3Service().delete(); //line 3
Wenn ein Fehler Auftritt, Ausführung von Zeile #2 (create-Entität) muss ich das zusammengeschlossene Unternehmen (oder auch update, create) oder vor der DB-Funktion verwenden, um rollback, so dass die Daten auf meinem DB-achten Sie auf richtige
Ich bin mit der OpenSessionInViewFilter
in Kombination mit @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>
Und in meinem GenericDAO
ich habe
getSessionFactory().getCurrentSession().merge(objeto);
getSessionFactory().getCurrentSession().delete(objeto);
getSessionFactory().getCurrentSession().createQuery(queryString);
Was mir fehlt? weil, wenn die Linie #1 ausgeführt wird, werden die Daten zu DB.
Auszug aus meiner App LOG
beim ausführen von Zeile #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]
...
beim ausführen von Zeile #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
Vielen Dank im Voraus.
** vielen Dank für die Beantwortung, aktualisierte Frage: ****
Das ist mein 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>
Meine 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);
}
}
Meine Business Logik 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);
}
}
Ich habe versucht mit @Transactional in meinem GenericDAOHibernateImpl nur GenericDAOHibernateImpl und Bussines-Schicht-Klasse beide, aber ich bekomme das gleiche Ergebnis die ganze Zeit
*DRITTE FRAGE UPDATE***
Ok, ich habe die service-Schicht
DAO-Schicht:
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
}
Ich habe auch versucht, mit der @Transactional auf service layer Interface /service-layer-Klasse, aber der commit ist noch passiert, wenn die Kontrolle kommt zurück, um JSF-managedBean.
Szenario 2.
*, Wenn Sie entfernt die @Transactional-von service-Schicht und mit auf der JSF managed bean Methode:*
@Transactional(rollbackFor = Exception.class)
public void onUpdatingDataRow(RowEditEvent ree) throws Exception {
dem Wechsel auf DB ist nicht verpflichtet, die von der service-Schicht mehr, ABER das problem ist, dass die Strömung fertig zu werden (Kontrolle kommt zurück zur client-Seite), aber die commit auf der DB tritt nie ein! Bitte Lesen Sie meine DRITTE FRAGE UPDATE
- Was die transaction-manager benutzt du, und wie ist die konfiguriert? Auch die Frage, wie werden Ihre Transaktionen einzurichten. Von dem was ich verstehe das problem nicht zu sein scheinen mit der OpenSessionInViewFilter, sondern die Transaktion-setup, da es einen dedizierten verpflichten, nach der Zeile #1.
- Das default-Verhalten
OpenSessionInViewFilter
ist, wickeln Sie die gesamte Anfrage mit einem nur-lese-Transaktion, um zu unterstützen, lazy loading in JSPs. Es spielt keine commit-Transaktionen und meint, dass die service-Schicht@Transactional
Methoden Griff übertragen der Daten in die Datenbank. Sind Ihremerge()
undcreate()
Operationen geschieht innerhalb der gleichen@Transactional
Methode? Wenn nicht, ich denke, Sie werden behandelt werden als selbständige Geschäfte. - Vielen Dank für die Beantwortung. Ich aktualisiert meine Frage für @Carsten auch.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Sie haben Ihren DAO transaktionale, also sollte es nicht verwunderlich sein, dass jedes DAO-Methode bewirkt, dass eine separate Transaktion. DAO-Methoden sollten nie transaktional sein. Geschäfte gehören in die service-Schicht.
<tx:annotation-driven/>
deklariert ist. Dies ist ein häufiger Fehler, wenn Sie nicht vollständig verstehen, wie die Feder funktioniert. Siehe, z.B., stackoverflow.com/questions/7561360/... und stackoverflow.com/questions/10538345/....Ok danke Euch allen für Eure Zeit. Die Lösung ist als @Ryan Stewart sagte. Und abschließend:
Eine weitere Sache, ich habe eine neue DaoService, die sich aus meiner GenericDAOService so in dieser neuen service-Implementierung habe ich die Besondere Logik wie bereits erwähnt auf Punkt Nummer 2.
Dank noch einmal an Euch alle.
Ok, nach dem sehen dieser meine Vermutung ist, dass das problem einer Spring-JSF integration. Das problem könnte verursacht werden durch die Tatsache, dass Sie mit einem Spring-Transaktions-Manager. Aber deine Bohnen verwaltet werden, und injiziert durch JSF (
@ManagedBean
und@ManagedProperty
). Dies könnte dazu führen, dass einige unerwartete Transaktion Verhalten.Um dieses Problem zu lösen wäre mein Vorschlag, um alle Ihre Bohnen (oder zumindest die, die relevant für die Geschäfte) unter Frühling controll mit
@Component
statt@ManagedBean
und@Autowired
oder@Inject
statt@ManagedProperty
.Um weiterhin Zugriff auf Ihre Beans von JSF über EL müssen Sie den
SpringBeanFacesELResolver
auf Ihre faces-config wie diese.Hier ist ein einfaches tutorial für eine Schritt für Schritt Erklärung: http://www.mkyong.com/jsf2/jsf-2-0-spring-integration-example/
Selbst wenn dies nicht lösen das unmittelbare problem an der hand ist es noch vorteilhaft und macht das Projekt mehr konsistent und weniger fehleranfällig.
Bekommen rollback müssen Sie sicherstellen, dass folgende Dinge:
Zweite code-snippet (Ausnahmen):