2017-01-26 12 views
10

Chúng tôi có một ứng dụng có một số lớp thực thể mà phải có hai bảng. Các bảng giống hệt nhau, với sự khác biệt duy nhất là tên. Các giải pháp phổ biến được cung cấp ở đây trên SO là sử dụng thừa kế (một siêu lớp được ánh xạ và một chiến lược bảng mỗi lớp) hoặc hai đơn vị bền vững với các ánh xạ khác nhau. Chúng tôi sử dụng giải pháp thứ hai và ứng dụng được xây dựng trên đầu trang của phương pháp này, vì vậy nó bây giờ được coi là một.Có thể có hai đơn vị tồn tại MSSQL trong một giao dịch không có XA không?

Có các phương pháp EJB sẽ thực hiện cập nhật trên cả hai ngữ cảnh bền vững và phải thực hiện trong một giao dịch. Cả hai bối cảnh bền vững đều có cùng nguồn dữ liệu, đó là kết nối được kích hoạt XA cho cơ sở dữ liệu Microsoft SQL Server (phiên bản 2012). Sự khác biệt duy nhất giữa các bối cảnh là có một XML ánh xạ để thay đổi tên bảng cho một số lớp thực thể và do đó hoạt động trên các bảng đó.

Một trong những khách hàng tiềm năng kiến ​​trúc muốn xem các giao dịch XA bị loại bỏ, vì chúng gây ra chi phí đáng kể trên cơ sở dữ liệu và dường như cũng ghi nhật ký và phân tích các truy vấn được thực hiện khó khăn hơn. . Tôi không biết tất cả các chi tiết, nhưng đối với rất nhiều ứng dụng chúng tôi đã quản lý để loại bỏ XA. Tuy nhiên, đối với điều này, chúng tôi hiện không thể vì hai bối cảnh bền bỉ.

Có cách nào trong tình huống này để cập nhật cả hai ngữ cảnh xảy ra theo cách giao dịch mà không có XA? Nếu vậy, làm thế nào? Nếu không, có một số thay đổi kiến ​​trúc hoặc cấu hình có thể sử dụng một ngữ cảnh bền vững mà không phải chuyển sang các lớp con cho hai bảng không?

Tôi nhận thức được những câu hỏi sau: Is it possible to use more than one persistence unit in a transaction, without it being XA?XA transaction for two phase commit

Trước khi bỏ phiếu đóng cửa này là trùng lặp, hãy lưu ý rằng các tình huống khác nhau . Chúng ta không ở trong một tình huống chỉ đọc như trong câu hỏi đầu tiên, cả hai bối cảnh đều hoạt động trên cùng một cơ sở dữ liệu, chúng ta đang sử dụng MSSQL độc quyền và chúng ta đang trên GlassFish, chứ không phải WebLogic.

+0

Nếu hai đơn vị liên tục sử dụng cùng một nguồn dữ liệu thì bạn không cần XA –

+0

@SteveC Họ sẽ nhận được kết nối của riêng họ từ một nhóm, vì vậy tôi không thấy bất kỳ cách nào để buộc họ có cùng kết nối (và do đó giao dịch) hoặc cách JPA thực sự sẽ quản lý việc này. –

+0

Tôi hy vọng rằng cả hai sẽ tham gia vào cùng một giao dịch JTA ... –

Trả lời

5

Sau một số thử nghiệm, tôi nhận thấy rằng trên thực tế, có thể có hai đơn vị liên tục sử dụng tài nguyên không phải XA trong giao dịch được quản lý bởi vùng chứa. Tuy nhiên, nó có thể phụ thuộc vào việc triển khai thực hiện. TL; DR ở phía dưới.

JTA nên yêu cầu tài nguyên XA nếu có nhiều tài nguyên tham gia vào giao dịch. Nó sử dụng X/Open XA cho mục đích cho phép các giao dịch phân tán, ví dụ như trên nhiều cơ sở dữ liệu, hoặc một cơ sở dữ liệu và hàng đợi JMS. Có vẻ như một số tối ưu hóa (nó có thể là GlassFish cụ thể, tôi không chắc chắn) cho phép người tham gia cuối cùng không phải là XA. Tuy nhiên, trong trường hợp sử dụng của tôi, cả hai đơn vị lưu giữ đều cho cùng một cơ sở dữ liệu (nhưng một bộ bảng khác nhau, với một số chồng chéo có thể) và cả hai đều không phải là XA. Điều này có nghĩa là chúng tôi mong đợi một ngoại lệ được ném khi tài nguyên thứ hai không hỗ trợ XA.

Giả sử đây là persistence.xml của chúng tôi

