2012-05-24 36 views
16

Xem xét kịch bản hai phương pháp tồn tại trong đậu quốc tịch khác nhauGiao dịch JPA Nested Và Khóa

public class Bean_A { 
    Bean_B beanB; // Injected or whatever 
    public void methodA() { 
    Entity e1 = // get from db 
    e1.setName("Blah"); 
    entityManager.persist(e1); 
    int age = beanB.methodB(); 

    } 
} 
public class Bean_B { 
    //Note transaction 
    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) 
    public void methodB() { 

    // complex calc to calculate age 
    } 

} 

Giao dịch bắt đầu bởi BeanA.methodA sẽ bị đình chỉ và giao dịch mới sẽ được bắt đầu vào năm BeanB.methodB. Điều gì xảy ra nếu methodB cần truy cập cùng một thực thể đã được sửa đổi bởi methodA. Điều này sẽ dẫn đến deadlock.Is nó có thể ngăn chặn nó mà không dựa vào mức cô lập?

+0

Bạn nhận được bế tắc ở đâu và ở đâu? Từ bộ nhớ cache phiên hoặc từ các hàng bị khóa cơ sở dữ liệu? –

Trả lời

20

Hm, hãy liệt kê tất cả các trường hợp.

REQUIRES_NEW không thực sự tổ chức các giao dịch, nhưng như bạn đã đề cập tạm dừng giao dịch hiện tại. Sau đó, chỉ có hai giao dịch truy cập cùng một thông tin. (Điều này tương tự như hai giao dịch đồng thời thông thường, ngoại trừ việc chúng không đồng thời nhưng trong cùng một luồng thực thi).

T1 T2   T1 T2 
―    ― 
|    | 
       | 
    ―   | ― 
    |   | | 
    |  =  | | 
    ―   | ― 
       | 
|    | 
―    ― 

Sau đó chúng ta cần phải xem xét lạc vs bi quan khóa.

Ngoài ra, chúng tôi cần xem xét tuôn ra do ORM vận hành. Với ORM, chúng tôi không có quyền kiểm soát rõ ràng khi ghi xảy ra, vì flush được kiểm soát bởi khung. Thông thường, một lệnh tuôn ra ngầm xảy ra trước cam kết, nhưng nếu nhiều mục được sửa đổi, khung làm việc cũng có thể làm các phép bung trung gian.

1) Hãy xem xét khóa lạc quan, nơi đọc không có khóa, nhưng viết có được khóa độc quyền.

Đọc bởi T1 không có khóa.

1a) Nếu T1 đã tuôn ra các thay đổi sớm, nó sẽ có khóa độc quyền. Khi T2 cam kết, nó cố gắng để có được khóa nhưng không thể. Hệ thống bị chặn. Điều này có thể là một loại bế tắc cụ thể. Hoàn thành phụ thuộc vào cách giao dịch hoặc khóa thời gian.

1b) Nếu T1 không tuôn ra các thay đổi sớm, không có khóa đã được mua. Khi T2 cam kết, nó mua lại và phát hành nó và thành công. Khi T1 cố gắng cam kết, nó sẽ thông báo xung đột và thất bại.

2) Hãy xem xét khóa bi quan, nơi đọc có được khóa chia sẻ và viết khóa độc quyền.

Đọc bởi T1 có khóa chia sẻ.

2a) Nếu T1 bị tuôn ra sớm, nó sẽ khóa khóa inta một khóa độc quyền. Tình hình là tương tự như 1a)

2b) Nếu T1 không tuôn ra sớm, T1 giữ khóa chia sẻ. Khi T2 cam kết, nó cố gắng để có được một khóa và khối độc quyền. Hệ thống bị chặn lại.

Kết luận: tốt với khóa lạc quan nếu không có sự tuôn ra sớm xảy ra, bạn không thể kiểm soát chặt chẽ.

+0

@ewernil Tôi có một nghi ngờ ở đây, bây giờ chúng tôi có hai giao dịch, giao dịch đầu tiên chưa hoàn thành, vậy làm thế nào để giao dịch thứ hai (require_new) xem kết quả chưa được cam kết đầu tiên? Bạn vui lòng ném một số ánh sáng trên cùng không? –

