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

事务的强制回滚导致嵌套事务也回滚?

澹台权
2023-03-14

情景故事时间:
我“继承”了一个程序,一个用于访问数据库的相当简单的webservice。该程序在某个地方有一个缺陷:它试图更新一个没有更新授权的表。该程序只具有更新数据库队列的权限(Oracle),以保存谁访问了什么信息。这是不受欢迎的行为,现在我纠正了它。注意:这与这个问题本身无关,它只是导致我提出这个问题的原因。

该程序使用Spring+Hibernate来管理和访问数据和事务。

因为程序的需求量很大,而且错误被认为是无法忍受的,我有了一个快速的想法,添加了一个修补程序,简单地对每个事务强制回滚,直到我找到软件中实际操作它不应该操作的数据的部分。

相关代码段(为了清晰起见)

@Override
@Transactional
@PreAuthorize(Rollen.INFOVN)
public InfoVNAntwort infoVNAnfrage(final InfoVNAnfrage infoVNAnfrage) {

    // extract data from request
    final InfoVNDatenhalter datenhalter = (InfoVNDatenhalter) this.getController().erzeugeNeuenDatenhalter(ProzessNamen.INFOVN);
    datenhalter.setAnfrageFin(StringUtils.trimToNull(infoVNAnfrage.getFIN()));
    datenhalter.setAnfrageZB2(StringUtils.trimToNull(infoVNAnfrage.getZB2()));
    final String username = this.getCurrentUserName();
    datenhalter.setBenutzerkennung(username);
    datenhalter.setErgaenzungstext(infoVNAnfrage.getErgaenzungstext());
    datenhalter.setAnfragegrund(infoVNAnfrage.getAnfrageanlass());

    // actual fetch of database data
    final DialogAntwort da = (DialogAntwort) this.getController().verarbeite(datenhalter);

    // convert to ws response
    final InfoVNAntwort vnAntwort = this.getMapper().map(da, InfoVNAntwort.class);

    // log who did what
    this.erstelleBewirt(vnAntwort, infoVNAnfrage, new DateTime(), username);

    // roll back outer transaction
    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

    return vnAntwort;
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
private void erstelleBewirt(final InfoVNAntwort vnAntwort, final InfoVNAnfrage infoVNAnfrage, final DateTime zeitpunktAuskunft, final String username) {
    final InfoVNBewirt eintrag = new InfoVNBewirt();
    eintrag.setZeitpunktErteilungAuskunft(zeitpunktAuskunft);
    eintrag.setSteuerelement(STEUERELEMENT_INFOVN);
    eintrag.setAnfrageAnlass(infoVNAnfrage.getAnfrageanlass());
    this.completeEintrag(username, eintrag);
    this.logdatenPersister.persistiereLogdaten(eintrag);
}

数据库-连接:

    <bean id="sessionFactory"
        class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
        <property name="dataSource" ref="dataSource"></property>
        <property name="packagesToScan">
            <list>
                <value>de.mm.vwn</value>
                <value>de.mm.cfo.allgemein.kenauthent</value>
                <value>de.mm.cfo.infovn.logdaten</value>
            </list>
        </property>
        <property name="hibernateProperties" ref="hibernateProperties"></property>
    </bean>

    <bean id="hibernateProperties"
        class="org.springframework.beans.factory.config.PropertiesFactoryBean">
        <property name="properties">
            <props>
                <prop key="hibernate.dialect">${hibernate.dialect}</prop>
                <prop key="hibernate.hbm2ddl.auto">none</prop>
                <prop key="hibernate.show_sql">false</prop>
                <prop key="hibernate.format_sql">false</prop>
                <prop key="hibernate.cache.use_second_level_cache">true</prop>
<prop key="hibernate.cache.use_query_cache">true</prop>
<prop key="hibernate.cache.region.factory_class">net.sf.ehcache.hibernate.EhCacheRegionFactory</prop>
<prop key="hibernate.jdbc.use_scrollable_resultset">true</prop>
<prop key="hibernate.jdbc.batch_size">25</prop>
            </props>
        </property>
    </bean>

    <bean id="transactionManager"
        class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="dataSource" ref="dataSource" />
        <property name="sessionFactory" ref="sessionFactory"></property>
    </bean>

    <tx:annotation-driven />

共有1个答案

祁永嘉
2023-03-14

好的。问题就是我想的那个。

以下是Spring如何使bean成为事务性的:当您从Spring bean工厂获得事务性bean时,或者由于依赖项注入,Spring不会给出bean类的实例。它为您提供了一个与bean类具有相同接口的代理,并将所有方法调用委托给bean类的一个实例,除了它在调用方法之前启动一个事务(如果需要),并在方法返回后回滚/提交事务(如果需要):

Client ----> transactional proxy ----> bean.infoVNAnfrage()

如果从bean类实例(infovnantwort)调用同一bean的另一个方法,则方法调用不通过代理:

Client ----> transactional proxy ----> bean.infoVNAnfrage() ----> bean.erstelleBewirt()
Client ----> transactional proxy ----> bean.infoVNAnfrage() ----> transactional proxy ----> otherBean.erstelleBewirt()
 类似资料:
  • Spring 4.1.4 Hibernate 4.2.0 JDK 1.8 我的上下文:我有一个控制器调用-->Service-->调用Dao业务功能是删除(在1到许多DB关系中)一些子级,但不是所有子级。然后,在删除了一些子项之后,我尝试删除父项,然后我得到了java.sql.SqlIntegrityConstraintViolationException

  • 我正在与用propagation.requires_new注释的方法的奇怪行为作斗争。 以下是TransactionManager的日志:

  • 问题内容: 我在EJB3无状态会话Bean中使用CMT。另外,我还创建了自己的具有注释“ @ApplicationException(rollback = true)”的异常。 要回滚事务时是否必须使用“ context.setRollbackOnly()”? 我可以通过在bean的public方法内抛出异常来回滚事务吗? 如果是这样(对Q#2的回答是“是”),我是否必须通过在方法中声明异常来将异

  • 我有一个类,它有一个方法,该方法调用另一个服务上的另一个方法。大概是这样的: 我预计一个实体将被插入,但是如果任何嵌套事务抛出异常插入将拒绝,即使这个异常也是在处理的。 我可以用注释。但它会胜过表演。

  • 我正在使用spring-test运行JUnit测试,我的代码如下所示 我的问题是我希望我的测试不影响其他测试。所以我想为每个测试创建一些类似回滚的东西。我为此找了很多,但到目前为止一无所获。我使用Hibernate和MySql来实现这个

  • -ZJ 以下是我在Application.Properties中的数据源设置: