Support for Hibernate ORM 5.2

Issue: SPR-14327
This commit is contained in:
Juergen Hoeller 2016-06-02 23:35:31 +02:00
parent 521c41d75e
commit 9394616f8f
13 changed files with 134 additions and 63 deletions

View File

@ -46,7 +46,7 @@ configure(allprojects) { project ->
ext.hamcrestVersion = "1.3"
ext.hibernate3Version = "3.6.10.Final"
ext.hibernate4Version = "4.3.11.Final"
ext.hibernate5Version = "5.1.0.Final"
ext.hibernate5Version = "5.2.0.Final"
ext.hibval4Version = "4.3.2.Final"
ext.hibval5Version = "5.2.4.Final"
ext.hsqldbVersion = "2.3.4"
@ -798,7 +798,6 @@ project("spring-orm-hibernate5") {
provided(project(":spring-tx"))
optional(project(":spring-web"))
optional("org.hibernate:hibernate-core:${hibernate5Version}")
optional("org.hibernate:hibernate-entitymanager:${hibernate5Version}")
optional("javax.servlet:javax.servlet-api:3.0.1")
optional("javax.transaction:javax.transaction-api:${jtaVersion}")
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -480,7 +480,7 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana
if (!definition.isReadOnly() && !txObject.isNewSession()) {
// We need AUTO or COMMIT for a non-read-only transaction.
FlushMode flushMode = session.getFlushMode();
if (session.getFlushMode().equals(FlushMode.MANUAL)) {
if (FlushMode.MANUAL.equals(flushMode)) {
session.setFlushMode(FlushMode.AUTO);
txObject.getSessionHolder().setPreviousFlushMode(flushMode);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -27,7 +27,6 @@ import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Criteria;
import org.hibernate.Filter;
import org.hibernate.FlushMode;
@ -35,7 +34,6 @@ import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.Query;
import org.hibernate.ReplicationMode;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
@ -211,7 +209,7 @@ public class HibernateTemplate implements HibernateOperations, InitializingBean
* <p>To specify the query region to be used for queries cached
* by this template, set the "queryCacheRegion" property.
* @see #setQueryCacheRegion
* @see Query#setCacheable
* @see org.hibernate.Query#setCacheable
* @see Criteria#setCacheable
*/
public void setCacheQueries(boolean cacheQueries) {
@ -232,7 +230,7 @@ public class HibernateTemplate implements HibernateOperations, InitializingBean
* <p>The cache region will not take effect unless queries created by this
* template are configured to be cached via the "cacheQueries" property.
* @see #setCacheQueries
* @see Query#setCacheRegion
* @see org.hibernate.Query#setCacheRegion
* @see Criteria#setCacheRegion
*/
public void setQueryCacheRegion(String queryCacheRegion) {
@ -317,6 +315,7 @@ public class HibernateTemplate implements HibernateOperations, InitializingBean
* @return a result object returned by the action, or {@code null}
* @throws DataAccessException in case of Hibernate errors
*/
@SuppressWarnings("deprecation")
protected <T> T doExecute(HibernateCallback<T> action, boolean enforceNativeSession) throws DataAccessException {
Assert.notNull(action, "Callback object must not be null");
@ -499,7 +498,7 @@ public class HibernateTemplate implements HibernateOperations, InitializingBean
public <T> List<T> loadAll(final Class<T> entityClass) throws DataAccessException {
return executeWithNativeSession(new HibernateCallback<List<T>>() {
@Override
@SuppressWarnings("unchecked")
@SuppressWarnings({"unchecked", "deprecation"})
public List<T> doInHibernate(Session session) throws HibernateException {
Criteria criteria = session.createCriteria(entityClass);
criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
@ -862,8 +861,9 @@ public class HibernateTemplate implements HibernateOperations, InitializingBean
public List<?> find(final String queryString, final Object... values) throws DataAccessException {
return executeWithNativeSession(new HibernateCallback<List<?>>() {
@Override
@SuppressWarnings({"rawtypes", "deprecation"})
public List<?> doInHibernate(Session session) throws HibernateException {
Query queryObject = session.createQuery(queryString);
org.hibernate.Query queryObject = session.createQuery(queryString);
prepareQuery(queryObject);
if (values != null) {
for (int i = 0; i < values.length; i++) {
@ -891,13 +891,12 @@ public class HibernateTemplate implements HibernateOperations, InitializingBean
}
return executeWithNativeSession(new HibernateCallback<List<?>>() {
@Override
@SuppressWarnings({"rawtypes", "deprecation"})
public List<?> doInHibernate(Session session) throws HibernateException {
Query queryObject = session.createQuery(queryString);
org.hibernate.Query queryObject = session.createQuery(queryString);
prepareQuery(queryObject);
if (values != null) {
for (int i = 0; i < values.length; i++) {
applyNamedParameterToQuery(queryObject, paramNames[i], values[i]);
}
for (int i = 0; i < values.length; i++) {
applyNamedParameterToQuery(queryObject, paramNames[i], values[i]);
}
return queryObject.list();
}
@ -910,8 +909,9 @@ public class HibernateTemplate implements HibernateOperations, InitializingBean
return executeWithNativeSession(new HibernateCallback<List<?>>() {
@Override
@SuppressWarnings({"rawtypes", "deprecation"})
public List<?> doInHibernate(Session session) throws HibernateException {
Query queryObject = session.createQuery(queryString);
org.hibernate.Query queryObject = session.createQuery(queryString);
prepareQuery(queryObject);
queryObject.setProperties(valueBean);
return queryObject.list();
@ -928,8 +928,9 @@ public class HibernateTemplate implements HibernateOperations, InitializingBean
public List<?> findByNamedQuery(final String queryName, final Object... values) throws DataAccessException {
return executeWithNativeSession(new HibernateCallback<List<?>>() {
@Override
@SuppressWarnings({"rawtypes", "deprecation"})
public List<?> doInHibernate(Session session) throws HibernateException {
Query queryObject = session.getNamedQuery(queryName);
org.hibernate.Query queryObject = session.getNamedQuery(queryName);
prepareQuery(queryObject);
if (values != null) {
for (int i = 0; i < values.length; i++) {
@ -958,8 +959,9 @@ public class HibernateTemplate implements HibernateOperations, InitializingBean
}
return executeWithNativeSession(new HibernateCallback<List<?>>() {
@Override
@SuppressWarnings({"rawtypes", "deprecation"})
public List<?> doInHibernate(Session session) throws HibernateException {
Query queryObject = session.getNamedQuery(queryName);
org.hibernate.Query queryObject = session.getNamedQuery(queryName);
prepareQuery(queryObject);
if (values != null) {
for (int i = 0; i < values.length; i++) {
@ -977,8 +979,9 @@ public class HibernateTemplate implements HibernateOperations, InitializingBean
return executeWithNativeSession(new HibernateCallback<List<?>>() {
@Override
@SuppressWarnings({"rawtypes", "deprecation"})
public List<?> doInHibernate(Session session) throws HibernateException {
Query queryObject = session.getNamedQuery(queryName);
org.hibernate.Query queryObject = session.getNamedQuery(queryName);
prepareQuery(queryObject);
queryObject.setProperties(valueBean);
return queryObject.list();
@ -1033,6 +1036,7 @@ public class HibernateTemplate implements HibernateOperations, InitializingBean
}
@Override
@SuppressWarnings("deprecation")
public <T> List<T> findByExample(
final String entityName, final T exampleEntity, final int firstResult, final int maxResults)
throws DataAccessException {
@ -1066,8 +1070,9 @@ public class HibernateTemplate implements HibernateOperations, InitializingBean
public Iterator<?> iterate(final String queryString, final Object... values) throws DataAccessException {
return executeWithNativeSession(new HibernateCallback<Iterator<?>>() {
@Override
@SuppressWarnings({"rawtypes", "deprecation"})
public Iterator<?> doInHibernate(Session session) throws HibernateException {
Query queryObject = session.createQuery(queryString);
org.hibernate.Query queryObject = session.createQuery(queryString);
prepareQuery(queryObject);
if (values != null) {
for (int i = 0; i < values.length; i++) {
@ -1093,8 +1098,9 @@ public class HibernateTemplate implements HibernateOperations, InitializingBean
public int bulkUpdate(final String queryString, final Object... values) throws DataAccessException {
return executeWithNativeSession(new HibernateCallback<Integer>() {
@Override
@SuppressWarnings({"rawtypes", "deprecation"})
public Integer doInHibernate(Session session) throws HibernateException {
Query queryObject = session.createQuery(queryString);
org.hibernate.Query queryObject = session.createQuery(queryString);
prepareQuery(queryObject);
if (values != null) {
for (int i = 0; i < values.length; i++) {
@ -1122,7 +1128,7 @@ public class HibernateTemplate implements HibernateOperations, InitializingBean
* @see FlushMode#MANUAL
*/
protected void checkWriteOperationAllowed(Session session) throws InvalidDataAccessApiUsageException {
if (isCheckWriteOperations() && session.getFlushMode().lessThan(FlushMode.COMMIT)) {
if (isCheckWriteOperations() && SessionFactoryUtils.getFlushMode(session).lessThan(FlushMode.COMMIT)) {
throw new InvalidDataAccessApiUsageException(
"Write operations are not allowed in read-only mode (FlushMode.MANUAL): "+
"Turn your Session into FlushMode.COMMIT/AUTO or remove 'readOnly' marker from transaction definition.");
@ -1136,7 +1142,8 @@ public class HibernateTemplate implements HibernateOperations, InitializingBean
* @see #setCacheQueries
* @see #setQueryCacheRegion
*/
protected void prepareQuery(Query queryObject) {
@SuppressWarnings({"rawtypes", "deprecation"})
protected void prepareQuery(org.hibernate.Query queryObject) {
if (isCacheQueries()) {
queryObject.setCacheable(true);
if (getQueryCacheRegion() != null) {
@ -1192,7 +1199,8 @@ public class HibernateTemplate implements HibernateOperations, InitializingBean
* @param value the value of the parameter
* @throws HibernateException if thrown by the Query object
*/
protected void applyNamedParameterToQuery(Query queryObject, String paramName, Object value)
@SuppressWarnings({"rawtypes", "deprecation"})
protected void applyNamedParameterToQuery(org.hibernate.Query queryObject, String paramName, Object value)
throws HibernateException {
if (value instanceof Collection) {
@ -1221,6 +1229,7 @@ public class HibernateTemplate implements HibernateOperations, InitializingBean
}
@Override
@SuppressWarnings("deprecation")
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// Invocation on Session interface coming in...
@ -1243,8 +1252,8 @@ public class HibernateTemplate implements HibernateOperations, InitializingBean
// If return value is a Query or Criteria, apply transaction timeout.
// Applies to createQuery, getNamedQuery, createCriteria.
if (retVal instanceof Query) {
prepareQuery(((Query) retVal));
if (retVal instanceof org.hibernate.Query) {
prepareQuery(((org.hibernate.Query) retVal));
}
if (retVal instanceof Criteria) {
prepareCriteria(((Criteria) retVal));

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -412,6 +412,7 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana
}
@Override
@SuppressWarnings("deprecation")
protected void doBegin(Object transaction, TransactionDefinition definition) {
HibernateTransactionObject txObject = (HibernateTransactionObject) transaction;
@ -476,8 +477,8 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana
if (!definition.isReadOnly() && !txObject.isNewSession()) {
// We need AUTO or COMMIT for a non-read-only transaction.
FlushMode flushMode = session.getFlushMode();
if (session.getFlushMode().equals(FlushMode.MANUAL)) {
FlushMode flushMode = SessionFactoryUtils.getFlushMode(session);
if (FlushMode.MANUAL.equals(flushMode)) {
session.setFlushMode(FlushMode.AUTO);
txObject.getSessionHolder().setPreviousFlushMode(flushMode);
}
@ -627,6 +628,7 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana
}
@Override
@SuppressWarnings("deprecation")
protected void doCleanupAfterCompletion(Object transaction) {
HibernateTransactionObject txObject = (HibernateTransactionObject) transaction;
@ -703,6 +705,7 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana
* @param session the Hibernate Session to check
* @see ConnectionReleaseMode#ON_CLOSE
*/
@SuppressWarnings("deprecation")
protected boolean isSameConnectionForEntireSession(Session session) {
if (!(session instanceof SessionImplementor)) {
// The best we can do is to assume we're safe.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,10 +16,12 @@
package org.springframework.orm.hibernate5;
import java.lang.reflect.Method;
import javax.sql.DataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.FlushMode;
import org.hibernate.HibernateException;
import org.hibernate.JDBCException;
import org.hibernate.NonUniqueObjectException;
@ -59,6 +61,8 @@ import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.dao.InvalidDataAccessResourceUsageException;
import org.springframework.dao.PessimisticLockingFailureException;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
/**
* Helper class featuring methods for Hibernate Session handling.
@ -86,6 +90,57 @@ public abstract class SessionFactoryUtils {
static final Log logger = LogFactory.getLog(SessionFactoryUtils.class);
private static Method getFlushMode;
static {
try {
// Hibernate 5.2+ getHibernateFlushMode()
getFlushMode = Session.class.getMethod("getHibernateFlushMode");
}
catch (NoSuchMethodException ex) {
try {
// Hibernate 5.0/5.1 getFlushMode() with FlushMode return type
getFlushMode = Session.class.getMethod("getFlushMode");
}
catch (NoSuchMethodException ex2) {
throw new IllegalStateException("No compatible Hibernate getFlushMode signature found", ex2);
}
}
// Check that it is the Hibernate FlushMode type, not JPA's...
Assert.state(FlushMode.class == getFlushMode.getReturnType());
}
/**
* Get the native Hibernate FlushMode, adapting between Hibernate 5.0/5.1 and 5.2+.
* @param session the Hibernate Session to get the flush mode from
* @return the FlushMode (never {@code null})
* @since 4.3
*/
static FlushMode getFlushMode(Session session) {
return (FlushMode) ReflectionUtils.invokeMethod(getFlushMode, session);
}
/**
* Perform actual closing of the Hibernate Session,
* catching and logging any cleanup exceptions thrown.
* @param session the Hibernate Session to close (may be {@code null})
* @see Session#close()
*/
public static void closeSession(Session session) {
if (session != null) {
try {
session.close();
}
catch (HibernateException ex) {
logger.debug("Could not close Hibernate Session", ex);
}
catch (Throwable ex) {
logger.debug("Unexpected exception on closing Hibernate Session", ex);
}
}
}
/**
* Determine the DataSource of the given SessionFactory.
* @param sessionFactory the SessionFactory to check
@ -114,26 +169,6 @@ public abstract class SessionFactoryUtils {
return null;
}
/**
* Perform actual closing of the Hibernate Session,
* catching and logging any cleanup exceptions thrown.
* @param session the Hibernate Session to close (may be {@code null})
* @see Session#close()
*/
public static void closeSession(Session session) {
if (session != null) {
try {
session.close();
}
catch (HibernateException ex) {
logger.debug("Could not close Hibernate Session", ex);
}
catch (Throwable ex) {
logger.debug("Unexpected exception on closing Hibernate Session", ex);
}
}
}
/**
* Convert the given HibernateException to an appropriate exception
* from the {@code org.springframework.dao} hierarchy.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -38,6 +38,7 @@ public class SpringJtaSessionContext extends JTASessionContext {
}
@Override
@SuppressWarnings("deprecation")
protected Session buildOrObtainSession() {
Session session = super.buildOrObtainSession();
if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -76,6 +76,7 @@ public class SpringSessionContext implements CurrentSessionContext {
* Retrieve the Spring-managed Session for the current thread, if any.
*/
@Override
@SuppressWarnings("deprecation")
public Session currentSession() throws HibernateException {
Object value = TransactionSynchronizationManager.getResource(this.sessionFactory);
if (value instanceof Session) {
@ -91,7 +92,7 @@ public class SpringSessionContext implements CurrentSessionContext {
sessionHolder.setSynchronizedWithTransaction(true);
// Switch to FlushMode.AUTO, as we have to assume a thread-bound Session
// with FlushMode.MANUAL, which needs to allow flushing within the transaction.
FlushMode flushMode = session.getFlushMode();
FlushMode flushMode = SessionFactoryUtils.getFlushMode(session);
if (flushMode.equals(FlushMode.MANUAL) &&
!TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
session.setFlushMode(FlushMode.AUTO);

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -111,6 +111,7 @@ public class SpringSessionSynchronization implements TransactionSynchronization,
}
@Override
@SuppressWarnings("deprecation")
public void beforeCompletion() {
try {
Session session = this.sessionHolder.getSession();

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -199,6 +199,7 @@ public class OpenSessionInViewFilter extends OncePerRequestFilter {
* @throws DataAccessResourceFailureException if the Session could not be created
* @see FlushMode#MANUAL
*/
@SuppressWarnings("deprecation")
protected Session openSession(SessionFactory sessionFactory) throws DataAccessResourceFailureException {
try {
Session session = sessionFactory.openSession();

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -179,6 +179,7 @@ public class OpenSessionInViewInterceptor implements AsyncWebRequestInterceptor
* @throws DataAccessResourceFailureException if the Session could not be created
* @see FlushMode#MANUAL
*/
@SuppressWarnings("deprecation")
protected Session openSession() throws DataAccessResourceFailureException {
try {
Session session = getSessionFactory().openSession();

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -102,6 +102,7 @@ public class OpenSessionInterceptor implements MethodInterceptor, InitializingBe
* @throws DataAccessResourceFailureException if the Session could not be created
* @see FlushMode#MANUAL
*/
@SuppressWarnings("deprecation")
protected Session openSession() throws DataAccessResourceFailureException {
try {
Session session = getSessionFactory().openSession();

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -66,13 +66,14 @@ import org.springframework.orm.jpa.JpaSystemException;
import org.springframework.transaction.InvalidIsolationLevelException;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionException;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
/**
* {@link org.springframework.orm.jpa.JpaDialect} implementation for
* Hibernate EntityManager. Developed and tested against Hibernate 3.6,
* 4.2/4.3 as well as 5.0.
* 4.2/4.3 as well as 5.0/5.1/5.2.
*
* @author Juergen Hoeller
* @author Costin Leau
@ -88,6 +89,8 @@ public class HibernateJpaDialect extends DefaultJpaDialect {
private static Class<?> pessimisticLockExceptionClass;
private static Method getFlushMode;
static {
// Checking for Hibernate 4.x's Optimistic/PessimisticEntityLockException
ClassLoader cl = HibernateJpaDialect.class.getClassLoader();
@ -104,6 +107,22 @@ public class HibernateJpaDialect extends DefaultJpaDialect {
catch (ClassNotFoundException ex) {
pessimisticLockExceptionClass = null;
}
try {
// Hibernate 5.2+ getHibernateFlushMode()
getFlushMode = Session.class.getMethod("getHibernateFlushMode");
}
catch (NoSuchMethodException ex) {
try {
// Classic Hibernate getFlushMode() with FlushMode return type
getFlushMode = Session.class.getMethod("getFlushMode");
}
catch (NoSuchMethodException ex2) {
throw new IllegalStateException("No compatible Hibernate getFlushMode signature found", ex2);
}
}
// Check that it is the Hibernate FlushMode type, not JPA's...
Assert.state(FlushMode.class == getFlushMode.getReturnType());
}
@ -184,7 +203,7 @@ public class HibernateJpaDialect extends DefaultJpaDialect {
}
protected FlushMode prepareFlushMode(Session session, boolean readOnly) throws PersistenceException {
FlushMode flushMode = session.getFlushMode();
FlushMode flushMode = (FlushMode) ReflectionUtils.invokeMethod(getFlushMode, session);
if (readOnly) {
// We should suppress flushing for a read-only transaction.
if (!flushMode.equals(FlushMode.MANUAL)) {

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -35,7 +35,7 @@ import org.hibernate.dialect.SQLServerDialect;
/**
* {@link org.springframework.orm.jpa.JpaVendorAdapter} implementation for Hibernate
* EntityManager. Developed and tested against Hibernate 3.6, 4.2/4.3 as well as 5.0.
* EntityManager. Developed and tested against Hibernate 3.6, 4.2/4.3 as well as 5.x.
* <b>Hibernate 4.2+ is strongly recommended for use with Spring 4.0+.</b>
*
* <p>Exposes Hibernate's persistence provider and EntityManager extension interface,