Tôi nên đặt chú thích @Transactional: tại định nghĩa giao diện hoặc tại lớp triển khai ở đâu?


91

Câu hỏi từ tiêu đề trong mã:

@Transactional (readonly = true)
public interface FooService {
   void doSmth ();
}


public class FooServiceImpl implements FooService {
   ...
}

vs

public interface FooService {
   void doSmth ();
}

@Transactional (readonly = true)
public class FooServiceImpl implements FooService {
   ...
}

Câu trả lời:


121

Từ http://static.springsource.org/spring/docs/2.0.x/reference/transaction.html

Khuyến nghị của nhóm Spring là bạn chỉ chú thích các lớp cụ thể bằng @Transactionalchú thích, trái ngược với chú thích giao diện. Bạn chắc chắn có thể đặt @Transactionalchú thích trên một giao diện (hoặc một phương thức giao diện), nhưng điều này sẽ chỉ hoạt động như bạn mong đợi nếu bạn đang sử dụng proxy dựa trên giao diện. Thực tế là các chú thích không được kế thừa có nghĩa là nếu bạn đang sử dụng proxy dựa trên lớp thì cài đặt giao dịch sẽ không được cơ sở hạ tầng ủy quyền dựa trên lớp nhận dạng và đối tượng sẽ không được bao bọc trong proxy giao dịch (điều này hoàn toàn là xấu ) . Vì vậy, vui lòng thực hiện lời khuyên của nhóm Spring và chỉ chú thích các lớp cụ thể (và các phương thức của các lớp cụ thể) với @Transactionalchú thích.

Lưu ý: Vì cơ chế này dựa trên proxy nên chỉ các lệnh gọi phương thức 'bên ngoài' đến thông qua proxy mới bị chặn. Điều này có nghĩa là 'tự gọi', tức là một phương thức bên trong đối tượng đích gọi một số phương thức khác của đối tượng đích, sẽ không dẫn đến một giao dịch thực sự trong thời gian chạy ngay cả khi phương thức được gọi được đánh dấu bằng @Transactional!

(Phần nhấn mạnh được thêm vào câu đầu tiên, phần nhấn mạnh khác từ câu gốc.)


Big +1 BTW, tôi đã tự do đặt câu trích dẫn thành một câu trích dẫn, vì vậy mọi người không bị nhầm lẫn và thêm lại các chữ in nghiêng và những thứ tương tự từ bản gốc.
TJ Crowder

Tôi đã đọc tuyên bố này trước đây trong tài liệu Spring, nhưng tôi vẫn không thể hiểu tại sao cài đặt giao dịch sẽ không được cơ sở hạ tầng ủy quyền dựa trên lớp nhận dạng ? Cá nhân tôi nghĩ, đây chỉ là giới hạn triển khai Spring, không phải vấn đề của cơ sở hạ tầng proxy cơ bản.
dma_k

