5

When programming multi-tier application it seems to be best practice to pass only object ids to transactional service methods. But I would rather like to pass actual JPA objects. Unlike in the question Is domain model object passing between layers overhead? some colleagues fear that a) object could belong to another/no transaction if and thus cause problems when modified inside the service method and b) objects could cause problems when modified after invoking such a service method from the UI component because the transaction is already committed.

Expressed in code I would rather like to have

@Named public class MyServiceImpl
{
   ...

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

instead of

@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 technique where the transaction manager (spring) cares for the objects properly? Or do you recommend to use JPA merge or anything else explicitly to handle direct object references properly? Or do you discourage passing objects instead of IDs as well?

Especially explanations with links to official or well-known sources would be helpful, obviously.

Community
  • 1
  • 1
Christian
  • 13,285
  • 2
  • 32
  • 49

4 Answers4

2

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

To make sure object are sane you can do

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

This is cheap, as the objects are fetched from the cache. But it is not required as the objects are within the same physical transaction.

If the objects really are from a different transaction you can work around it with the code above. But your calling method will not see any changes made to the object unless it fetches its objects from database again (and depending on isolation starts a new transaction).

isi
  • 136
  • 3
1

As per my experience, I will favor passing objects instead of ids. As per your example, it is very simple scenario. But in real time, object may be very complex and have many child objects. In such cases, retrieving objects every time will hamper the performance.

It varies according to the scenario. In case of complex objects, I will prefer to store the objects in the session and pass it along and merge it in the entity manager.

Also, it depends on other factors like how much data you want to keep in session (In case of passing full object) or how much performance degradation you can afford in retrieving object every time.

javafan
  • 1,525
  • 3
  • 21
  • 40
0

Your concept of detached object is not clear. Refer to below link for different object states in JPA/hibernate and when they occur.

Hibernate - Working with objects and refer here for example

Pavan
  • 121
  • 4
  • Yes, that's exactly the concept of detached object I was referring to. What do you think is not clear about it? – Christian Nov 14 '14 at 08:20
  • As per your statement - a) object could become detached inside the service method and b) objects could be detached/broken after invoking such a service method from the UI component. It making think that its not clear. Object will get detached once your session closed, let it be in service or any other layer. Now, if you want to pass that detached object into UI then that be discourage, you should create new client model and copy dao model into client model when passing it to UI. From UI, you will get client model copy that to dao model and then merger it. – Pavan Nov 14 '14 at 08:34
  • Ah. I tried to improve the concerns in my question. But the actual question is, if passing objects is considered a bad or good practice. Or in which situations it can/cannot be used. – Christian Nov 14 '14 at 08:43
  • You can pass objects, And best practice for it is to create another model class and copy DAO/DTO object state to that model class instance to pass it to UI or any other layer. Don't pass the DAO/DTO object to UI. To be more specific, DAO/DTO object should remain in DAO and business layer only, they should not be exposed to any other layer. To transfer to any other layer you create model classes for that layer and copy them it from business layer. – Pavan Nov 14 '14 at 09:00
  • That's interesting. Do you mean business objects in general should not be shared across layers? If yes, how are references passed between layers? Because ids cannot be used as in the UI layer no database is accessible. Or do you mean business objects are used in all layers but they are not persistent, but DTOs are? – Christian Nov 14 '14 at 09:48
  • Yes, business objects are used in all layers but they are not persistent, but DTOs are used in persistent only. – Pavan Nov 14 '14 at 10:32
0

As of my experience, the correct way is in the middle: depending on your use case (i.e bussiness logic) you will want one or the other.

Example for using IDs: suppose an Credit Approver will want to approve or deny a credit. For that I would definitely go for the ID: ApprovalService.approveCreditRequest(Long creditRequestId, ApprovalStatus approvalStatus); The problem of fetching the CreditRequest in the web layer seems an useless call to me, and suppose that is not a problem, you will have to make sure in your EJB/Service Layer that the web layer send you the correctly loaded CreditRequest instance, i.e that no one in the Web Layer changed any important fields, like creditRequest.setApprovalStatus(ApprovalStatus.DENY) or creditRequest.setRequester(anotherRequester). And also do not think only about the WebLayer, as there could be a Remote Ejb that calls your service layer, that you will have to trust. Another use case where you definitely want to use IDs is in the following scenario: you have a batch in which you approve many (tens of thousands) CreditRequests, by calling the method approveCreditRequest(). Fetching all entities from DB with all its relationships (instead of their IDs only) is for sure a performance bottleneck AND passing every instance to the method could approve certain CreditRequests twice (e.g in the meantime between instance was fetched and the approveCreditRequest() is called, it was already approved).

The disadvantage of this method is: it is not as easy to UnitTest, you will need an integration test OR a mocking framework (e.g mockito).

Example of using fully objects: Suppose you need to change 90% of the fields after an HTTP Request, for example when an Entity is updated. Than it is definitely easier to work with entire objects.

Now related to the managed state of the instances: when the Entity instance leaves the Service Layer (no matter EJB or Spring Beans), you must be sure the transaction is closed (in the WebLayer never use Transactional), and so the entities will be unmanaged automatically, so that you can change them without problems in the WebLayer.

V G
  • 18,822
  • 6
  • 51
  • 89
  • In the first example the object is not available in the UI, in the first place, right? So you won't fetch it from the DB. That's perfectly sane. But in case of existing objects you would pass them. Is that your answer? If yes, do you know about any sources to proof that this is a best practice? – Christian Nov 14 '14 at 09:51
  • Yes, in the first example the object is not available in the UI. There are no proofs when it comes to best practices, there are only arguments. But I mentioned some arguments pro and against "using IDs only". – V G Nov 14 '14 at 10:13
  • @Christian I have added another details: an additional example when to use IDs and what to do when you use IDs. – V G Nov 18 '14 at 17:37