Chú thích @Transactional thuộc về đâu?


528

Bạn nên đặt @Transactionaltrong DAOcác lớp học và / hoặc các phương pháp của họ hoặc là nó tốt hơn để chú thích các lớp dịch vụ được gọi sử dụng các đối tượng DAO? Hay nó có ý nghĩa để chú thích cả hai "lớp"?

Câu trả lời:


537

Tôi nghĩ các giao dịch thuộc về lớp Dịch vụ. Đó là một trong những người biết về các đơn vị công việc và các trường hợp sử dụng. Đó là câu trả lời đúng nếu bạn có một số DAO được đưa vào một Dịch vụ cần phối hợp với nhau trong một giao dịch.


Tôi đồng ý với điều đó. Đôi khi điều đó không quan trọng, nhưng đôi khi bạn có thể hưởng lợi từ đó, ví dụ như phiên Hibernate được kéo dài cho giao dịch trong khi đó, vì vậy tất cả các đối tượng được tải đều nằm trong bộ đệm cấp 1 và bạn không cần phải gắn lại các đối tượng để phiên lại, cộng với tải một cách lười biếng chức năng không có fuzz.
dma_k

5
Một giao dịch toàn cầu có thể bao gồm nhiều hơn một trong số @Transactional (tuyên truyền = Tuyên truyền.REQUIRED) không? Hoặc @Transactional luôn là ranh giới cho giao dịch? Tôi không chắc chắn tôi đã nhận được nó từ tài liệu, nhưng có vẻ như bạn có thể tạo các giao dịch bao gồm các phương thức @Transactional và mọi thứ chạy bên trong
lisak

1
Có, tôi nghĩ rằng @Transactional ngoài cùng là thứ trở thành ranh giới cho giao dịch nếu Tuyên truyền.REQUIRED được bật.
duffymo


309

Nói chung, tôi đồng ý với những người khác nói rằng các giao dịch thường được bắt đầu ở cấp độ dịch vụ (tùy thuộc vào mức độ chi tiết mà bạn yêu cầu tất nhiên).

Tuy nhiên, trong thời gian đó tôi cũng bắt đầu thêm @Transactional(propagation = Propagation.MANDATORY)vào lớp DAO của mình (và các lớp khác không được phép bắt đầu giao dịch nhưng yêu cầu các giao dịch hiện tại) vì việc phát hiện lỗi trong khi bạn quên bắt đầu giao dịch trong trình gọi (dễ dàng hơn nhiều) ví dụ dịch vụ). Nếu DAO của bạn được chú thích với sự lan truyền bắt buộc, bạn sẽ nhận được một ngoại lệ cho biết rằng không có giao dịch hoạt động khi phương thức được gọi.

Tôi cũng có một bài kiểm tra tích hợp trong đó tôi kiểm tra tất cả các bean (bộ xử lý bài bean) cho chú thích này và thất bại nếu có một @Transactionalchú thích với sự lan truyền khác với Bắt buộc trong một bean không thuộc lớp dịch vụ. Bằng cách này, tôi đảm bảo rằng chúng tôi không bắt đầu giao dịch trên lớp sai.


3
Điều này nên được thực hiện trong lớp dao (giao diện), trong dao impl. lớp hay cả hai?
Johan

3
Tôi không biết liệu nó có phù hợp với cuộc thảo luận này không, nhưng một mẹo khác có thể là thêm @Transactional (readOnly = true) trong dao ẩn trong các hoạt động không viết.
maxivis

10
@Johan Spring khuyên nên đặt các chú thích Giao dịch trên các lớp triển khai thay vì các giao diện.
Mathias G.

Tôi thực sự thích ý tưởng này, cũng đã thử nó cho các dự án của mình
Thomas Einwaller

1
Hãy xem tôi có hiểu không ... bạn có nói rằng tôi nên đưa @Transactionalvào lớp triển khai Dịch vụ không và tôi có nên đưa @Transactional(propagation = MANDATORY)vào triển khai lớp DAO (kho lưu trữ) không?
John Henckel

93

Chú thích giao dịch nên được đặt xung quanh tất cả các hoạt động không thể tách rời.

Ví dụ: cuộc gọi của bạn là "thay đổi mật khẩu". Điều đó bao gồm hai hoạt động

  1. Thay đổi mật khẩu.
  2. Kiểm toán sự thay đổi.
  3. Gửi email cho khách hàng rằng mật khẩu đã thay đổi.

Vậy ở phần trên, nếu kiểm toán thất bại thì thay đổi mật khẩu cũng thất bại? Nếu vậy, thì giao dịch nên ở khoảng 1 và 2 (vì vậy ở lớp dịch vụ). Nếu email thất bại (có lẽ nên có một số loại không an toàn về vấn đề này để nó không bị lỗi) thì nó có nên quay lại mật khẩu thay đổi và kiểm toán không?

Đây là những loại câu hỏi bạn cần phải hỏi khi quyết định đặt @Transactional.


41

Câu trả lời đúng cho các kiến ​​trúc Spring truyền thống là đặt ngữ nghĩa giao dịch trên các lớp dịch vụ, vì những lý do mà những người khác đã mô tả.

Một xu hướng mới nổi trong Spring là hướng tới thiết kế hướng tên miền (DDD). Spring Roo minh họa cho xu hướng độc đáo. Ý tưởng là làm cho các đối tượng miền POJO phong phú hơn rất nhiều so với các kiến ​​trúc Spring điển hình (thường là thiếu máu ), và đặc biệt là đặt ngữ nghĩa giao dịch và tồn tại trên chính các đối tượng miền. Trong trường hợp tất cả những gì cần thiết là các thao tác CRUD đơn giản, bộ điều khiển web hoạt động trực tiếp trên POJO của đối tượng miền (chúng hoạt động như các thực thể trong ngữ cảnh này) và không có tầng dịch vụ. Trong trường hợp có một số loại phối hợp cần thiết giữa các đối tượng miền, bạn có thể có một bean dịch vụ xử lý, với@Transactiontheo truyền thống. Bạn có thể đặt lan truyền giao dịch trên các đối tượng miền thành một cái gì đó giống như REQUIREDvậy để các đối tượng miền sử dụng bất kỳ giao dịch hiện có nào, chẳng hạn như các giao dịch được bắt đầu tại bean dịch vụ.

Về mặt kỹ thuật, kỹ thuật này sử dụng AspectJ và <context:spring-configured />. Roo sử dụng các định nghĩa liên loại AspectJ để phân tách ngữ nghĩa thực thể (giao dịch và tính bền vững) khỏi nội dung đối tượng miền (về cơ bản là các trường và phương thức kinh doanh).


38

Trường hợp bình thường sẽ là chú thích ở cấp độ dịch vụ, nhưng điều này thực sự phụ thuộc vào yêu cầu của bạn.

Việc chú thích trên một lớp dịch vụ sẽ dẫn đến các giao dịch dài hơn so với chú thích ở cấp độ DAO. Tùy thuộc vào mức độ cô lập giao dịch mà bạn có thể gặp sự cố, vì các giao dịch đồng thời sẽ không thấy các thay đổi của nhau trong ví dụ. ĐỌC LẠI ĐỌC.

Việc chú thích trên các DAO sẽ giữ cho các giao dịch càng ngắn càng tốt, với nhược điểm là chức năng mà lớp dịch vụ của bạn đang phơi bày sẽ không được thực hiện trong một giao dịch (có thể phục hồi).

Sẽ không có ý nghĩa khi chú thích cả hai lớp nếu chế độ lan truyền được đặt thành mặc định.


31

Tôi đặt @Transactionaltrên @Servicelớp và thiết lập rollbackForbất kỳ ngoại lệ và readOnlyđể tối ưu hóa các giao dịch tiếp theo.

Theo mặc định @Transactionalsẽ chỉ tìm kiếm RuntimeException(Ngoại lệ không được kiểm tra), bằng cách đặt rollback thành Exception.class(Ngoại lệ được kiểm tra), nó sẽ quay trở lại cho bất kỳ ngoại lệ nào.

@Transactional(readOnly = false, rollbackFor = Exception.class)

Xem Ngoại lệ được kiểm tra so với không được kiểm tra .


17

Hay nó có ý nghĩa để chú thích cả hai "lớp"? - không có ý nghĩa gì khi chú thích cả lớp dịch vụ và lớp dao - nếu người ta muốn đảm bảo rằng phương thức DAO luôn được gọi (được truyền) từ một lớp dịch vụ với sự lan truyền "bắt buộc" trong DAO. Điều này sẽ cung cấp một số hạn chế cho các phương thức DAO được gọi từ lớp UI (hoặc bộ điều khiển). Ngoài ra - khi đặc biệt kiểm tra lớp DAO - có chú thích DAO cũng sẽ đảm bảo nó được kiểm tra chức năng giao dịch.


Sẽ không làm điều này dẫn đến một giao dịch lồng nhau? Và tất cả các vấn đề tinh tế đi cùng với nó?
HDave

11
Không, không có giao dịch lồng nhau trong JPA. Đặt chúng vào cả hai sẽ hoàn toàn ổn - nếu bạn đã tham gia giao dịch khi bạn đạt DAO, giao dịch đó sẽ được tiếp tục.
lừa4jesus

4
Giao dịch lồng nhau có thể xảy ra nếu bạn sử dụng propagation=Propagation.REQUIRES_NEW. Mặt khác, đối với hầu hết các trường hợp, bao gồm propogation = bắt buộc, DAO sẽ chỉ tham gia vào giao dịch hiện có được bắt đầu bởi lớp dịch vụ.
dan carter


15

Đối với giao dịch ở cấp cơ sở dữ liệu

chủ yếu tôi đã sử dụng @Transactionaltrong DAO chỉ ở mức phương thức, do đó cấu hình có thể được dành riêng cho một phương thức / sử dụng mặc định (bắt buộc)

  1. Phương pháp của DAO có thể tìm nạp dữ liệu (chọn ..) - không cần @Transactional điều này có thể dẫn đến một số chi phí vì bị chặn giao dịch / và proxy AOP cũng cần được thực thi.

  2. Các phương thức của DAO thực hiện thao tác chèn / cập nhật sẽ nhận được @Transactional

blog rất tốt trên transctional

Đối với cấp độ ứng dụng -
Tôi đang sử dụng giao dịch cho logic nghiệp vụ, tôi muốn có thể quay lại trong trường hợp có lỗi không mong muốn

@Transactional(rollbackFor={MyApplicationException.class})
public void myMethod(){

    try {    
        //service logic here     
    } catch(Throwable e) {

        log.error(e)
        throw new MyApplicationException(..);
    }
}

6
+1 bài viết rất hay về TransactionalinJava
sồi

11

Thông thường, người ta nên đặt một giao dịch ở lớp dịch vụ.

Nhưng như đã nêu trước đây, tính nguyên tử của một hoạt động là những gì cho chúng ta biết nơi cần chú thích. Do đó, nếu bạn sử dụng các khung như Hibernate, trong đó một thao tác "lưu / cập nhật / xóa / ... sửa đổi" trên một đối tượng có khả năng sửa đổi một số hàng trong một số bảng (do xếp tầng qua biểu đồ đối tượng), của Tất nhiên cũng cần có quản lý giao dịch về phương pháp DAO cụ thể này.


10

@TransactionalCác chú thích nên được đặt xung quanh tất cả các hoạt động không thể tách rời. Sử dụng @Transactionaltuyên truyền giao dịch được xử lý tự động. Trong trường hợp này nếu phương thức khác được gọi bằng phương thức hiện tại, thì phương thức đó sẽ có tùy chọn tham gia giao dịch đang diễn ra.

Vì vậy, hãy lấy ví dụ:

Chúng tôi có 2 mô hình tức là CountryCity. Lập bản đồ CountryCitymô hình quan hệ giống như một người Countrycó thể có nhiều Thành phố nên ánh xạ giống như,

@OneToMany(fetch = FetchType.LAZY, mappedBy="country")
private Set<City> cities;

Ở đây Quốc gia được ánh xạ tới nhiều thành phố với việc tìm nạp chúng Lazily. Vì vậy, ở đây có vai trò @Transactinalkhi chúng ta truy xuất đối tượng Quốc gia từ cơ sở dữ liệu, sau đó chúng ta sẽ nhận được tất cả dữ liệu của đối tượng Quốc gia nhưng sẽ không nhận được Tập hợp các thành phố vì chúng ta đang tìm nạp các thành phố LAZILY.

//Without @Transactional
public Country getCountry(){
   Country country = countryRepository.getCountry();
   //After getting Country Object connection between countryRepository and database is Closed 
}

Khi chúng tôi muốn truy cập Tập hợp các thành phố từ đối tượng quốc gia thì chúng tôi sẽ nhận được các giá trị null trong Tập hợp đó vì đối tượng của Tập chỉ được tạo Tập hợp này không được khởi tạo cùng với dữ liệu để lấy các giá trị của Tập mà chúng tôi sử dụng @Transactional,

//with @Transactional
@Transactional
public Country getCountry(){
   Country country = countryRepository.getCountry();
   //below when we initialize cities using object country so that directly communicate with database and retrieve all cities from database this happens just because of @Transactinal
   Object object = country.getCities().size();   
}

Vì vậy, về cơ bản @Transactionallà Dịch vụ có thể thực hiện nhiều cuộc gọi trong một giao dịch mà không cần kết nối với điểm cuối.


2
rất nhiều thông tin, cảm ơn bạn! Chỉ là những gì tôi đang tìm kiếm, một lời giải thích về những gì @Transactionalthực sự là
CybeX

6

Nó là tốt hơn để có nó trong lớp dịch vụ! Điều này được giải thích rõ ràng trên một trong những bài báo mà tôi đã gặp ngày hôm qua! Đây là liên kết mà bạn có thể kiểm tra!


5

nhập mô tả hình ảnh ở đây

Các @Transactionalnên được sử dụng trên lớp dịch vụ vì nó có chứa các logic kinh doanh. Lớp DAO thường chỉ có các hoạt động CRUD cơ sở dữ liệu.

// the service class that we want to make transactional
@Transactional
public class DefaultFooService implements FooService {

    Foo getFoo(String fooName);

    Foo getFoo(String fooName, String barName);

    void insertFoo(Foo foo);

    void updateFoo(Foo foo);
}

Tài liệu mùa xuân: https://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/transaction.html


5

Lớp dịch vụ là nơi tốt nhất để thêm @Transactionalchú thích vì hầu hết logic nghiệp vụ có ở đây, nó chứa hành vi trường hợp sử dụng mức độ chi tiết.

Giả sử chúng ta thêm nó vào DAO và từ dịch vụ, chúng ta đang gọi 2 lớp DAO, một thất bại và thành công khác, trong trường hợp này nếu @Transactionalkhông có dịch vụ, một DB sẽ cam kết và lớp khác sẽ quay lại.

Do đó, khuyến nghị của tôi là sử dụng chú thích này một cách khôn ngoan và chỉ sử dụng ở lớp Dịch vụ.

Dự án Github- java-algos


Các ngoại lệ như ObjectOptimisticLockingFailureException chỉ xảy ra sau khi giao dịch hoàn tất. Nếu bạn có các luồng riêng biệt cho hoạt động khác như dịch vụ thư, thiết kế này hoàn toàn thất bại. Chúng tôi đang đau khổ ngay bây giờ. Chỉ còn lại giải pháp là AOP.
Nabin Kumar Khatiwada

4

Trước hết hãy xác định nơi chúng ta phải sử dụng giao dịch ?