Nhìn vào các nguồn Spring, người ta có thể tìm ra điều đó JdkDynamicAopProxyđi qua tất cả các cố vấn bean trên mỗi lệnh gọi phương thức (xem thêm DefaultAdvisorChainFactory#getInterceptorsAndDynamicInterceptionAdvice()), bao gồm trong trường hợp thiết lập giao dịch khai báo BeanFactoryTransactionAttributeSourceAdvisor. Đến lượt nó TransactionAttributeSourcePointcut#matches()được gọi ra, sẽ thu thập thông tin giao dịch tương đối. Nó được chuyển qua lớp đích và nó luôn có thể đi qua tất cả các giao diện mà lớp này triển khai. Bây giờ: tại sao điều này không thể hoạt động đáng tin cậy?
dma_k

2
@dma_k - Thời gian chạy Java chỉ cấp quyền truy cập trực tiếp vào các chú thích lớp được kế thừa từ các lớp cha hoặc các chú thích phương thức không có sự kế thừa nào cả. Vì vậy, Pointcut.matches () sẽ phải thực sự duyệt đệ quy tất cả các giao diện, tìm phương thức ghi đè "thực" với phản xạ, xử lý các phương thức cầu nối, nạp chồng, varargs, generics, proxy và tất nhiên là làm điều đó nhanh chóng . Sau đó, bạn phải giải quyết vấn đề thừa kế kim cương - mà nhiều @Transactionalchú thích được thừa kế sẽ áp dụng? Vì vậy, dù tất cả đều có thể xảy ra nhưng tôi không trách Xuân. jira.springsource.org/browse/SPR-975
Peter Davis

9

Bạn có thể đặt chúng trên giao diện nhưng hãy lưu ý rằng các giao dịch có thể không xảy ra trong một số trường hợp. Xem mẹo thứ hai trong Phần 10.5.6 của tài liệu Spring:

Spring khuyên bạn chỉ nên chú thích các lớp cụ thể (và các phương thức của các lớp cụ thể) với chú thích @Transactional, trái ngược với chú thích các giao diện. Bạn chắc chắn có thể đặt chú thích @Transactional trên một giao diện (hoặc một phương thức giao diện), nhưng điều này chỉ hoạt động như bạn mong đợi nếu bạn đang sử dụng proxy dựa trên giao diện. Thực tế là các chú thích Java không được kế thừa từ các giao diện có nghĩa là nếu bạn đang sử dụng proxy dựa trên lớp (proxy-target-class = "true") hoặc khía cạnh dựa trên dệt (mode = "khía cạnhj"), thì cài đặt giao dịch sẽ không được công nhận bởi cơ sở hạ tầng ủy quyền và dệt, và đối tượng sẽ không được bao bọc trong một ủy quyền giao dịch, điều này sẽ rất tệ.

Tôi khuyên bạn nên đưa chúng vào triển khai vì lý do này.

Ngoài ra, đối với tôi, các giao dịch dường như là một chi tiết triển khai vì vậy chúng phải nằm trong lớp thực thi. Hãy tưởng tượng có các triển khai trình bao bọc để ghi nhật ký hoặc triển khai thử nghiệm (mocks) mà không cần phải giao dịch.


9

Khuyến nghị của Spring là bạn nên chú thích các triển khai cụ thể thay vì một giao diện. Việc sử dụng chú thích trên một giao diện không sai, chỉ là bạn có thể sử dụng sai tính năng đó và vô tình bỏ qua khai báo @Transaction của bạn.

Nếu bạn đã đánh dấu một thứ gì đó giao dịch trong một giao diện và sau đó tham chiếu đến một trong các lớp triển khai của nó ở nơi khác vào mùa xuân, thì không hẳn là đối tượng mà mùa xuân tạo ra sẽ không tôn trọng chú thích @Transactional.

Trong thực tế, nó trông giống như sau:

public class MyClass implements MyInterface { 

    private int x;

    public void doSomethingNonTx() {}

    @Transactional
    public void toSomethingTx() {}

}

1

Hỗ trợ @Transactional trên các lớp cụ thể:

Tôi thích kiến ​​trúc một giải pháp theo 3 phần nói chung: API, Triển khai và Web (nếu cần). Tôi cố gắng hết sức để giữ API nhẹ / đơn giản / POJO nhất có thể bằng cách giảm thiểu sự phụ thuộc. Điều đặc biệt quan trọng nếu bạn chơi nó trong môi trường phân tán / tích hợp, nơi bạn phải chia sẻ nhiều API.

Đặt @Transactional yêu cầu thư viện Spring trong phần API, IMHO không hiệu quả. Vì vậy, tôi muốn thêm nó trong Triển khai nơi giao dịch đang chạy.


0

Đặt nó trên giao diện là tốt miễn là tất cả những người triển khai có thể thấy trước của IFC của bạn quan tâm đến dữ liệu TX (giao dịch không phải là vấn đề mà chỉ cơ sở dữ liệu giải quyết). Nếu phương thức không quan tâm đến TX (nhưng bạn cần đặt nó ở đó cho Hibernate hoặc bất cứ điều gì), hãy đặt nó vào cấy ghép.

Ngoài ra, có thể tốt hơn một chút nếu đặt @Transactionalcác phương thức trong giao diện:

public interface FooService {
    @Transactional(readOnly = true)
    void doSmth();
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.