@Transactional and JPA Object Parameters

When programming a multilevel application, it is best to pass only the identifiers of the objects to the methods of transactional services. But I would prefer to pass the actual JPA objects. In contrast to the question, Is a domain model object passing between invoice layers? Some colleagues fear that a) the object may belong to another / no transaction if and, therefore, cause problems when changing inside the service method and b) objects can cause problems when changing after calling such a service method from the user interface component, because the transaction has already been completed .

Expressed in code, I would like to have

@Named public class MyServiceImpl { ... @Transactional public BigDecimal calculate(ObjectOne objectOne, ObjectTwo objectTwo) { ... } } 

instead

 @Named public class MyServiceImpl { ... @Transactional public BigDecimal calculate(long objectOneId, long objectTwoId) { ObjectOne objectOne = objectOneService.find(objectOneId); ObjectTwo objectTwo = objectTwoService.find(objectTwoId); ... } } 

So, is there a way when a transaction manager (spring) takes care of objects correctly? Or do you recommend using JPA merging or something else explicitly to properly handle references to direct objects? Or do you prevent objects from being transferred instead of identifiers?

Particularly obvious explanations with links to official or well-known sources.

+5
source share
4 answers

Since the default behavior of @Transactional PROPAGATION_REQUIRED should be good to pass business objects into transactional methods. If the calling operation has already opened the transaction, a new virtual transaction is opened (for the same physical transaction) and object references remain intact. If no transaction is active, no persistent state can be corrupted.

To make sure that the object is functional, you can do

 @Transactional public BigDecimal calculate(ObjectOne objectOne, ObjectTwo objectTwo) { objectOne = objectOneService.find(objectOne.getId()); objectTwo = objectTwoService.find(objectTwo.getId()); ... } 

This is cheap because objects are fetched from the cache. But this is not required because the objects are within the same physical transaction.

If the objects really come from another transaction, you can bypass it using the code above. But your calling method will not see any changes made to the object if it does not retrieve its objects from the database again (and, depending on isolation, starts a new transaction).

+2
source

According to my experience, I prefer to transfer objects instead of ids. According to your example, this is a very simple scenario. But in real time, an object can be very complex and have many child objects. In such cases, finding objects every time will hinder performance.

It depends on the scenario . In the case of complex objects, I prefer to store objects in a session and pass them together and merge them in the entity manager.

In addition, it depends on other factors, such as the amount of data that you want to save in the session (in the case of passing a full object) or how much performance degradation you can afford when retrieving an object each time.

+1
source

Your concept of an individual object is not clear. See the link below for different states of an object in JPA / sleep mode and when they occur.

Hibernate - working with objects and link to here for an example

0
source

In my experience, the right path is in the middle: depending on your use case (for example, business logic), you will want one or the other.

An example of using identifiers: suppose a credit certificate wants to approve or reject a loan. For this, I would definitely go for ID: ApprovalService.approveCreditRequest (Long creditRequestId, ApprovalStatus approvedStatus); The problem of getting CreditRequest in the web layer seems to be a useless call for me, and suppose this is not a problem, you need to make sure that on your EJB / Service Layer the web layer will send you the correctly loaded CreditRequest , i.e. none of the web layers changed any important fields, for example creditRequest.setApprovalStatus(ApprovalStatus.DENY) or creditRequest.setRequester(anotherRequester) . And don't just think about WebLayer, as there might be Remote Ejb that invokes your level of service that you have to trust. Another use case where you definitely want to use identifiers is in the following scenario: you have a package in which you approve many (tens of thousands) of CreditRequests by calling the approveCreditRequest() method. Getting all entities from a database with all its relationships (and not just their identifiers) is probably a performance bottleneck. And passing each instance to the method can approve certain credit requirements twice (for example, between the time the instance was retrieved and approveCreditRequest() is approveCreditRequest() , it has already been approved).

The disadvantage of this method: it is not so simple UnitTest , you will need an integration test or a mocking structure (for example, mockito ).

An example of using completely objects: Suppose you need to change 90% of the fields after an HTTP request, for example, when the object is updated. The easier it is to work with all objects.

Now this has to do with the managed state of the instances: when the Entity instance leaves the service level (regardless of EJB or Spring Beans), you must be sure that the transaction is closed (Transactional is never used in WebLayer), and therefore the objects will be unmanaged automatically, so you You can easily change them in WebLayer.

0
source

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


All Articles