<?xml version="1.0" encoding="UTF-8"?> 
<persistence version="2.0" 
    xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> 
    <persistence-unit name="playground" transaction-type="JTA"> 
     <provider>org.hibernate.ejb.HibernatePersistence</provider> 
     <jta-data-source>jdbc/playground</jta-data-source> 
     <properties> 
      <property name="hibernate.dialect" value="be.dkv.hibernate.SQLServer2012Dialect" /> 
      <property name="hibernate.hbm2ddl.auto" value="update" /> 
      <property name="hibernate.show_sql" value="true" /> 
     </properties> 
    </persistence-unit> 
    <persistence-unit name="playground-copy" transaction-type="JTA"> 
     <provider>org.hibernate.ejb.HibernatePersistence</provider> 
     <jta-data-source>jdbc/playground</jta-data-source> 
     <mapping-file>META-INF/orm-playground-copy.xml</mapping-file> 
     <properties> 
      <property name="hibernate.dialect" value="be.dkv.hibernate.SQLServer2012Dialect" /> 
      <property name="hibernate.hbm2ddl.auto" value="update" /> 
      <property name="hibernate.show_sql" value="true" /> 
     </properties> 
    </persistence-unit> 
</persistence> 

Có hai đơn vị kiên trì, một với tên playground, khác với tên playground-copy. Sau này có một tập tin bản đồ ORM nhưng đó là một chút ngoài điểm ở đây. Điều quan trọng là cả hai đều có cùng một chỉ định <jta-data-source>.

Trong máy chủ ứng dụng (GlassFish trong trường hợp này), chúng tôi sẽ có một hồ bơi kết nối JDBC, với tài nguyên JDBC có tên playground sử dụng nhóm này.

connection pool and resource

Bây giờ nếu hai bối cảnh bền vững đã được tiêm vào một EJB, và một phương pháp được gọi như vậy được coi là giao dịch trong vòng một container được quản lý, bạn mong muốn điều cần trông như thế này.

persistence unit connections

Cả bối cảnh kiên trì sử dụng các nguồn dữ liệu tương tự, nhưng không phải người quản lý giao dịch cũng không lớp JPA thực sự cần quan tâm nhiều về điều đó. Sau khi tất cả, họ có thể có các nguồn dữ liệu khác nhau. Vì nguồn dữ liệu được hỗ trợ bởi một nhóm kết nối, bạn mong muốn cả hai thiết bị có được kết nối của riêng mình. XA sẽ cho phép công việc hoạt động theo cách giao dịch, bởi vì các tài nguyên được kích hoạt XA sẽ thực thi cam kết 2 pha.

Tuy nhiên, khi cố gắng ở trên với nguồn dữ liệu trỏ đến một nhóm kết nối với triển khai không thực hiện XA (và thực hiện một số công việc thực tế), không có ngoại lệ và mọi thứ hoạt động tốt! Hỗ trợ XA trong máy chủ MSSQL thậm chí bị vô hiệu hóa và cố gắng sử dụng trình điều khiển XA sẽ dẫn đến lỗi cho đến khi nó được kích hoạt, vì vậy nó không giống như tôi đã vô tình sử dụng XA mà không biết.

Đi sâu vào mã bằng trình gỡ lỗi cho thấy cả hai ngữ cảnh bền vững, là các nhà quản lý thực thể khác nhau (như chúng nên) thực tế sử dụng cùng một kết nối. Một số phân tích sâu hơn cho thấy rằng kết nối không được thiết lập như là trong một giao dịch XA, và có cùng một định danh giao dịch trên cấp JDBC. Vì vậy, tình hình trở nên này:

shared connection

Tôi chỉ có thể giả định rằng nhà cung cấp JPA có tối ưu hóa để sử dụng các kết nối tương tự nếu nhiều đơn vị được tạo ra cho cùng một giao dịch. Vậy tại sao điều này lại ổn? Ở cấp JDBC, các giao dịch được thực hiện trên một kết nối. Theo như tôi biết, đặc tả JDBC không cung cấp phương thức có nhiều giao dịch chạy trên một kết nối duy nhất. Điều đó có nghĩa rằng nếu công việc cho một bối cảnh kiên trì được cam kết, cam kết cũng sẽ xảy ra cho một bối cảnh khác.

Nhưng đó thực sự là lý do tại sao hoạt động. Điểm cam kết cho một giao dịch phân phối sẽ hoạt động như thể tất cả các phần tạo thành một tổng thể (giả sử tất cả đã bỏ phiếu "có" trong giai đoạn bỏ phiếu). Trong trường hợp này, cả hai ngữ cảnh bền vững đều hoạt động trên cùng một kết nối, vì vậy chúng hoàn toàn là một đơn vị công việc. Vì giao dịch được quản lý bởi vùng chứa, nên không thể truy cập ngay vào giao dịch đó, nghĩa là bạn không thể di chuyển để cam kết một ngữ cảnh và không phải là ngữ cảnh khác. Và chỉ với một kết nối duy nhất để thực sự đăng ký với giao dịch, nó không phải là XA, vì nó không được coi là phân phối từ quan điểm của người quản lý giao dịch.

Lưu ý rằng điều này không vi phạm địa phương của ngữ cảnh liên tục. Tìm nạp một thực thể từ cơ sở dữ liệu dẫn đến một đối tượng riêng biệt trong cả hai ngữ cảnh. Chúng vẫn có thể hoạt động độc lập với nhau, giống như chúng với các kết nối riêng biệt. Trong sơ đồ trên, các thực thể được tìm nạp cùng loại với cùng một khóa chính đại diện cho cùng một hàng cơ sở dữ liệu, nhưng là các đối tượng riêng được quản lý bởi các trình quản lý thực thể tương ứng của chúng.

