Do Not Use JPA Entity (Data Object) Across JPA Transactions

What is the issue

JPA entity is very commonly used in Hibernate involved Java applications, like Spring. Usually you can define DOs (data objects) to manage the database operations in some ORM way, but lots developers do not use the DOs property, one of the very common bad practice is the use the DOs across JPA transactions or outside JPA transactions.

JPA Entities represent persistent data stored in a relational database automatically using container-managed persistence, they are the representatives and abstractions on persistence related logics models. An entity can aggregate objects together and effectively persist data and related objects using the transactional, security, and concurrency services of a JPA persistence provider.

The JPA entities should be used as model interfaces of DAO (Data Access Objects) classes,  it should be always inside one JPA transaction, and if it is used across transactions like business services or used outside business service like responded to presentation classes, it should be always converted to none persistent models, all persistent related info should be hidden from the business service classes.  We do this for 2 major reasons:

1. Logic isolation, decouple the data persistent logics from business logics. so the your whole application architecture will be better layered and loose coupled for better scalability, maintainability and security.

2. Avoid transaction leak, data processing error and entity inconsistency so that you have your whole ORM styled entities maintained in a consistent, healthy and secure state.



Issue Examples

Example 1: LazyInitializationException

A good example that you should not use DO across transactions is the exception LazyInitializationException, since Hibernate implements the lazy loading by using proxy,  the query logic is wrapped inside the proxy object instead of the real data queried from DB,  the real data loading is triggered only your code logic first time uses the data.  however if the DO is loaded from transaction A and passed into transaction B, from the transaction B, the lazy loading data is actually first time fetched, since the the lazy load proxy still references the transaction A,  so you have whole code logic into a really undefined state.


There are several possible scenarios,  if the transaction A still open, the load load proxy still works and can load the data through the transaction A, but the data might be in some inconstant with the entities from the transaction B which very likely causes severe data issue, it also causes the transaction A to be referenced by transition B, transaction A either can not be completed and results in some ugly resource leak issue.

The other more famous  scenario is, the transaction A has been closed, the the lazy load proxy will throw the LazyInitializationException telling “No session or session was closed”,  it eventually crashes the transaction B and the investigation of the root cause might waster developer a lot of time.




Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s