How to manage transactions using JAX-RS, Spring and JPA

I am using JAX-RS to provide an HTTP interface for managing a data model. The data model is stored in a database and interacts via JPA.

This allows me to modify the interface to the data model in accordance with the requirements of REST clients and, apparently, works very well. However, I’m not sure how to handle the scenario in which the method provided by the JAX-RS resource requires a transaction that affects the JPA template get, update, commit-on-tx-end, since there is only transactional packaging the get operation, so the update never not executed. I see the same problem if a single REST operation requires multiple JPA operations.

Since I am using Spring transaction support, it is obvious that you need to apply @Transactional to these methods in JAX-RS resources. However, for this to work, Spring needs to manage the JAX-RS resource lifecycle, as well as the use cases that I know that resources are created using β€œnew” when necessary, which makes me a little nervous anyway .

I can think of the following solutions:

  • upgrade my JPA methods to provide a transaction-driven version, all I want to do is using my REST interface. It should work, holds transactions from the JAX-RS level, but prevents the creation of the get, update, commit-on-tx-end template and means that I need to create a very granular JPA interface.
  • Inject Resource Objects; but they usually have a state with at least the identifier of the object interacting with
  • Abandon the hierarchy of resources and add large, stateless super resources at the root that control the entire hierarchy from that root; not cohesive, great services
  • Have a hierarchy of indexed, apartheid, transaction-supporting auxiliary objects that are β€œshadow” actual resources; resources are created and stored, but delegate method calls to auxiliary objects.

Anyone have any suggestions? Maybe I missed some key point somewhere.


Update - to get around the lack of a transaction around the get, update, commit-on-tx-close thread, I can expose the EntityManager merge method (object) and call it manually. Not neat and does not solve a larger problem.


Update 2 @skaffman Code Example: In the JPA service layer, injected annotations work

 public class MyEntityJPAService { ... @Transactional(readOnly=true) // do in transaction public MyEntity getMyEntity(final String id) { return em.find(MyEntity.class, id); } 

No new transactions in JAX-RS resource created by new

 public class MyEntityResource { ... private MyEntityJPAService jpa; ... @Transactional // not injected so not effective public void updateMyEntity(final String id, final MyEntityRepresentation rep) { MyEntity entity = jpa.getMyEntity(id); MyEntity.setSomeField(rep.getSomeField()); // no transaction commit, change not saved... } 
+6
source share
1 answer

I have some suggestions

  • Insert a layer between the JPA and JAX-RS layers. This layer will consist of Spring -managed @Transactional beans and will make up various business layer operations from their JPA component calls. This is somewhat similar to your (1), but supports a simple JPA level.

  • Replace JAX-RS Spring-MVC, which provides the same (or similar) functions, including @PathVariable , @ResponseBody , etc.

  • Programmatically wrap JAX-RS objects in transactional proxies using TransactionProxyFactorybean . This can separate your @Transactional annotations and create a proxy server that will distinguish them.

  • Use @Configurable and AspectJ LTW so that Spring @Transactional even if you create an object using `new. See 8.8.1 Using AspectJ for Dependencies Embeds Domain Objects with Spring

+4
source

Source: https://habr.com/ru/post/910169/


All Articles