While a lot of posts have been written on the subject of Spring's OpenSession/EntityManagerInViewFilter, I couldn't find any that mentions its flaws. From what I understand, and assuming a typical layered web application architecture using a @Transactional service layer, the filter works as follows:
- The filter intercepts a servlet request
- The filter opens an EntityManager and binds it to the current thread
- Web controller is called
- Web controller calls service
- Transaction interceptor begins a new transaction, retrieves the thread-bound EntityManager and binds it to the transaction
- Service is called, does some stuff with EntityManager, then returns
- Transaction interceptor flushes the EntityManager then commits the transaction
- Web controller prepares view, then returns
- View is built
- Filter closes the EntityManager and unbinds it from current thread
In steps 8 and 9, objects that were loaded by the thread's EntityManager are still managed. Consequently, if lazy associations are touched in these steps, they will be loaded from the database using the still open EntityManager. From what I understand, each such access will require that the database open a transaction. Spring's transaction management will be unaware of this, hence my calling it "implicit transaction".
I see 2 problems with this:
- Loading several lazy associations will result in multiple database transactions, a possible hit on performance
- The root object and its lazy associations are loaded in different database transactions, so the data may possibly be stale (e.g. root loaded by thread 1, root associatio开发者_StackOverflow中文版ns updated by thread 2, root associations loaded by thread 1)
On the one hand, these 2 issues seem enough to reject using this filter (performance hit, data inconsistency). On the other hand, this solution is very convenient, avoids writing several lines of code, problem 1 may not be that noticeable and problem 2 may be pure paranoia.
What do you think?
Thanks!
As you said, the OpenSessionInView filter is very convenient in web applications. Regarding the limitations you mentioned:
1) Loading several lazy associations will result in multiple database transactions, a possible hit on performance.
Yes, going to the DB often might lead to performance problems. Ideally you want to fetch all the data you need in one trip. Consider using Hibernate join-fetch for this. But fetching too much data from the DB will also be slow. The rule of thumb I use is to use join fetching if the data is needed every time I paint the view; if the data is not needed in most cases, I let Hibernate lazy fetch it when I need it - the threadlocal open session helps then.
2) The root object and its lazy associations are loaded in different database transactions, so the data may possibly be stale (e.g. root loaded by thread 1, root associations updated by thread 2, root associations loaded by thread 1).
Imagine writing this application in JDBC - if the application's consistency requirements demand that the root and leaves both should be loaded in the same txn, use join fetching. If not, which is often the case, lazy fetching won't lead to any consistency problems.
IMHO, the more important disadvantage with OpenSessionInView is when you want your service layer to be reused in a non-web context. From your description, you don't seem to have that problem.
The main argument I've heard against OpenSessionInView and lazy loading is an excess of transactions and a negative impact on performance. It is extremely convenient to use on an app with low usage requirements, but on a high-scale app, I'd recommend using the old fashioned fully-populated DTOs (data-transfer objects).
One of the major issues I've encounter with OpenSessionInViewFilter is using AJAX apps and javascript. If you are using javascript to render grids, or certain UI components; there is a lazy-load (considering you turn on the filter); and an exception is thrown. Your application UI rendering goes for a toss. The data might never show up, the page starts throwing weird javascript exceptions, for which you need to write additional js code to handle. And you are exposing the db exceptions to users (not a good idea).
In a regular application these exceptions can be captured and a valid user exception can be thrown.
If your application is a multi layered architecture ( view layer deployed on different JVM and service layer will be deployed on different VM), then keep the session in open state doesn't make sense. I would see not use any OpenSessionViewFilter if your service layer independent of you app layer.
精彩评论