@Transactional
chú thích trong mùa xuân hoạt động bằng cách bao bọc đối tượng của bạn trong một proxy, đến lượt nó sẽ bao bọc các phương thức được chú thích bằng @Transactional
trong một giao dịch. Do đó, chú thích sẽ không hoạt động trên các phương thức riêng tư (như trong ví dụ của bạn) vì phương thức riêng tư không thể được kế thừa => chúng không thể được bao bọc (điều này không đúng nếu bạn sử dụng các giao dịch khai báo với khía cạnhj, thì các cảnh báo liên quan đến proxy bên dưới không áp dụng).
Đây là giải thích cơ bản về cách @Transactional
tác phẩm phép thuật mùa xuân.
Bạn đã viết:
class A {
@Transactional
public void method() {
}
}
Nhưng đây là những gì bạn thực sự nhận được khi tiêm một hạt đậu:
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
}
}
}
Điều này có những hạn chế. Chúng không hoạt động với @PostConstruct
bởi vì chúng được gọi trước khi đối tượng được proxied. Và ngay cả khi bạn đã định cấu hình tất cả một cách chính xác, các giao dịch chỉ được khôi phục khi bỏ chọn ngoại lệ theo mặc định. Sử dụng @Transactional(rollbackFor={CustomCheckedException.class})
nếu bạn cần khôi phục trên một số ngoại lệ đã kiểm tra.
Một cảnh báo thường gặp khác mà tôi biết:
@Transactional
phương thức sẽ chỉ hoạt động nếu bạn gọi nó là "từ bên ngoài", trong ví dụ sau b()
sẽ không được bao bọc trong giao dịch:
class X {
public void a() {
b();
}
@Transactional
public void b() {
}
}
Đó cũng là vì @Transactional
hoạt động bằng cách ủy quyền đối tượng của bạn. Trong ví dụ trên a()
sẽ gọi X.b()
không phải là phương pháp "spring proxy" nâng cao b()
vì vậy sẽ không có giao dịch. Để giải quyết vấn đề, bạn phải gọi b()
từ một hạt đậu khác.
Khi bạn gặp phải bất kỳ cảnh báo nào trong số này và không thể sử dụng cách giải quyết được đề xuất (đặt phương thức là không riêng tư hoặc gọi b()
từ một bean khác) bạn có thể sử dụng TransactionTemplate
thay vì các giao dịch khai báo:
public class A {
@Autowired
TransactionTemplate transactionTemplate;
public void method() {
transactionTemplate.execute(status -> {
A();
B();
return null;
});
}
...
}
Cập nhật
Trả lời câu hỏi được cập nhật OP bằng cách sử dụng thông tin ở trên.
Phương thức nào nên được chú thích với @Transactional:changes ()? databaseChanges ()?
@Transactional(rollbackFor={Exception.class})
public void changes() throws Exception {
someLogicBefore();
databaseChanges();
someLogicAfter();
}
Đảm bảo rằng changes()
được gọi là "từ bên ngoài" của bean, không phải từ chính lớp và sau khi ngữ cảnh được khởi tạo (ví dụ:đây không phải là afterPropertiesSet()
hoặc @PostConstruct
phương pháp chú thích). Hiểu rằng giao dịch Spring rollbacks chỉ dành cho các trường hợp ngoại lệ chưa được kiểm tra theo mặc định (hãy cố gắng cụ thể hơn trong danh sách rollbackF đối với các trường hợp ngoại lệ đã kiểm tra).