2

我正在使用 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 行(创建实体)发生错误,我需要合并实体(或更新、创建)或先前的 DB 函数来回滚,以便我的 DB 上的数据保持正确

我正在OpenSessionInViewFilter结合使用@TransactionalSpring 注释。

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

从我的应用程序日志中提取

执行第 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);
    }
}

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

我曾尝试在我的GenericDAOHibernateImplGenericDAOHibernateImpl 和 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; ...

应用程序上下文.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 托管 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
}

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

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

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

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

4

4 回答 4

3

您已经使您的 DAO 具有事务性,因此每个 DAO 方法都会导致一个单独的事务也就不足为奇了。DAO 方法永远不应该是事务性的。事务属于服务层。

于 2013-05-25T12:02:54.357 回答
1

好的,谢谢大家的时间。解决方案正如@Ryan Stewart所说。总之:

  1. 使用服务层
  2. 如果您需要同时处理多个实体,则在服务层而不是业务逻辑层处理所有实体
  3. 在服务层的方法将控制权返回给业务逻辑层的方法之后,如果出现问题,则不会将任何内容持久化到数据库;但是如果没有触发异常,那么所有实体都会被提交。

还有一件事,我添加了一个从我的 GenericDAOService 扩展的新 DaoService,因此在这个新服务实现中,我具有第 2 点提到的特定逻辑。

再次感谢大家。

于 2013-05-29T06:36:20.717 回答
0

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

<application>
    <el-resolver>
            org.springframework.web.jsf.el.SpringBeanFacesELResolver
    </el-resolver>
</application>

这是一个更一步一步解释的基本教程:http ://www.mkyong.com/jsf2/jsf-2-0-spring-integration-example/

即使这不能解决手头的直接问题,它仍然是有益的,并使项目更加一致且不易出错。

于 2013-05-25T11:50:40.790 回答
0

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

  1. 从第一个代码中截取的所有 3 行代码都必须包含在一个事务中
  2. 事务注释必须由 Spring 提取,并且必须在运行时应用实际事务。由于多个问题,这一点可能不起作用。为确保事务已启动,请在第一行设置一个刹车点并检查堆栈跟踪。您必须在堆栈中看到事务拦截器。
  3. 为确保您不会错过任何内容,最好在任何异常类型的情况下表明您想要回滚(默认情况下,回滚将仅针对 RuntimeException 及其子类触发)
  4. 编辑。要强制回滚,异常必须跨越事务边界,这意味着: a) 您根本不使用 try/catch;b) 或者如果你使用 try/catch 那么你会抛出异常(参见我的第二个代码片段)
@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
    }
}
于 2013-05-24T09:30:29.820 回答