2008-09-19 29 views
26

Tôi có một cái gì đó không quốc tịch đậu như:EJB3 giao dịch Tuyên truyền

@Stateless 
public class MyStatelessBean implements MyStatelessLocal, MyStatelessRemote { 
    @PersistenceContext(unitName="myPC") 
    private EntityManager mgr; 

    @TransationAttribute(TransactionAttributeType.SUPPORTED) 
    public void processObjects(List<Object> objs) { 
     // this method just processes the data; no need for a transaction 
     for(Object obj : objs) { 
      this.process(obj); 
     } 
    } 

    @TransationAttribute(TransactionAttributeType.REQUIRES_NEW) 
    public void process(Object obj) { 
     // do some work with obj that must be in the scope of a transaction 

     this.mgr.merge(obj); 
     // ... 
     this.mgr.merge(obj); 
     // ... 
     this.mgr.flush(); 
    } 
} 

này thường sử dụng sau đó là khách hàng sẽ gọi processObjects (...), mà không thực sự tương tác với người quản lý thực thể. Nó làm những gì nó cần phải làm và gọi quá trình (...) riêng cho từng đối tượng để xử lý. Thời gian của quá trình (...) là tương đối ngắn, nhưng processObjects (...) có thể mất một thời gian rất dài để chạy qua tất cả mọi thứ. Do đó tôi không muốn nó duy trì một giao dịch mở. Tôi do cần các quy trình riêng lẻ (...) hoạt động để hoạt động trong giao dịch của riêng họ. Đây sẽ là giao dịch mới cho mọi cuộc gọi. Cuối cùng tôi muốn giữ cho các tùy chọn mở cho khách hàng để gọi quá trình (...) trực tiếp.

Tôi đã thử một số loại giao dịch khác nhau: không bao giờ, không được hỗ trợ, được hỗ trợ (trên processObjects) và bắt buộc, yêu cầu mới (trên quy trình) nhưng tôi nhận được TransactionRequiredException mỗi khi hợp nhất() được gọi.

tôi đã có thể để làm cho nó hoạt động bằng cách chia lên các phương pháp thành hai hạt cà phê khác nhau:

@Stateless 
@TransationAttribute(TransactionAttributeType.NOT_SUPPORTED) 
public class MyStatelessBean1 implements MyStatelessLocal1, MyStatelessRemote1 { 
    @EJB 
    private MyStatelessBean2 myBean2; 

    public void processObjects(List<Object> objs) { 
     // this method just processes the data; no need for a transaction 
     for(Object obj : objs) { 
      this.myBean2.process(obj); 
     } 
    } 
} 

@Stateless 
public class MyStatelessBean2 implements MyStatelessLocal2, MyStatelessRemote2 { 
    @PersistenceContext(unitName="myPC") 
    private EntityManager mgr; 

    @TransationAttribute(TransactionAttributeType.REQUIRES_NEW) 
    public void process(Object obj) { 
     // do some work with obj that must be in the scope of a transaction 

     this.mgr.merge(obj); 
     // ... 
     this.mgr.merge(obj); 
     // ... 
     this.mgr.flush(); 
    } 
} 

nhưng tôi vẫn tò mò nếu nó có thể thực hiện điều này trong một lớp. Dường như với tôi như người quản lý giao dịch chỉ hoạt động ở cấp độ bean, ngay cả khi các phương thức riêng lẻ được đưa ra các chú thích cụ thể hơn. Vì vậy, nếu tôi đánh dấu một phương pháp theo cách ngăn chặn giao dịch bắt đầu gọi các phương thức khác trong cùng một trường hợp đó cũng sẽ không tạo giao dịch, bất kể chúng được đánh dấu như thế nào?

Tôi đang sử dụng Máy chủ ứng dụng JBoss 4.2.1.GA, nhưng các câu trả lời không cụ thể được chào đón/ưa thích.

Trả lời

24

Một cách khác để thực hiện nó thực sự là có cả hai phương pháp trên cùng một bean - và có một tham chiếu @EJB cho chính nó! Một cái gì đó như thế:

// supposing processObjects defined on MyStatelessRemote1 and process defined on MyStatelessLocal1 
@Stateless 
@TransationAttribute(TransactionAttributeType.NOT_SUPPORTED) 
public class MyStatelessBean1 implements MyStatelessLocal1, MyStatelessRemote1 { 
    @EJB 
    private MyStatelessLocal1 myBean2; 

