Transaction Boundary and DTO conversion with JPA
Asked Answered
D

3

11

I have been wondering how this anomaly should be handled:

  1. DTO's should be converted in the controller, the service layer does not need to know about them.
  2. Transaction boundaries are defined by the service layer.

But how do you avoid a JPA LazyInitialization exception then? The DTO conversion might need Lazy Fetched data but is unable to as the transaction was handled by the service layer.

There are ways I can think of, but all of them are ugly. Putting the DTO conversion in the service layer seems the best to me now.

Dantzler answered 22/3, 2017 at 16:2 Comment(0)
E
8

Yes, definitely it is better to manipulate DTOs in the service layer. This is especially true when updating entities with changes contained in DTOs, as otherwise you would need to get and update detached entities, pass them to service, merge them again into the persistence context, etc.

"DTO's should be converted in the controller, the service layer does not need to know about them."

Instead of this, I would say the better rule of thumb is that controllers do not need to know about entities. But you can use detached entities instead of DTOs for simple cases to avoid creating lots of small DTO classes, although I personally always use DTOs just to be consistent and to make later changes easier.

Ember answered 5/4, 2017 at 20:18 Comment(0)
C
3

Raised 'LazyInitializationException' is just signal that some parts of data were not loaded, so the best solution will be to make multiple calls from controller method to service level and fetch all required fields for DTO.

Less elegant options are:

  1. Its possible to detect fields that were not loaded via 'org.hibernate.Hibernate.isInitialized' method and skip them during DTO build, see here full sample: How to test whether lazy loaded JPA collection is initialized?

  2. You can mark controller method as transactional, there will be opened hibernate session after call to service level and so lazy loading will work.

Cecelia answered 6/4, 2017 at 9:53 Comment(0)
U
1

DTOs are the model you should be working against from a layer above services. Only the service should know about the entity model. In simple degenerate cases the DTO model might look almost like the entity model which is why many people will just use the entity model. This works well until people get real requirements that will force them to change the way they use data. This is when the illusion that DTO = Entity falls apart.

A DTO is often a subset or a tranformation of the entity model. The point about the LazyInitializationException is a perfect example of when the illusion starts to crumble.

A service should return fully initialized DTOs i.e. not just some object that delegates to entity objects. There shouldn't be any lazy loading inovlved after a DTO was returned from a service. This means that you have to fetch exactly the state required for a DTO and wire that data into objects to be returned. Since that usually requires quite some boilerplate code and will sometimes result in having to duplicate logic, people tend to stick even longer to the DTO = Entity illusion by sprinkling some fetch joins here and there to make the LazyInitializationExceptions go away.

This is why I started the Blaze-Persistence Entity Views project which will give you the best of both worlds. Ease of use with less boilerplate, good performance and a safe model to avoid accidental errors. Maybe you want to give it a shot to see what it can do for you?

Underset answered 19/7, 2018 at 8:33 Comment(2)
I understand that. But,what happend when a service needs to call another service to get an entity? In this scenario they will always receive a DTO ,is that ok?Unexceptional
It depends on your needs, but in general, it is surely ok to work with DTOs within services. On a case by case basis, you can also introduce a dedicated entity/persistence model based API, let's call it Repository layer, which can be used to allow reuse on the lower level.Underset

© 2022 - 2024 — McMap. All rights reserved.