154

私はSpring Transactionが初めてです。なんか変だなと思ったのですが、ちゃんと理解できたのかもしれません。

メソッドレベルでトランザクションを行いたいのですが、同じクラス内に呼び出し元メソッドがあり、それが気に入らないようです。別のクラスから呼び出す必要があります。それがどのように可能かわかりません。

誰かがこの問題を解決する方法を知っているなら、私は大いに感謝します. 同じクラスを使用して、注釈付きのトランザクション メソッドを呼び出したいと考えています。

コードは次のとおりです。

public class UserService {

    @Transactional
    public boolean addUser(String userName, String password) {
        try {
            // call DAO layer and adds to database.
        } catch (Throwable e) {
            TransactionAspectSupport.currentTransactionStatus()
                    .setRollbackOnly();

        }
    }

    public boolean addUsers(List<User> users) {
        for (User user : users) {
            addUser(user.getUserName, user.getPassword);
        }
    } 
}
4

9 に答える 9

116

これは、Spring AOP (動的オブジェクトとcglib ) の制限です。

AspectJを使用してトランザクションを処理するように Spring を構成すると、コードが機能します。

シンプルでおそらく最良の代替手段は、コードをリファクタリングすることです。たとえば、ユーザーを処理する 1 つのクラスと、各ユーザーを処理する 1 つのクラスです。その後、Spring AOP でのデフォルトのトランザクション処理が機能します。


AspectJ でトランザクションを処理するための構成のヒント

Spring がトランザクションに AspectJ を使用できるようにするには、モードを AspectJ に設定する必要があります。

<tx:annotation-driven mode="aspectj"/>

Spring を 3.0 より古いバージョンで使用している場合は、これも Spring 構成に追加する必要があります。

<bean class="org.springframework.transaction.aspectj
        .AnnotationTransactionAspect" factory-method="aspectOf">
    <property name="transactionManager" ref="transactionManager" />
</bean>
于 2010-08-07T08:36:05.500 に答える
68

ここでの問題は、Spring の AOP プロキシが拡張されず、サービス インスタンスをラップして呼び出しをインターセプトすることです。これには、サービス インスタンス内からの「this」への呼び出しがそのインスタンスで直接呼び出され、ラッピング プロキシによって傍受できないという効果があります (プロキシはそのような呼び出しを認識しません)。解決策の 1 つが既に述べられています。もう 1 つの気の利いた方法は、単純に Spring でサービスのインスタンスをサービス自体に注入し、注入されたインスタンスでメソッドを呼び出すことです。これがトランザクションを処理するプロキシになります。ただし、サービス Bean がシングルトンでない場合、これには悪い副作用もあることに注意してください。

<bean id="userService" class="your.package.UserService">
  <property name="self" ref="userService" />
    ...
</bean>

public class UserService {
    private UserService self;

    public void setSelf(UserService self) {
        this.self = self;
    }

    @Transactional
    public boolean addUser(String userName, String password) {
        try {
        // call DAO layer and adds to database.
        } catch (Throwable e) {
            TransactionAspectSupport.currentTransactionStatus()
                .setRollbackOnly();

        }
    }

    public boolean addUsers(List<User> users) {
        for (User user : users) {
            self.addUser(user.getUserName, user.getPassword);
        }
    } 
}
于 2010-10-05T20:41:57.113 に答える
64

Java 8+ には別の可能性がありますが、以下の理由から私はそれを好みます。

@Service
public class UserService {

    @Autowired
    private TransactionHandler transactionHandler;

    public boolean addUsers(List<User> users) {
        for (User user : users) {
            transactionHandler.runInTransaction(() -> addUser(user.getUsername, user.getPassword));
        }
    }

    private boolean addUser(String username, String password) {
        // TODO call userRepository
    }
}

@Service
public class TransactionHandler {

    @Transactional(propagation = Propagation.REQUIRED)
    public <T> T runInTransaction(Supplier<T> supplier) {
        return supplier.get();
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public <T> T runInNewTransaction(Supplier<T> supplier) {
        return supplier.get();
    }
}

このアプローチには、次の利点があります。

  1. これは、プライベートメソッドに適用される場合があります。したがって、Spring の制限を満たすためだけにメソッドを公開してカプセル化を解除する必要はありません。

  2. 異なるトランザクション伝播内で同じメソッドが呼び出される場合があり、呼び出し元が適切なものを選択する必要があります。次の 2 行を比較します。

    transactionHandler.runInTransaction(() -> userService.addUser(user.getUserName, user.getPassword));

    transactionHandler.runInNewTransaction(() -> userService.addUser(user.getUserName, user.getPassword));

  3. 明示的であるため、より読みやすくなっています。

于 2019-05-27T13:25:18.893 に答える
7

これは自己呼び出しの私の解決策です:

public class SBMWSBL {
    private SBMWSBL self;

    @Autowired
    private ApplicationContext applicationContext;

    @PostConstruct
    public void postContruct(){
        self = applicationContext.getBean(SBMWSBL.class);
    }

    // ...
}
于 2016-12-04T03:53:42.383 に答える
1

同じクラス内で BeanFactory を自動配線して、

getBean(YourClazz.class)

クラスを自動的にプロキシし、@Transactional またはその他の aop アノテーションを考慮します。

于 2014-11-04T17:08:47.923 に答える