1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springframework.orm.jpa.vendor;
18
19 import java.lang.reflect.Method;
20 import java.sql.Connection;
21 import java.sql.SQLException;
22 import javax.persistence.EntityManager;
23 import javax.persistence.PersistenceException;
24
25 import org.hibernate.FlushMode;
26 import org.hibernate.HibernateException;
27 import org.hibernate.NonUniqueObjectException;
28 import org.hibernate.NonUniqueResultException;
29 import org.hibernate.ObjectDeletedException;
30 import org.hibernate.OptimisticLockException;
31 import org.hibernate.PersistentObjectException;
32 import org.hibernate.PessimisticLockException;
33 import org.hibernate.PropertyValueException;
34 import org.hibernate.QueryException;
35 import org.hibernate.QueryTimeoutException;
36 import org.hibernate.Session;
37 import org.hibernate.StaleObjectStateException;
38 import org.hibernate.StaleStateException;
39 import org.hibernate.TransientObjectException;
40 import org.hibernate.UnresolvableObjectException;
41 import org.hibernate.WrongClassException;
42 import org.hibernate.exception.ConstraintViolationException;
43 import org.hibernate.exception.DataException;
44 import org.hibernate.exception.JDBCConnectionException;
45 import org.hibernate.exception.LockAcquisitionException;
46 import org.hibernate.exception.SQLGrammarException;
47
48 import org.springframework.dao.CannotAcquireLockException;
49 import org.springframework.dao.DataAccessException;
50 import org.springframework.dao.DataAccessResourceFailureException;
51 import org.springframework.dao.DataIntegrityViolationException;
52 import org.springframework.dao.DuplicateKeyException;
53 import org.springframework.dao.IncorrectResultSizeDataAccessException;
54 import org.springframework.dao.InvalidDataAccessApiUsageException;
55 import org.springframework.dao.InvalidDataAccessResourceUsageException;
56 import org.springframework.dao.PessimisticLockingFailureException;
57 import org.springframework.jdbc.datasource.ConnectionHandle;
58 import org.springframework.jdbc.datasource.DataSourceUtils;
59 import org.springframework.jdbc.support.JdbcUtils;
60 import org.springframework.orm.ObjectOptimisticLockingFailureException;
61 import org.springframework.orm.ObjectRetrievalFailureException;
62 import org.springframework.orm.jpa.DefaultJpaDialect;
63 import org.springframework.orm.jpa.EntityManagerFactoryUtils;
64 import org.springframework.orm.jpa.JpaSystemException;
65 import org.springframework.transaction.InvalidIsolationLevelException;
66 import org.springframework.transaction.TransactionDefinition;
67 import org.springframework.transaction.TransactionException;
68 import org.springframework.util.ClassUtils;
69 import org.springframework.util.ReflectionUtils;
70
71
72
73
74
75
76
77
78
79 @SuppressWarnings({"serial", "deprecation"})
80 public class HibernateJpaDialect extends DefaultJpaDialect {
81
82 private static Class<?> optimisticLockExceptionClass;
83
84 private static Class<?> pessimisticLockExceptionClass;
85
86 static {
87
88 ClassLoader cl = HibernateJpaDialect.class.getClassLoader();
89 try {
90 optimisticLockExceptionClass = cl.loadClass("org.hibernate.dialect.lock.OptimisticEntityLockException");
91 }
92 catch (ClassNotFoundException ex) {
93
94 optimisticLockExceptionClass = OptimisticLockException.class;
95 }
96 try {
97 pessimisticLockExceptionClass = cl.loadClass("org.hibernate.dialect.lock.PessimisticEntityLockException");
98 }
99 catch (ClassNotFoundException ex) {
100 pessimisticLockExceptionClass = null;
101 }
102 }
103
104
105 private boolean prepareConnection = (HibernateConnectionHandle.sessionConnectionMethod == null);
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 public void setPrepareConnection(boolean prepareConnection) {
133 this.prepareConnection = prepareConnection;
134 }
135
136
137 @Override
138 public Object beginTransaction(EntityManager entityManager, TransactionDefinition definition)
139 throws PersistenceException, SQLException, TransactionException {
140
141 Session session = getSession(entityManager);
142
143 if (definition.getTimeout() != TransactionDefinition.TIMEOUT_DEFAULT) {
144 session.getTransaction().setTimeout(definition.getTimeout());
145 }
146
147 boolean isolationLevelNeeded = (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT);
148 Integer previousIsolationLevel = null;
149 boolean resetConnection = false;
150
151 if (isolationLevelNeeded || definition.isReadOnly()) {
152 if (this.prepareConnection) {
153 Connection con = HibernateConnectionHandle.doGetConnection(session);
154 previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
155 resetConnection = true;
156 }
157 else if (isolationLevelNeeded) {
158 throw new InvalidIsolationLevelException(getClass().getSimpleName() +
159 " does not support custom isolation levels since the 'prepareConnection' flag is off. " +
160 "This is the case on Hibernate 3.6 by default; either switch that flag at your own risk " +
161 "or upgrade to Hibernate 4.x, with 4.2+ recommended.");
162 }
163 }
164
165
166 entityManager.getTransaction().begin();
167
168
169 FlushMode previousFlushMode = prepareFlushMode(session, definition.isReadOnly());
170 return new SessionTransactionData(session, previousFlushMode, resetConnection, previousIsolationLevel);
171 }
172
173 @Override
174 public Object prepareTransaction(EntityManager entityManager, boolean readOnly, String name)
175 throws PersistenceException {
176
177 Session session = getSession(entityManager);
178 FlushMode previousFlushMode = prepareFlushMode(session, readOnly);
179 return new SessionTransactionData(session, previousFlushMode, false, null);
180 }
181
182 protected FlushMode prepareFlushMode(Session session, boolean readOnly) throws PersistenceException {
183 FlushMode flushMode = session.getFlushMode();
184 if (readOnly) {
185
186 if (!flushMode.equals(FlushMode.MANUAL)) {
187 session.setFlushMode(FlushMode.MANUAL);
188 return flushMode;
189 }
190 }
191 else {
192
193 if (flushMode.lessThan(FlushMode.COMMIT)) {
194 session.setFlushMode(FlushMode.AUTO);
195 return flushMode;
196 }
197 }
198
199 return null;
200 }
201
202 @Override
203 public void cleanupTransaction(Object transactionData) {
204 ((SessionTransactionData) transactionData).resetSessionState();
205 }
206
207 @Override
208 public ConnectionHandle getJdbcConnection(EntityManager entityManager, boolean readOnly)
209 throws PersistenceException, SQLException {
210
211 Session session = getSession(entityManager);
212 return new HibernateConnectionHandle(session);
213 }
214
215 @Override
216 public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
217 if (ex instanceof HibernateException) {
218 return convertHibernateAccessException((HibernateException) ex);
219 }
220 if (ex instanceof PersistenceException && ex.getCause() instanceof HibernateException) {
221 return convertHibernateAccessException((HibernateException) ex.getCause());
222 }
223 return EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(ex);
224 }
225
226
227
228
229
230
231
232 protected DataAccessException convertHibernateAccessException(HibernateException ex) {
233 if (ex instanceof JDBCConnectionException) {
234 return new DataAccessResourceFailureException(ex.getMessage(), ex);
235 }
236 if (ex instanceof SQLGrammarException) {
237 SQLGrammarException jdbcEx = (SQLGrammarException) ex;
238 return new InvalidDataAccessResourceUsageException(ex.getMessage() + "; SQL [" + jdbcEx.getSQL() + "]", ex);
239 }
240 if (ex instanceof QueryTimeoutException) {
241 QueryTimeoutException jdbcEx = (QueryTimeoutException) ex;
242 return new org.springframework.dao.QueryTimeoutException(ex.getMessage() + "; SQL [" + jdbcEx.getSQL() + "]", ex);
243 }
244 if (ex instanceof LockAcquisitionException) {
245 LockAcquisitionException jdbcEx = (LockAcquisitionException) ex;
246 return new CannotAcquireLockException(ex.getMessage() + "; SQL [" + jdbcEx.getSQL() + "]", ex);
247 }
248 if (ex instanceof PessimisticLockException) {
249 PessimisticLockException jdbcEx = (PessimisticLockException) ex;
250 return new PessimisticLockingFailureException(ex.getMessage() + "; SQL [" + jdbcEx.getSQL() + "]", ex);
251 }
252 if (ex instanceof ConstraintViolationException) {
253 ConstraintViolationException jdbcEx = (ConstraintViolationException) ex;
254 return new DataIntegrityViolationException(ex.getMessage() + "; SQL [" + jdbcEx.getSQL() +
255 "]; constraint [" + jdbcEx.getConstraintName() + "]", ex);
256 }
257 if (ex instanceof DataException) {
258 DataException jdbcEx = (DataException) ex;
259 return new DataIntegrityViolationException(ex.getMessage() + "; SQL [" + jdbcEx.getSQL() + "]", ex);
260 }
261
262
263 if (ex instanceof QueryException) {
264 return new InvalidDataAccessResourceUsageException(ex.getMessage(), ex);
265 }
266 if (ex instanceof NonUniqueResultException) {
267 return new IncorrectResultSizeDataAccessException(ex.getMessage(), 1, ex);
268 }
269 if (ex instanceof NonUniqueObjectException) {
270 return new DuplicateKeyException(ex.getMessage(), ex);
271 }
272 if (ex instanceof PropertyValueException) {
273 return new DataIntegrityViolationException(ex.getMessage(), ex);
274 }
275 if (ex instanceof PersistentObjectException) {
276 return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
277 }
278 if (ex instanceof TransientObjectException) {
279 return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
280 }
281 if (ex instanceof ObjectDeletedException) {
282 return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
283 }
284 if (ex instanceof UnresolvableObjectException) {
285 UnresolvableObjectException hibEx = (UnresolvableObjectException) ex;
286 return new ObjectRetrievalFailureException(hibEx.getEntityName(), hibEx.getIdentifier(), ex.getMessage(), ex);
287 }
288 if (ex instanceof WrongClassException) {
289 WrongClassException hibEx = (WrongClassException) ex;
290 return new ObjectRetrievalFailureException(hibEx.getEntityName(), hibEx.getIdentifier(), ex.getMessage(), ex);
291 }
292 if (ex instanceof StaleObjectStateException) {
293 StaleObjectStateException hibEx = (StaleObjectStateException) ex;
294 return new ObjectOptimisticLockingFailureException(hibEx.getEntityName(), hibEx.getIdentifier(), ex);
295 }
296 if (ex instanceof StaleStateException) {
297 return new ObjectOptimisticLockingFailureException(ex.getMessage(), ex);
298 }
299 if (optimisticLockExceptionClass.isInstance(ex)) {
300 return new ObjectOptimisticLockingFailureException(ex.getMessage(), ex);
301 }
302 if (pessimisticLockExceptionClass != null && pessimisticLockExceptionClass.isInstance(ex)) {
303 if (ex.getCause() instanceof LockAcquisitionException) {
304 return new CannotAcquireLockException(ex.getMessage(), ex.getCause());
305 }
306 return new PessimisticLockingFailureException(ex.getMessage(), ex);
307 }
308
309
310 return new JpaSystemException(ex);
311 }
312
313 protected Session getSession(EntityManager entityManager) {
314 return entityManager.unwrap(Session.class);
315 }
316
317
318 private static class SessionTransactionData {
319
320 private final Session session;
321
322 private final FlushMode previousFlushMode;
323
324 private final boolean resetConnection;
325
326 private final Integer previousIsolationLevel;
327
328 public SessionTransactionData(
329 Session session, FlushMode previousFlushMode, boolean resetConnection, Integer previousIsolationLevel) {
330 this.session = session;
331 this.previousFlushMode = previousFlushMode;
332 this.resetConnection = resetConnection;
333 this.previousIsolationLevel = previousIsolationLevel;
334 }
335
336 public void resetSessionState() {
337 if (this.previousFlushMode != null) {
338 this.session.setFlushMode(this.previousFlushMode);
339 }
340 if (this.resetConnection && this.session.isConnected()) {
341 Connection con = HibernateConnectionHandle.doGetConnection(this.session);
342 DataSourceUtils.resetConnectionAfterTransaction(con, this.previousIsolationLevel);
343 }
344 }
345 }
346
347
348 private static class HibernateConnectionHandle implements ConnectionHandle {
349
350
351 private static final Method sessionConnectionMethod =
352 ClassUtils.getMethodIfAvailable(Session.class, "connection");
353
354 private static volatile Method connectionMethodToUse = sessionConnectionMethod;
355
356 private final Session session;
357
358 public HibernateConnectionHandle(Session session) {
359 this.session = session;
360 }
361
362 @Override
363 public Connection getConnection() {
364 return doGetConnection(this.session);
365 }
366
367 @Override
368 public void releaseConnection(Connection con) {
369 if (sessionConnectionMethod != null) {
370
371
372
373
374
375 JdbcUtils.closeConnection(con);
376 }
377 }
378
379 public static Connection doGetConnection(Session session) {
380 try {
381 if (connectionMethodToUse == null) {
382
383 connectionMethodToUse = session.getClass().getMethod("connection");
384 }
385 return (Connection) ReflectionUtils.invokeMethod(connectionMethodToUse, session);
386 }
387 catch (NoSuchMethodException ex) {
388 throw new IllegalStateException("Cannot find connection() method on Hibernate Session", ex);
389 }
390 }
391 }
392
393 }