当前位置: 首页 > 知识库问答 >
问题:

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

羊舌阎宝
2023-03-14

我正在使用JSF 2.1Hibernate4.1.7Spring3.2.1Spring安全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>

在我的< code>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

提前谢谢你。

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

这是我的应用程序上下文. 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);
    }
}

我的业务逻辑层

//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层类中使用@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 管理豆:

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

DB上的更改不再由服务层提交,但问题是所有流程都完成了(控制权回到客户端),但对DB的提交从未发生!请参阅我的第三个问题更新

共有3个答案

卫飞鹏
2023-03-14

好吧,在看到这个之后,我的猜测是问题出在Spring-JSF集成之一。问题可能是由您使用的是Spring事务管理器引起的。但是你的豆子是由JSF(@ManagedBean@ManagedProperty)管理和注入的。这可能会导致一些意外的事务行为。为了解决这个问题,我的建议是使用@Component而不是@ManagedBean @Autowired,@Inject而不是@ManagedProperty,将所有豆子(或至少与交易相关的豆子)置于Spring控制之下。为了仍然能够通过EL从JSF访问你的豆子,你需要将Spring豆面解析器添加到你的人脸配置中。

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

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

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

哈雅珺
2023-03-14

好的,谢谢大家抽出宝贵时间。解决方案正如斯图尔特所说@Ryan。最后:

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

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

再次感谢大家。

酆出野
2023-03-14

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

 类似资料:
  • 我想使用Spring MVC 和Hibernate。我不想在所有控制器方法上使用服务层或属性(或者,更确切地说,我希望Spring将它们全部视为事务性)。因此,我想在控制器方法开始使用数据库时启动事务,并在控制器方法返回 ViewAndModel 或回滚事务(如果发生任何错误)时提交事务。此外,我希望视图支持延迟Hibernate加载,例如,如果html模板请求,请在自动提交模式下选择数据。 我知

  • 我有以下情况 我有一个REST客户端,它充当其他3个REST客户端的门面。(我正在用Java编程,使用Spring Boot) 客户机的职责之一包括对用户执行CRUD操作 现在,所有其他3个公开自己REST API的系统都有某种用户管理功能。 例如,当我收到创建用户的请求时,我必须通过REST API在这3个系统上创建它们,并将它们保存在我的数据库中。 现在,在最好的情况下,我只是调用他们的API

  • 我正在尝试发送一个经过身份验证的请求,只需点击邮递员。 所以,我有一个名为“Oauth”的请求,我正在使用测试将令牌存储在局部变量中。 我现在要做的是,对于需要承载令牌的任何其他请求,自动运行Oauth请求(从预请求脚本)。 有没有一种方法可以通过单击邮递员按钮来获取访问令牌并发送经过身份验证的请求?

  • 问题内容: 我们有一个weblogic批处理应用程序,它可以同时处理来自使用者的多个请求。我们使用log4j记录目的。现在,我们登录到单个日志文件以处理多个请求。调试给定请求的问题变得很麻烦,因为所有请求都将日志记录在一个文件中。 因此,计划是每个请求只有一个日志文件。使用者发送一个请求ID,必须对其进行处理。现在,实际上可能有多个使用者将请求ID发送到我们的应用程序。因此,问题是如何根据请求隔离

  • 为什么这是一个好主意呢?您通过使用这种方法获得了什么好处?在某些情况下,这会是一个好主意吗?在每个存储库方法调用实例化时,您是否可以使用这种技术做一些不能做的事情?

  • 为什么首先这是一个好主意?通过使用这种方法,您获得了哪些优势?在某些情况下,这是一个好主意吗?在每个存储库方法调用实例化s时,使用此技术是否可以执行一些不能执行的操作?