2

状況-ストーリータイム:
私はプログラムを「継承」しました。これは、データベースにアクセスするためのかなり単純なWebサービスです。このプログラムにはどこかに欠陥がありました。更新が許可されていないテーブルを更新しようとしました。プログラムには、データベースキュー(Oracle)を更新して、誰が何にアクセスしたかに関する情報を保存する権利しかありません。これは望ましくない動作でしたが、今では修正しました。注:これは、この質問自体には関係ありません。この質問に私を導いた原因にすぎません。

プログラムは、Spring + Hibernateを使用して、データとトランザクションを管理およびアクセスします。

プログラムの需要が高く、エラーが許容できないと見なされたため、ソフトウェアの中で実際に操作してはならないデータを操作する部分が見つかるまで、各トランザクションでロールバックを強制するだけで、修正プログラムを追加するという簡単なアイデアがありました。

ソフトウェアは2つの@Transactional注釈を使用します。1つはプロセス全体で、もう1つはログデータの書き込み部分です。この2つ目は、Requires_New伝播設定で使用されました。私が理解している限り、この2番目のトランザクションは常に新しいトランザクションを作成し、そのスパン(この場合は1つのメソッド)が終了したときにそれをフラッシュします。

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

2 に答える 2

2

わかった。問題は私が思ったものです。

SpringがBeanをトランザクション化する方法は次のとおりです。SpringBeanファクトリからトランザクションBeanを取得する場合、または依存性注入のおかげで、SpringはBeanクラスのインスタンスを提供しません。Beanクラスと同じインターフェイスを持つプロキシを提供し、メソッドを呼び出す前にトランザクションを開始し(必要な場合)、トランザクションをロールバック/コミットすることを除いて、すべてのメソッド呼び出しをBeanクラスのインスタンスに委任します(必要に応じて)メソッドが戻った後:

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

Beanクラスインスタンス(InfoVNAntwort)から、同じBeanの別のメソッドを呼び出す場合、メソッドの呼び出しはプロキシを経由しません。

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

したがって、Springにはの新しいトランザクションを開始する方法がありませんerstelleBewirt()

より簡単な方法は、erstelleBewirt()メソッドを別のトランザクションSpring Beanに配置し、この他のSpringBeanを現在のBeanに注入することです。

Client ----> transactional proxy ----> bean.infoVNAnfrage() ----> transactional proxy ----> otherBean.erstelleBewirt()
于 2012-07-06T13:57:18.413 に答える
0

@Transactionalシングルオンメソッドを使用するだけでよいと思います(クラスレベルでも使用できます)。この例のように:

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void updateFoo(Foo foo) {//transaction1
    // do something
    updateFoo1(); 
}

public void updateFoo1() {//transaction 2
    // do something
}
于 2012-07-06T10:36:23.270 に答える