Tôi nghĩ rằng câu trả lời đúng là - khi chúng ta cần đảm bảo rằng chuỗi hành động sẽ được kết thúc cùng nhau như một hoạt động nguyên tử hoặc không có thay đổi nào được thực hiện ngay cả khi một trong những hành động thất bại.

Đó là thực tế nổi tiếng để đưa logic kinh doanh vào các dịch vụ. Vì vậy, các phương thức dịch vụ có thể chứa các hành động khác nhau phải được thực hiện dưới dạng một đơn vị công việc logic duy nhất. Nếu vậy - thì phương pháp đó phải được đánh dấu là Giao dịch . Tất nhiên, không phải phương thức nào cũng yêu cầu giới hạn như vậy, vì vậy bạn không cần phải đánh dấu toàn bộ dịch vụ là giao dịch .

Và hơn thế nữa - đừng quên tính đến việc @Transactional rõ ràng, có thể làm giảm hiệu suất phương pháp. Để xem toàn bộ hình ảnh, bạn phải biết mức độ cô lập giao dịch. Biết rằng điều đó có thể giúp bạn tránh sử dụng @Transactional khi không cần thiết.


3

Tốt hơn là giữ @Transactional ở một lớp giữa riêng biệt giữa DAO và Lớp dịch vụ. Vì, rollback rất quan trọng, bạn có thể đặt tất cả thao tác DB của mình vào lớp giữa và viết logic nghiệp vụ trong Lớp dịch vụ. Lớp giữa sẽ tương tác với các lớp DAO của bạn.

Điều này sẽ giúp bạn trong nhiều tình huống như ObjectOptimisticLockingFailureException - Ngoại lệ này chỉ xảy ra sau khi Giao dịch của bạn kết thúc. Vì vậy, bạn không thể bắt nó ở lớp giữa nhưng bạn có thể bắt trong lớp dịch vụ của mình ngay bây giờ. Điều này sẽ không thể thực hiện được nếu bạn có @Transactional trong lớp Dịch vụ. Mặc dù bạn có thể bắt trong Bộ điều khiển nhưng Bộ điều khiển nên càng sạch càng tốt.

Nếu bạn đang gửi thư hoặc tin nhắn trong luồng riêng biệt sau khi hoàn thành tất cả các tùy chọn lưu, xóa và cập nhật, bạn có thể thực hiện việc này trong dịch vụ sau khi Giao dịch được hoàn thành trong lớp giữa của bạn. Một lần nữa, nếu bạn đề cập đến @Transactional trong lớp dịch vụ, thư của bạn sẽ hoạt động ngay cả khi giao dịch của bạn không thành công.

Vì vậy, có một lớp @Transaction ở giữa sẽ giúp làm cho mã của bạn tốt hơn và dễ xử lý. Mặt khác, nếu bạn sử dụng trong lớp DAO, bạn có thể không thể Phục hồi tất cả các hoạt động. Nếu bạn sử dụng trong lớp Dịch vụ, bạn có thể phải sử dụng AOP (Lập trình hướng theo khía cạnh) trong một số trường hợp nhất định.


2

Lý tưởng nhất là lớp Dịch vụ (Trình quản lý) đại diện cho logic nghiệp vụ của bạn và do đó, nó nên được chú thích với @Transactionallớp .Service có thể gọi các DAO khác nhau để thực hiện các hoạt động DB. Giả sử một tình huống trong đó bạn có N số hoạt động DAO trong một phương thức dịch vụ. Nếu hoạt động DAO đầu tiên của bạn không thành công, các hoạt động khác có thể vẫn được thông qua và bạn sẽ kết thúc trạng thái DB không nhất quán. Lớp dịch vụ chú thích có thể cứu bạn khỏi những tình huống như vậy.


1

Tôi thích sử dụng @Transactionaltrên lớp dịch vụ ở mức phương thức.


1

@Transactionalsử dụng trong lớp dịch vụ được gọi bằng cách sử dụng lớp điều khiển ( @Controller) và gọi lớp dịch vụ đến lớp DAO ( @Repository) tức là hoạt động liên quan đến cơ sở dữ liệu.

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.