    public void processObjects(List<Object> objs) { 
     // this method just processes the data; no need for a transaction 
     for(Object obj : objs) { 
      this.myBean2.process(obj); 
     } 
    } 


    @TransationAttribute(TransactionAttributeType.REQUIRES_NEW) 
    public void process(Object obj) { 
     // do some work with obj that must be in the scope of a transaction 

     this.mgr.merge(obj); 
     // ... 
     this.mgr.merge(obj); 
     // ... 
     this.mgr.flush(); 
    } 
} 

này cách bạn thực sự 'lực' các process() phương pháp để được truy cập thông qua ejb chồng proxy, do đó lấy @TransactionAttribute có hiệu lực - và vẫn chỉ giữ lại một lớp. Phew!

+0

Chỉ cần nghĩ rằng tôi sẽ ghé qua và chia sẻ điều đó vì hỏi câu hỏi này vấn đề này đã xuất hiện nhiều lần (tình cờ, sếp của tôi nói rằng anh ta đã tìm ra câu hỏi này qua Google một lần trong khi cố gắng giải quyết nó.) Chúng tôi đã sử dụng giải pháp này một số lần, vì vậy cảm ơn một lần nữa. –

+0

Tôi nghĩ đây là một câu hỏi hay để biết. Bạn mong đợi những ngữ nghĩa này nếu bạn phải làm bất cứ điều gì từ thế giới EJB2, nhưng chúng có vẻ hơi lạ nếu bạn chỉ làm việc trong EJB3. Một cách khác để xem xét mọi thứ là bạn cần đảm bảo phương thức mà bạn đang gọi được gọi là * thông qua giao diện EJB *. – cwash

0

Tôi nghĩ rằng phải làm với @TransationAttribute (TransactionAttributeType.Never) trên phương thức processObjects.

TransactionAttributeType.Never

http://docs.sun.com/app/docs/doc/819-3669/6n5sg7cm3?a=view

Nếu khách hàng đang chạy trong một giao dịch và gọi phương pháp này doanh nghiệp bean, container ném một RemoteException . Nếu khách hàng không phải là được kết hợp với giao dịch, vùng chứa không bắt đầu giao dịch mới trước khi chạy phương thức.

Tôi giả định rằng bạn là khách hàng theo phương thức processObjects từ mã máy khách. Bởi vì có lẽ khách hàng của bạn không được liên kết với giao dịch, cuộc gọi phương thức với TransactionAttributeType.Never là điều hạnh phúc ngay từ đầu. Sau đó, bạn gọi phương thức quy trình từ quy trìnhObjects có độ dài là chú thích TransactionAttributeType.Required không phải là cuộc gọi phương thức đậu và chính sách giao dịch không được thực thi. Khi bạn gọi hợp nhất bạn sẽ nhận được ngoại lệ vì bạn vẫn không được liên kết với giao dịch.

Hãy thử sử dụng TransactionAttributeType.Required cho cả hai phương thức đậu để xem nó có thực hiện thủ thuật hay không.

+0

Như tôi đã hiểu, nó sẽ làm bạn thất vọng. Matt không muốn processObjects chạy trong một transation (mà sẽ là trường hợp nếu anh ta đặt TransactionAttributeType.Required trên processObjects), anh ta muốn nhiều giao dịch riêng lẻ cho phương thức quy trình. –

1

Matt, vì những gì đáng giá, tôi đã đi đến cùng một kết luận giống như bạn.

TransactionAttributeTypes chỉ được xem xét khi vượt qua ranh giới Bean. Khi gọi các phương thức trong cùng một giao thức BeanAttributeTypes không có hiệu lực, không có vấn đề gì loại được đưa vào phương pháp.

Theo như tôi có thể thấy không có gì trong EJB Persistence Spec xác định những hành vi nên được trong những trường hợp này.

Tôi cũng đã trải nghiệm điều này trong Jboss. Tôi cũng sẽ thử nó trong Glassfish và cho bạn biết kết quả.

2

Tôi nghĩ rằng mỗi thứ được gói trong một proxy điều khiển hành vi giao dịch. Khi bạn gọi từ bean này sang bean khác, bạn sẽ đi qua proxy của bean đó và hành vi giao dịch có thể được thay đổi bởi proxy.

Nhưng khi một bean gọi một phương thức trên chính nó với một thuộc tính giao dịch khác, cuộc gọi không đi qua proxy, do đó hành vi không thay đổi.

+0

