如何在每个请求中使用一个事务Spring + Hibernate + Spring Security + JSF

时间:2013-05-23 21:09:29

标签: java spring hibernate jsf-2 spring-security

我正在使用JSF 2.1 + Hibernate 4.1.7 + Spring 3.2.1 + Spring Security + SQLServer2012在Web应用程序中工作。 一切正常,即CRUD操作。但是某些方法需要使用2个或更多实体(更新,添加等),例如

getEntity1Service().merge();  // line 1
getEntity2Service().create(); // line 2
getEntity3Service().delete(); // line 3

如果执行第2行(创建实体)时发生错误,我需要合并实体(或更新,创建)或之前的数据库函数来进行回滚,以便我的数据库上的数据保持正确

我正在将OpenSessionInViewFilter@Transactional Spring注释结合使用。

<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>

GenericDAO我有

getSessionFactory().getCurrentSession().merge(objeto);
getSessionFactory().getCurrentSession().delete(objeto);
getSessionFactory().getCurrentSession().createQuery(queryString);

我缺少什么?因为当执行第1行时,数据将被发送到DB。

从我的App LOG中提取

执行第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]
...

执行第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

提前谢谢。

** 感谢您回复,更新了问题: ****

这是我的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>

我的数据访问层: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);
    }
}

我的业务逻辑层&gt; 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);
        }
    }

我曾尝试在我的 GenericDAOHibernateImpl GenericDAOHibernateImpl和Bussines Layer类中使用@Transactional,但我总是得到相同的结果

*第三个问题更新***

好的,我添加了服务层

DAO图层:

public interface IGenericDAOHibernate <T, PK extends Serializable>{ ...

public class GenericDAOHibernate <T, PK extends Serializable> implements IGenericDAOHibernate<T, PK>, Serializable{ ...

服务层:

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
}

我也尝试在服务层接口/服务层Class上使用@Transactional,但是当控制权回到JSF managedBean时,仍然会发生提交。

场景2。 * 从服务层删除@Transactional并在JSF托管bean方法上使用它时: *

@Transactional(rollbackFor = Exception.class)
    public void onUpdatingDataRow(RowEditEvent ree) throws Exception {

数据库的更改不再由服务层提交,但问题是所有流程都已完成(控制权返回到客户端),但是对DB的提交永远不会发生!请参阅我的第三个问题更新

4 个答案:

答案 0 :(得分:3)

您已经创建了DAO事务,因此每个DAO方法导致单独的事务都不足为奇。 DAO方法永远不应该是事务性的。交易属于服务层。

答案 1 :(得分:1)

好的,谢谢大家的时间。解决方案是 @Ryan Stewart 所说的。最后:

  1. 使用服务层
  2. 如果您需要同时使用多个实体,请在服务层一侧而不是bussines逻辑层工作
  3. 在服务层上的方法返回控制到业务逻辑层方法之后,如果出现问题,则没有任何内容持久存在于DB;但如果没有触发异常,那么所有实体都会被提交。
  4. 还有一件事,我添加了一个从我的GenericDAOService扩展的新DaoService,所以在这个新的服务实现中,我有第2点提到的特定逻辑。

    再次感谢大家。

答案 2 :(得分:0)

要获得回滚,您必须确保以下事项:

  1. 第一个代码剪切的所有3行必须包装在一个事务中
  2. 必须由Spring选择事务注释,并且必须在运行时应用实际事务。由于多个问题,这一点可能无效。为了确保事务开始,在第一行上制作一个制动点并检查堆栈跟踪。您必须在堆栈中看到事务拦截器。
  3. 为了确保您不会遗漏任何内容,最好在 ANY 异常类型的情况下指示您需要回滚(默认情况下,仅针对RuntimeException及其子类触发回滚)
  4. 编辑。要强制回滚,必须交叉事务边界,这意味着:a)您根本不使用try / catch; b)或者如果你使用try / catch然后你抛出异常(参见我的第二个代码片段)
  5. @Transactional(rollbackFor=Exception.class)
    public void doInTransaction() {
        getDAOImplEntity1().merge();  // line 1
        getDAOImplEntity2().create(); // line 2
        getDAOImplEntity3().delete(); // line 3
    }
    

    第二个代码段(例外):

    @Transactional(rollbackFor=Exception.class)
    public void doInTransaction() throws Exception {
        try{
            getDAOImplEntity1().merge();  // line 1
            getDAOImplEntity2().create(); // line 2
            getDAOImplEntity3().delete(); // line 3
        } catch (Exception ex) {
            Logger.getLogger(BeanJSF1.class.getName()).log(Level.SEVERE, null, ex);
            throw ex; // Very important!!! Without this, exception do not cross transaction boundaries (`doInTransaction()` method) and Spring wan't do rollback for you
        }
    }
    

答案 3 :(得分:0)

好的,看到这个后我的猜测是问题是Spring-JSF集成之一。问题可能是由于您使用的是Spring事务管理器。但是您的Bean由JSF(@ManagedBean@ManagedProperty)管理和注入。这可能会导致一些意外的事务行为。   为了解决这个问题,我的建议是使用@Component代替@ManagedBean@Autowired或{{1}将所有bean(或至少与交易相关的bean)置于spring controll下而不是@Inject。     要仍然能够通过EL从JSF访问您的Beans,您需要将@ManagedProperty添加到您的faces-config中。

SpringBeanFacesELResolver

以下是更详细说明的基本教程:http://www.mkyong.com/jsf2/jsf-2-0-spring-integration-example/

即使这不能解决眼前的问题,它仍然是有益的,使项目更加一致,不易出错。