+0

@SAM khi bạn sửa đổi một hàng trong một giao dịch bạn có được một khóa. Các giao dịch khác có thể đọc hàng cũ nhưng không thể sửa đổi hàng cho đến khi khóa đầu tiên được phát hành. – ewernli

0

bằng cách lập trình cam kết giao dịch sau entityManager.persist(e1); và trước int age = beanB.methodB();?

public class Bean_A { 
    Bean_B beanB; // Injected or whatever 
    public void methodA() { 
    EntityManager em = createEntityManager(); 
    Entity e1 = // get from db 
    e1.setName("Blah"); 
    entityManager.persist(e1); 
    em.getTransaction().commit(); 
    int age = beanB.methodB(); 

    } 
} 
public class Bean_B { 
    //Note transaction 
    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) 
    public void methodB() { 

    // complex calc to calculate age 
    } 

} 

EDIT: CMT

Nếu bạn có CMT, bạn vẫn có thể cam kết programatically, bạn chỉ nhận được các giao dịch từ EJBContext. ví dụ: http://geertschuring.wordpress.com/2008/10/07/how-to-use-bean-managed-transactions-with-ejb3-jpa-and-jta/

hoặc bạn có thể thêm @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) public void methodC() sẽ thực hiện e1.setName("Blah"); entityManager.persist(e1);, tức là nó sẽ tiếp tục e1 trong giao dịch. sau đó methodA() bạn sẽ gọi

methodC(); 
beanB.methodB(); 
+0

Và nếu điều đó là không thể? Ví dụ trong trường hợp CMT – anergy

+0

lạ khuyên giao dịch trong CMT nhưng vẫn có thể có trường hợp khác, điều này không khả thi ở giữa vì bạn đang gọi một số phương thức đậu khác – anergy

+1

Đó không phải là mục tiêu của EJB theo cách thủ công quản lý giao dịch ... Thế còn nếu ngoại lệ xảy ra sau methodB? Không có rollback có thể ... –

1

Đây là một recent article về việc sử dụng REQUIRES_NEW ranh giới giao dịch.

Từ kinh nghiệm của tôi, không nên có khóa chết với mã tiêu chuẩn: các truy vấn có mệnh đề hạn chế là where và vài lần chèn. Trong một số trường hợp cụ thể, một số công cụ cơ sở dữ liệu có thể thực hiện khóa leo thang nếu có nhiều hàng được đọc hoặc chèn vào một bảng duy nhất trong giao dịch ... và trong trường hợp đó, có một khóa chết có thể xảy ra.

Nhưng trong trường hợp đó, sự cố không đến từ REQUIRES_NEW nhưng từ thiết kế SQL. Nếu thiết kế đó không thể được cải thiện, thì bạn không có lựa chọn nào khác để thay đổi mức cách ly thành mức độ lỏng lẻo hơn.

3

Vượt qua thực thể và hợp nhất ...

Bạn có thể vượt qua thực thể mới của bạn để methodB(), và hợp nhất nó vào mới EntityManager. Khi trở về phương pháp làm mới tổ chức của bạn để xem các thay đổi:

public class Bean_A { 
    Bean_B beanB; // Injected or whatever 
    public void methodA() { 
    Entity e1 = // get from db 
    e1.setName("Blah"); 
    entityManager.persist(e1); 
    int age = beanB.methodB(e1); 
    entityManager.refresh(e1); 
    } 
} 

public class Bean_B { 
    //Note transaction 
    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) 
    public void methodB(Entity e1) { 
    e1 = entityManager.merge(e1); 
    // complex calc to calculate age 
    } 

} 

Lưu ý rằng điều này sẽ cam kết thực thể của bạn khi giao dịch mới đóng cửa sau khi methodB.

... hoặc lưu nó trước khi gọi methodB

Nếu bạn sử dụng các phương pháp trên các thực thể được lưu riêng rẽ với giao dịch chính của bạn, vì vậy bạn làm bất cứ điều gì không bị lỏng nếu bạn lưu nó từ Bean_A trước khi gọi methodB():

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