Bằng cách đó, đậu có thể tự gọi mình để có được hành vi đó, như thể nó là từ đậu khác. – Loki

4

Matt, câu hỏi bạn đặt ra là một câu hỏi khá cổ điển, tôi nghĩ rằng giải pháp tự tham khảo của Herval/Pascal là gọn gàng. Có một giải pháp tổng quát hơn không được đề cập ở đây.

Đây là trường hợp đối với giao dịch "người dùng" EJB. Vì bạn đang ở trong bean phiên, bạn có thể nhận được giao dịch người dùng từ ngữ cảnh phiên. Sau đây là cách mã của bạn sẽ xem xét với các giao dịch sử dụng:

// supposing processObjects defined on MyStatelessRemote1 and process defined on MyStatelessLocal1 
@Stateless 
@TransationAttribute(TransactionAttributeType.NOT_SUPPORTED) 
public class MyStatelessBean1 implements MyStatelessLocal1, MyStatelessRemote1 { 

    @Resource 
    private SessionContext ctx; 

    @EJB 
    private MyStatelessLocal1 myBean2; 

    public void processObjects(List<Object> objs) { 
     // this method just processes the data; no need for a transaction 
     for(Object obj : objs) { 
      this.myBean2.process(obj); 
     } 
    } 


    public void process(Object obj) { 

     UserTransaction tx = ctx.getUserTransaction(); 

     tx.begin(); 

     // do some work with obj that must be in the scope of a transaction 

     this.mgr.merge(obj); 
     // ... 
     this.mgr.merge(obj); 
     // ... 
     this.mgr.flush(); 

     tx.commit(); 
    } 
} 
+0

Tôi không thích giải pháp này. Nó đánh bại một trong những lợi thế của các container TX được quản lý bởi EJB, cụ thể là giảm việc quản lý TX thủ công và mã boilerplate liên quan. Không cần thiết cho điều này trong trường hợp này. Chỉ cần làm theo câu trả lời được chấp nhận, OR, bạn có thể sử dụng ngữ cảnh phiên như sau: ctx.getBusinessObject (MyStatelessLocal1.class) .process (obj); và bạn sẽ có tác dụng tương tự như tiêm chính mình. – NBW

1

Trong trường hợp ai đó đi qua một ngày này:

để tránh phụ thuộc vòng tròn (cho phép tự tham khảo ví dụ) trong JBoss sử dụng chú thích 'IgnoreDependency' cho ví dụ:

@IgnoreDependency @EJB Bản thân mìnhRef;

+0

Đây là giải pháp thay thế cho JBoss. Nó biến mất như AS6. – NBW

0

Tôi có các vấn đề phụ thuộc vòng tròn mà Kevin đã đề cập. Tuy nhiên, chú thích @IgnoreDependency được đề xuất là một chú giải đặc trưng của jboss và không có đối tác nào trong ví dụ: Glassfish.

Vì nó không hoạt động với tham chiếu EJB mặc định, tôi cảm thấy hơi khó chịu với giải pháp này.

Vì vậy, tôi đã cung cấp giải pháp của bluecarbon một cơ hội, do đó bắt đầu giao dịch nội bộ "bằng tay". Bên cạnh đó, tôi thấy không có giải pháp nhưng để thực hiện quy trình bên trong() trong một bean khác cũng xấu xí bởi vì chúng ta đơn giản chỉ muốn làm rối loạn mô hình lớp của chúng ta cho các chi tiết kỹ thuật như vậy.

+0

@IgnoreDependency không còn áp dụng cho JBoss, nó là loại giải pháp sửa lỗi và không còn cần thiết như AS6 nữa. Glassfish không bao giờ có vấn đề này và không bao giờ cần một workaround như vậy. – NBW

1

Tôi chưa thử (tôi sắp sửa), nhưng cách thay thế để tiêm tự tham chiếu qua chú thích @EJB là phương thức SessionContext.getBusinessObject(). Đây sẽ là một cách khác để tránh khả năng một tham chiếu vòng tròn thổi mọi thứ lên trên bạn - mặc dù ít nhất là cho việc tiêm đậu không trạng thái dường như hoạt động.

Tôi đang làm việc trên một hệ thống lớn, trong đó cả hai kỹ thuật được sử dụng (có lẽ bởi các nhà phát triển khác nhau), nhưng tôi không chắc chắn đó là cách "đúng" để làm điều đó.

+0

Dù bằng cách nào cũng tốt. – NBW

Các vấn đề liên quan