Để xác minh rằng đây thực sự là một tối ưu hóa bởi nhà cung cấp JPA, tôi đã tạo một nhóm kết nối thứ hai (cùng cơ sở dữ liệu) và tài nguyên JDBC riêng biệt, đặt nó cho đơn vị kiên trì thứ hai và thử nghiệm. Điều này dẫn đến ngoại lệ dự kiến:

Caused by: java.sql.SQLException: Error in allocating a connection. 
Cause: java.lang.IllegalStateException: Local transaction already has 1 non-XA Resource: cannot add more resources. 

Nếu bạn tạo hai tài nguyên JDBC, nhưng trỏ cả hai vào cùng một hồ bơi kết nối, thì lại hoạt động bình thường.Điều này thậm chí làm việc khi sử dụng rõ ràng lớp com.microsoft.sqlserver.jdbc.SQLServerConnectionPoolDataSource, xác nhận rằng đó có thể là một tối ưu hóa ở cấp JPA thay vì vô tình nhận được cùng một kết nối hai lần cho cùng một nguồn dữ liệu (sẽ đánh bại sự kết hợp của GlassFish). Khi sử dụng một nguồn dữ liệu XA, nó thực sự sẽ là một kết nối được kích hoạt XA, nhưng nhà cung cấp JPA vẫn sẽ sử dụng cùng một nguồn cho cả hai bối cảnh bền vững. Chỉ khi sử dụng các hồ bơi riêng biệt thì thực tế nó sẽ là hai kết nối XA hoàn toàn riêng biệt và bạn sẽ không còn nhận được ngoại lệ trên nữa.

Vì vậy, bắt là gì? Trước hết, tôi đã không tìm thấy bất cứ điều gì mô tả (hoặc bắt buộc) hành vi này trong thông số kỹ thuật JPA hoặc JTA. Điều đó có nghĩa đây có lẽ là một tối ưu hóa cụ thể cho việc triển khai. Di chuyển đến một nhà cung cấp JPA khác, hoặc thậm chí là một phiên bản khác, và nó có thể không hoạt động nữa.

Thứ hai, bạn có thể gặp lỗi chết người. Nếu bạn lấy thực thể trong ví dụ trên trong cả hai ngữ cảnh, sau đó thay đổi nó trong một và tuôn ra, nó là tốt. Lấy nó trong một bối cảnh, gọi phương thức tuôn ra và sau đó tìm cách lấy nó trong cái kia, và bạn có thể có một bế tắc. Nếu bạn cho phép tách biệt giao dịch đọc không được cam kết, bạn sẽ tránh điều này nhưng những gì bạn thấy trong một ngữ cảnh sẽ phụ thuộc vào thời điểm bạn tìm nạp nó với các khía cạnh khác nhau. Vì vậy, các cuộc gọi xả thủ công có thể phức tạp.

Để tham khảo, phiên bản GlassFish được sử dụng là 3.1.2.2. Nhà cung cấp JPA là phiên bản Hibernate 3.6.4.Final.


TL; DR

, bạn có thể sử dụng hai bối cảnh kiên trì với các tài nguyên phi XA tương tự trong một JavaEE giao dịch container được quản lý, và các thuộc tính ACID được bảo toàn. Tuy nhiên, điều này là nhờ vào khả năng tối ưu hóa Hibernate khi nhiều EntityManagers được tạo cho cùng một giao dịch với cùng một nguồn dữ liệu. Vì nó dường như không được ủy quyền bởi các đặc tả JPA hoặc JTA, bạn có thể không dựa vào hành vi này trên các triển khai, các phiên bản hoặc các máy chủ ứng dụng JPA. Vì vậy, kiểm tra và không mong đợi tính di động đầy đủ.

+1

Bạn có chắc chắn rằng bối cảnh kiên trì (phiên) của cả hai nhà quản lý thực thể được đóng lại đúng cách không? Nếu không, bạn có thể kết thúc mà không có bộ nhớ sau một thời gian hoặc làm việc với dữ liệu cũ nếu một cá thể ngữ cảnh kiên trì được tái sử dụng trong các yêu cầu tiếp theo. –

+0

@DraganBozanovic Điểm tốt. Tôi giả sử (có thể sai) rằng với nỗ lực đó, họ phải đưa các nhà phát triển Hibernate để phát hiện rõ ràng hai ngữ cảnh có trong cùng một giao dịch vùng chứa và có chia sẻ kết nối hay không, điều này đã được tính đến. Sẽ có một số cách tốt để thử nghiệm điều này? Sau khi rời khỏi cuộc gọi, tôi ở bên ngoài ngữ cảnh giao dịch, vì vậy tôi không thực sự có thứ gì đó để gỡ lỗi. Tôi đoán tôi có thể lấy nguồn Hibernate –

+0

Để bắt đầu, bạn chỉ có thể bắn rất nhiều yêu cầu liên quan đến cả quản lý thực thể (thông qua jmeter hoặc tương tự) và kiểm tra thông qua một profiler cho dù tiêu thụ bộ nhớ đang phát triển. –

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