1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
|
---
title: Using JPAContainer with Hibernate
order: 9
layout: page
---
[[jpacontainer.hibernate]]
= Using JPAContainer with Hibernate
Hibernate needs special handling in some cases.
[[jpacontainer.hibernate.lazyloading]]
== Lazy loading
In order for lazy loading to work automatically, an entity must be attached to
an entity manager. Unfortunately, Hibernate can not keep entity managers for
long without problems. To work around the problem, you need to use a special
lazy loading delegate for Hibernate.
JPAContainer entity providers handle lazy loading in delegates defined by the
[interfacename]#LazyLoadingDelegate# interface. The default implementation for
Hibernate is defined in [classname]#HibernateLazyLoadingDelegate#. You can
instantiate one and use it in an entity provider with
[methodname]#setLazyLoadingDelegate()#.
The default implementation works so that whenever a lazy property is accessed
through the Vaadin Property interface, the value is retrieved with a separate
(JPA Criteria API) query using the currently active entity manager. The value is
then manually attached to the entity instance, which is detached from the entity
manager. If this default implementation is not good enough, you may need to make
your own implementation.
ifdef::web[]
[[jpacontainer.hibernate.em-per-request]]
== The EntityManager-Per-Request pattern
One issue with Hibernate is that it is designed for short-lived sessions, but
the lifetime of an entity manager is normally roughly that of a user session.
The problem is that if an error occurs in a session or an entity manager, the
manager becomes unuseable. This causes big problems with long-lived sessions
that would work fine with EclipseLink.
The recommended solution is to use the __EntityManager-per-Request__ pattern. It
is highly recommended always when using Hibernate.
An entity manager can only be open during the request-response cycle of the
Vaadin servlet, so that one is created at the beginning of the request and
closed at the end.
[[jpacontainer.hibernate.em-per-request.provider]]
=== Storing an Entity Manager
You first need to implement an [interfacename]#EntityManagerProvider# that
returns a stored [interfacename]#EntityManager# with
[methodname]#getEntityManager()#. The entity manager must be stored in a
[classname]#ThreadLocal# variable.
----
public class LazyHibernateEntityManagerProvider
implements EntityManagerProvider {
private static ThreadLocal<EntityManager>
entityManagerThreadLocal =
new ThreadLocal<EntityManager>();
@Override
public EntityManager getEntityManager() {
return entityManagerThreadLocal.get();
}
public static void setCurrentEntityManager(
EntityManager em) {
entityManagerThreadLocal.set(em);
}
}
----
You need to create and store the per-request instance at the beginning of each
request with [methodname]#setCurrentEntityManager()# and clear it at the end by
setting it as [literal]#++null++#.
[[jpacontainer.hibernate.em-per-request.provider]]
=== Creating Entity Managers in a Servlet Filter
You can create the entity managers for each request either by extending
[classname]#VaadinServlet# and overriding the [methodname]#service()# method or
by implementing a servlet filter. In the following, we describe how to implement
a servlet filter to do the task, but overriding the servlet could be even
easier.
----
public class LazyHibernateServletFilter
implements Filter {
private EntityManagerFactory entityManagerFactory;
@Override
public void init(FilterConfig filterConfig)
throws ServletException {
entityManagerFactory = Persistence
.createEntityManagerFactory("lazyhibernate");
}
@Override
public void doFilter(ServletRequest servletRequest,
ServletResponse servletResponse,
FilterChain filterChain)
throws IOException, ServletException {
try {
// Create and set the entity manager
LazyHibernateEntityManagerProvider
.setCurrentEntityManager(
entityManagerFactory
.createEntityManager());
// Handle the request
filterChain.doFilter(servletRequest,
servletResponse);
} finally {
// Reset the entity manager
LazyHibernateEntityManagerProvider
.setCurrentEntityManager(null);
}
}
@Override
public void destroy() {
entityManagerFactory = null;
}
}
----
You need to define the servlet filter in the [filename]#web.xml# deployment
descriptor as follows:
[subs="normal"]
----
<filter>
<filter-name>**LazyHibernateServletFilter**</filter-name>
<filter-class>**com.example.LazyHibernateServletFilter**</filter-class>
</filter>
<filter-mapping>
<filter-name>**LazyHibernateServletFilter**</filter-name>
<url-pattern>**/++*++**</url-pattern>
</filter-mapping>
----
The [literal]#++url-pattern++# must match the pattern for your Vaadin servlet.
endif::web[]
[[jpacontainer.hibernate.joins]]
== Joins in Hibernate vs EclipseLink
EclipseLink supports implicit joins, while Hibernate requires explicit joins. In
SQL terms, an explicit join is a " [literal]#++FROM a INNER JOIN b ON a.bid =
b.id++#" expression, while an implicit join is done in a WHERE clause, such as:
" [literal]#++FROM a,b WHERE a.bid = b.id++#".
In a JPAContainer filter with EclipseLink, an implicit join would have form:
----
new Equal("skills.skill", s)
----
In Hibernate you would need to use [classname]#JoinFilter# for the explicit
join:
----
new JoinFilter("skills", new Equal("skill", s))
----
|