Trước hết, vì Spring không tự tồn tại, nên nó không thể xác định chính xác ý nghĩa của readOnly. Thuộc tính này chỉ là một gợi ý cho nhà cung cấp, hành vi phụ thuộc vào, trong trường hợp này, Hibernate.
Nếu bạn chỉ định readOnly là true, chế độ xả sẽ được đặt là FlushMode.NEVER trong Phiên Hibernate hiện tại ngăn phiên thực hiện giao dịch.
Hơn nữa, setReadOnly (true) sẽ được gọi trên Kết nối JDBC, đây cũng là một gợi ý cho cơ sở dữ liệu cơ bản. Nếu cơ sở dữ liệu của bạn hỗ trợ nó (rất có thể là như vậy), về cơ bản nó có tác dụng tương tự như FlushMode.NEVER, nhưng nó mạnh hơn vì bạn thậm chí không thể xả bằng tay.
Bây giờ hãy xem cách truyền bá giao dịch hoạt động.
Nếu bạn không đặt rõ ràng readOnly thành true, bạn sẽ có các giao dịch đọc/ghi. Tùy thuộc vào các thuộc tính giao dịch (như REQUIRES_NEW), đôi khi giao dịch của bạn bị đình chỉ tại một số điểm, một giao dịch mới được bắt đầu và cuối cùng được cam kết và sau đó giao dịch đầu tiên được nối lại.
OK, chúng tôi gần như ở đó. Hãy xem điều gì mang readOnly vào kịch bản này.
Nếu một phương thức trong đọc/ghi giao dịch gọi một phương thức yêu cầu giao dịch readOnly, thì phương thức đầu tiên sẽ bị đình chỉ, vì nếu không, một lần xả/cam kết sẽ xảy ra ở cuối của phương pháp thứ hai.
Ngược lại, nếu bạn gọi một phương thức từ trong một readOnly giao dịch yêu cầu đọc/ghi, một lần nữa, phương thức đầu tiên sẽ bị đình chỉ, vì nó không thể bị xóa/cam kết và phương pháp thứ hai cần điều đó.
Trong readOnly-to-readOnly và read/write-to-read/write trường hợp giao dịch bên ngoài không cần phải tạm dừng (trừ khi bạn chỉ định cách truyền khác , hiển nhiên, rõ ràng).
Gọi readOnly = false từ readOnly = true không hoạt động kể từ khi giao dịch trước đó tiếp tục.
Trong ví dụ của bạn, phương thức xử lý () trên lớp dịch vụ của bạn đang bắt đầu một giao dịch đọc-ghi mới. Nếu phương thức xử lý lần lượt gọi các phương thức dịch vụ có chú thích chỉ đọc, thì chỉ đọc sẽ không có hiệu lực vì chúng sẽ tham gia vào giao dịch đọc-ghi hiện có.
Nếu điều đó là cần thiết cho các phương thức đó là chỉ đọc, thì bạn có thể chú thích chúng với Tuyên truyền.REQUIRES_NEW và sau đó chúng sẽ bắt đầu một giao dịch chỉ đọc mới thay vì tham gia vào giao dịch đọc ghi hiện có.
Dưới đây là một ví dụ hoạt động, CircuitStateRep repository là kho lưu trữ dữ liệu JPA mùa xuân.
BeanS gọi một giao dịch = Bean1 chỉ đọc, thực hiện tra cứu và gọi giao dịch = đọc-ghi Bean2 để lưu một đối tượng mới.
- Bean1 bắt đầu một tx chỉ đọc.
31 09: 39: 44.199 [pool-1-thread-1] DEBUG o.s.orm.jpa.JpaTransactionManager - Tạo giao dịch mới với tên [nz.co.vodafone.wcim.business.Bean1.startS Something]: PROPAGATION_REQUIRED, ISOL ''
Bean 2 tham dự trong đó.
31 09: 39: 44.230 [pool-1-thread-1] DEBUG o.s.orm.jpa.JpaTransactionManager - Tham gia vào giao dịch hiện tại
Không có gì được cam kết với cơ sở dữ liệu.
Bây giờ thay đổi Bean2 @Transactional chú thích để thêm propagation=Propagation.REQUIRES_NEW
Bean1 bắt đầu một tx chỉ đọc.
31 09: 31: 36.418 [pool-1-thread-1] DEBUG o.s.orm.jpa.JpaTransactionManager - Tạo giao dịch mới với tên [nz.co.vodafone.wcim.business.Bean1.startS Something]: PROPAGATION_REQUIRED, ISOL ''
Bean2 bắt đầu một tx đọc-ghi mới
31 09: 31: 36.449 [pool-1-thread-1] DEBUG o.s.orm.jpa.JpaTransactionManager - Tạm dừng giao dịch hiện tại, tạo giao dịch mới với tên [nz.co.vodafone.wcim.business.Bean2.createS Something]
Và những thay đổi được thực hiện bởi Bean2 hiện được cam kết với cơ sở dữ liệu.
Đây là ví dụ, được thử nghiệm với dữ liệu mùa xuân, ngủ đông và Oracle.
@Named public class BeanS { @Inject Bean1 bean1; @Scheduled(fixedRate = 20000) public void runSomething() { bean1.startSomething(); } } @Named @Transactional(readOnly = true) public class Bean1 { Logger log = LoggerFactory.getLogger(Bean1.class); @Inject private CircuitStateRepository csr; @Inject private Bean2 bean2; public void startSomething() { Iterable<CircuitState> s = csr.findAll(); CircuitState c = s.iterator().next(); log.info("GOT CIRCUIT {}", c.getCircuitId()); bean2.createSomething(c.getCircuitId()); } } @Named @Transactional(readOnly = false) public class Bean2 { @Inject CircuitStateRepository csr; public void createSomething(String circuitId) { CircuitState c = new CircuitState(circuitId + "-New-" + new DateTime().toString("hhmmss"), new DateTime()); csr.save(c); } }Theo mặc định, việc truyền bá giao dịch là BẮT BUỘC, có nghĩa là cùng một giao dịch sẽ truyền từ một người gọi giao dịch sang giao dịch. Trong trường hợp này, trạng thái chỉ đọc sẽ lan truyền. Ví dụ. nếu một giao dịch chỉ đọc sẽ gọi một giao dịch đọc, toàn bộ giao dịch sẽ ở chế độ chỉ đọc.
Bạn có thể sử dụng mẫu Phiên mở trong Chế độ xem để cho phép tải nhanh không? Bằng cách đó, phương thức xử lý của bạn hoàn toàn không cần phải giao dịch.
Dường như bỏ qua các cài đặt cho giao dịch đang hoạt động hiện tại, nó chỉ áp dụng các cài đặt cho một giao dịch mới:
[.___.] hành vi lan truyền. [.___.] Lưu ý rằng các tham số như mức cô lập hoặc thời gian chờ sẽ chỉ được áp dụng cho các giao dịch mới và do đó bị bỏ qua khi tham gia vào các hoạt động. [.__.] Ngoài ra, không phải tất cả các cài đặt định nghĩa giao dịch sẽ được hỗ trợ bởi mỗi trình quản lý giao dịch: Việc triển khai trình quản lý giao dịch phù hợp sẽ đưa ra một ngoại lệ khi gặp phải các cài đặt không được hỗ trợ. [.__.] Một ngoại lệ cho quy tắc trên là cờ chỉ đọc, nên bỏ qua nếu không hỗ trợ chế độ chỉ đọc rõ ràng . Về cơ bản, cờ chỉ đọc chỉ là một gợi ý để tối ưu hóa tiềm năng.