情况 - 故事时间:
我“继承”了一个程序,一个用于访问数据库的相当简单的 Web 服务。这个程序在某个地方有一个缺陷:它试图更新一个没有更新授权的表。该程序仅有权更新数据库队列 (Oracle),以保存谁访问了什么信息。这是不受欢迎的行为,现在我修复了它。注意:这与这个问题本身无关,它只是导致我提出这个问题的原因。
该程序使用 Spring + Hibernate 来管理和访问数据和事务。
因为该程序的需求量很大,并且错误被认为是无法容忍的,所以我有一个快速的想法来添加一个修补程序,只是强制回滚每个事务,直到我找到实际操作它不应该操作的数据的软件部分。
该软件使用 2 个@Transactional
注释。一个在整个过程中,另一个在写入日志数据的部分。第二个与Requires_New
传播设置一起使用。据我了解,第二个事务总是会创建一个新事务并在其跨度(在本例中:一种方法)结束时刷新它。
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
然后,在嵌套事务结束后,我添加了一个直接的回滚语句,以回滚外部事务(从而撤消操作)。但它没有那样工作。两个事务都回滚了。谁能给我任何指示为什么会发生这种情况?它有点矛盾,我认为我知道系统是如何工作的。是否currentTransactionStatus()
包括与当前会话相关的所有事务?
相关代码片段(为清晰起见缩短)
@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 />