@Transactional
Springのアノテーションは、オブジェクトをプロキシでラップすることで機能します。プロキシは、@Transactional
でアノテーションが付けられたメソッドをラップします。 トランザクションで。そのため、プライベートメソッドは継承できないため、(例のように)プライベートメソッドではアノテーションは機能しません。 =>ラップすることはできません(これは、aspectjで宣言型トランザクションを使用する場合は当てはまりません。その場合、以下のプロキシ関連の警告は適用されません)。
@Transactional
の基本的な説明は次のとおりです。 春の魔法が効く。
あなたが書いた:
class A {
@Transactional
public void method() {
}
}
しかし、これはBeanを注入したときに実際に得られるものです:
class ProxiedA extends A {
private final A a;
public ProxiedA(A a) {
this.a = a;
}
@Override
public void method() {
try {
// open transaction ...
a.method();
// commit transaction
} catch (RuntimeException e) {
// rollback transaction
} catch (Exception e) {
// commit transaction
}
}
}
これには制限があります。 @PostConstruct
では機能しません オブジェクトがプロキシされる前に呼び出されるため、メソッド。また、すべてを正しく構成した場合でも、トランザクションはチェックされていないでのみロールバックされます。 デフォルトでは例外。 @Transactional(rollbackFor={CustomCheckedException.class})
を使用します チェックされた例外でロールバックが必要な場合。
私が知っているもう1つの頻繁に遭遇する警告:
@Transactional
次の例では、b()
のように、メソッドを「外部から」呼び出した場合にのみ機能します。 トランザクションにラップされません:
class X {
public void a() {
b();
}
@Transactional
public void b() {
}
}
@Transactional
も理由です オブジェクトをプロキシすることで機能します。上記の例では、a()
X.b()
を呼び出します 強化された「スプリングプロキシ」メソッドではありませんb()
したがって、トランザクションはありません。回避策として、b()
を呼び出す必要があります 別の豆から。
これらの警告のいずれかに遭遇し、推奨される回避策を使用できない場合(メソッドを非プライベートにするか、b()
を呼び出します 別のBeanから)TransactionTemplate
を使用できます 宣言型トランザクションの代わりに:
public class A {
@Autowired
TransactionTemplate transactionTemplate;
public void method() {
transactionTemplate.execute(status -> {
A();
B();
return null;
});
}
...
}
更新
上記の情報を使用して、OPの更新された質問に回答します。
@Transactional:changes()で注釈を付ける必要があるメソッドはどれですか? databaseChanges()?
@Transactional(rollbackFor={Exception.class})
public void changes() throws Exception {
someLogicBefore();
databaseChanges();
someLogicAfter();
}
changes()
を確認してください クラス自体からではなく、コンテキストがインスタンス化された後、Beanの「外部から」と呼ばれます(たとえば、これはafterPropertiesSet()
ではありません または@PostConstruct
注釈付きメソッド)。スプリングは、デフォルトでチェックされていない例外に対してのみトランザクションをロールバックすることを理解します(チェックされた例外のrollbackForリストでより具体的にするようにしてください)。