Upgrade Hibernate support baseline to 5.2+

Closes gh-25533
Closes gh-22326
This commit is contained in:
Juergen Hoeller 2020-08-05 14:14:31 +02:00
parent b6d2a2980d
commit ba65cef52b
10 changed files with 50 additions and 183 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2018 the original author or authors. * Copyright 2002-2020 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -60,7 +60,7 @@ import org.springframework.lang.Nullable;
* general {@link #execute} method and custom lambda blocks creating the queries, * general {@link #execute} method and custom lambda blocks creating the queries,
* ideally setting named parameters through {@link org.hibernate.query.Query}. * ideally setting named parameters through {@link org.hibernate.query.Query}.
* <b>Please be aware that deprecated operations are known to work with Hibernate * <b>Please be aware that deprecated operations are known to work with Hibernate
* ORM 5.0-5.2 but may not work with Hibernate ORM 5.3 and higher anymore.</b> * ORM 5.2 but may not work with Hibernate ORM 5.3 and higher anymore.</b>
* *
* @author Juergen Hoeller * @author Juergen Hoeller
* @since 4.2 * @since 4.2

View File

@ -41,6 +41,7 @@ import org.hibernate.Session;
import org.hibernate.SessionFactory; import org.hibernate.SessionFactory;
import org.hibernate.criterion.DetachedCriteria; import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Example; import org.hibernate.criterion.Example;
import org.hibernate.query.Query;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
import org.springframework.dao.DataAccessException; import org.springframework.dao.DataAccessException;
@ -49,7 +50,6 @@ import org.springframework.lang.Nullable;
import org.springframework.transaction.support.ResourceHolderSupport; import org.springframework.transaction.support.ResourceHolderSupport;
import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
/** /**
* Helper class that simplifies Hibernate data access code. Automatically * Helper class that simplifies Hibernate data access code. Automatically
@ -90,23 +90,6 @@ import org.springframework.util.ReflectionUtils;
*/ */
public class HibernateTemplate implements HibernateOperations, InitializingBean { public class HibernateTemplate implements HibernateOperations, InitializingBean {
private static final Method createQueryMethod;
private static final Method getNamedQueryMethod;
static {
// Hibernate 5.2's createQuery method declares a new subtype as return type,
// so we need to use reflection for binary compatibility with 5.0/5.1 here.
try {
createQueryMethod = Session.class.getMethod("createQuery", String.class);
getNamedQueryMethod = Session.class.getMethod("getNamedQuery", String.class);
}
catch (NoSuchMethodException ex) {
throw new IllegalStateException("Incompatible Hibernate Session API", ex);
}
}
protected final Log logger = LogFactory.getLog(getClass()); protected final Log logger = LogFactory.getLog(getClass());
@Nullable @Nullable
@ -250,7 +233,7 @@ public class HibernateTemplate implements HibernateOperations, InitializingBean
* <p>To specify the query region to be used for queries cached * <p>To specify the query region to be used for queries cached
* by this template, set the "queryCacheRegion" property. * by this template, set the "queryCacheRegion" property.
* @see #setQueryCacheRegion * @see #setQueryCacheRegion
* @see org.hibernate.Query#setCacheable * @see Query#setCacheable
* @see Criteria#setCacheable * @see Criteria#setCacheable
*/ */
public void setCacheQueries(boolean cacheQueries) { public void setCacheQueries(boolean cacheQueries) {
@ -271,7 +254,7 @@ public class HibernateTemplate implements HibernateOperations, InitializingBean
* <p>The cache region will not take effect unless queries created by this * <p>The cache region will not take effect unless queries created by this
* template are configured to be cached via the "cacheQueries" property. * template are configured to be cached via the "cacheQueries" property.
* @see #setCacheQueries * @see #setCacheQueries
* @see org.hibernate.Query#setCacheRegion * @see Query#setCacheRegion
* @see Criteria#setCacheRegion * @see Criteria#setCacheRegion
*/ */
public void setQueryCacheRegion(@Nullable String queryCacheRegion) { public void setQueryCacheRegion(@Nullable String queryCacheRegion) {
@ -359,7 +342,6 @@ public class HibernateTemplate implements HibernateOperations, InitializingBean
* @return a result object returned by the action, or {@code null} * @return a result object returned by the action, or {@code null}
* @throws DataAccessException in case of Hibernate errors * @throws DataAccessException in case of Hibernate errors
*/ */
@SuppressWarnings("deprecation")
@Nullable @Nullable
protected <T> T doExecute(HibernateCallback<T> action, boolean enforceNativeSession) throws DataAccessException { protected <T> T doExecute(HibernateCallback<T> action, boolean enforceNativeSession) throws DataAccessException {
Assert.notNull(action, "Callback object must not be null"); Assert.notNull(action, "Callback object must not be null");
@ -374,7 +356,7 @@ public class HibernateTemplate implements HibernateOperations, InitializingBean
} }
if (session == null) { if (session == null) {
session = obtainSessionFactory().openSession(); session = obtainSessionFactory().openSession();
session.setFlushMode(FlushMode.MANUAL); session.setHibernateFlushMode(FlushMode.MANUAL);
isNew = true; isNew = true;
} }
@ -543,7 +525,6 @@ public class HibernateTemplate implements HibernateOperations, InitializingBean
} }
@Override @Override
@SuppressWarnings({"deprecation"})
public void load(Object entity, Serializable id) throws DataAccessException { public void load(Object entity, Serializable id) throws DataAccessException {
executeWithNativeSession(session -> { executeWithNativeSession(session -> {
session.load(entity, id); session.load(entity, id);
@ -887,11 +868,9 @@ public class HibernateTemplate implements HibernateOperations, InitializingBean
@Deprecated @Deprecated
@Override @Override
@SuppressWarnings({"rawtypes", "unchecked", "deprecation"})
public List<?> find(String queryString, @Nullable Object... values) throws DataAccessException { public List<?> find(String queryString, @Nullable Object... values) throws DataAccessException {
return nonNull(executeWithNativeSession((HibernateCallback<List<?>>) session -> { return nonNull(executeWithNativeSession((HibernateCallback<List<?>>) session -> {
org.hibernate.Query queryObject = queryObject( Query<?> queryObject = session.createQuery(queryString);
ReflectionUtils.invokeMethod(createQueryMethod, session, queryString));
prepareQuery(queryObject); prepareQuery(queryObject);
if (values != null) { if (values != null) {
for (int i = 0; i < values.length; i++) { for (int i = 0; i < values.length; i++) {
@ -912,7 +891,6 @@ public class HibernateTemplate implements HibernateOperations, InitializingBean
@Deprecated @Deprecated
@Override @Override
@SuppressWarnings({"rawtypes", "unchecked", "deprecation"})
public List<?> findByNamedParam(String queryString, String[] paramNames, Object[] values) public List<?> findByNamedParam(String queryString, String[] paramNames, Object[] values)
throws DataAccessException { throws DataAccessException {
@ -920,8 +898,7 @@ public class HibernateTemplate implements HibernateOperations, InitializingBean
throw new IllegalArgumentException("Length of paramNames array must match length of values array"); throw new IllegalArgumentException("Length of paramNames array must match length of values array");
} }
return nonNull(executeWithNativeSession((HibernateCallback<List<?>>) session -> { return nonNull(executeWithNativeSession((HibernateCallback<List<?>>) session -> {
org.hibernate.Query queryObject = queryObject( Query<?> queryObject = session.createQuery(queryString);
ReflectionUtils.invokeMethod(createQueryMethod, session, queryString));
prepareQuery(queryObject); prepareQuery(queryObject);
for (int i = 0; i < values.length; i++) { for (int i = 0; i < values.length; i++) {
applyNamedParameterToQuery(queryObject, paramNames[i], values[i]); applyNamedParameterToQuery(queryObject, paramNames[i], values[i]);
@ -932,12 +909,9 @@ public class HibernateTemplate implements HibernateOperations, InitializingBean
@Deprecated @Deprecated
@Override @Override
@SuppressWarnings({"rawtypes", "unchecked", "deprecation"})
public List<?> findByValueBean(String queryString, Object valueBean) throws DataAccessException { public List<?> findByValueBean(String queryString, Object valueBean) throws DataAccessException {
return nonNull(executeWithNativeSession((HibernateCallback<List<?>>) session -> { return nonNull(executeWithNativeSession((HibernateCallback<List<?>>) session -> {
org.hibernate.Query queryObject = queryObject( Query<?> queryObject = session.createQuery(queryString);
ReflectionUtils.invokeMethod(createQueryMethod, session, queryString));
prepareQuery(queryObject); prepareQuery(queryObject);
queryObject.setProperties(valueBean); queryObject.setProperties(valueBean);
return queryObject.list(); return queryObject.list();
@ -951,11 +925,9 @@ public class HibernateTemplate implements HibernateOperations, InitializingBean
@Deprecated @Deprecated
@Override @Override
@SuppressWarnings({"rawtypes", "unchecked", "deprecation"})
public List<?> findByNamedQuery(String queryName, @Nullable Object... values) throws DataAccessException { public List<?> findByNamedQuery(String queryName, @Nullable Object... values) throws DataAccessException {
return nonNull(executeWithNativeSession((HibernateCallback<List<?>>) session -> { return nonNull(executeWithNativeSession((HibernateCallback<List<?>>) session -> {
org.hibernate.Query queryObject = queryObject( Query<?> queryObject = session.getNamedQuery(queryName);
ReflectionUtils.invokeMethod(getNamedQueryMethod, session, queryName));
prepareQuery(queryObject); prepareQuery(queryObject);
if (values != null) { if (values != null) {
for (int i = 0; i < values.length; i++) { for (int i = 0; i < values.length; i++) {
@ -976,7 +948,6 @@ public class HibernateTemplate implements HibernateOperations, InitializingBean
@Deprecated @Deprecated
@Override @Override
@SuppressWarnings({"rawtypes", "unchecked", "deprecation"})
public List<?> findByNamedQueryAndNamedParam( public List<?> findByNamedQueryAndNamedParam(
String queryName, @Nullable String[] paramNames, @Nullable Object[] values) String queryName, @Nullable String[] paramNames, @Nullable Object[] values)
throws DataAccessException { throws DataAccessException {
@ -985,8 +956,7 @@ public class HibernateTemplate implements HibernateOperations, InitializingBean
throw new IllegalArgumentException("Length of paramNames array must match length of values array"); throw new IllegalArgumentException("Length of paramNames array must match length of values array");
} }
return nonNull(executeWithNativeSession((HibernateCallback<List<?>>) session -> { return nonNull(executeWithNativeSession((HibernateCallback<List<?>>) session -> {
org.hibernate.Query queryObject = (org.hibernate.Query) Query<?> queryObject = session.getNamedQuery(queryName);
nonNull(ReflectionUtils.invokeMethod(getNamedQueryMethod, session, queryName));
prepareQuery(queryObject); prepareQuery(queryObject);
if (values != null) { if (values != null) {
for (int i = 0; i < values.length; i++) { for (int i = 0; i < values.length; i++) {
@ -999,12 +969,9 @@ public class HibernateTemplate implements HibernateOperations, InitializingBean
@Deprecated @Deprecated
@Override @Override
@SuppressWarnings({"rawtypes", "unchecked", "deprecation"})
public List<?> findByNamedQueryAndValueBean(String queryName, Object valueBean) throws DataAccessException { public List<?> findByNamedQueryAndValueBean(String queryName, Object valueBean) throws DataAccessException {
return nonNull(executeWithNativeSession((HibernateCallback<List<?>>) session -> { return nonNull(executeWithNativeSession((HibernateCallback<List<?>>) session -> {
org.hibernate.Query queryObject = queryObject( Query<?> queryObject = session.getNamedQuery(queryName);
ReflectionUtils.invokeMethod(getNamedQueryMethod, session, queryName));
prepareQuery(queryObject); prepareQuery(queryObject);
queryObject.setProperties(valueBean); queryObject.setProperties(valueBean);
return queryObject.list(); return queryObject.list();
@ -1018,11 +985,9 @@ public class HibernateTemplate implements HibernateOperations, InitializingBean
@Deprecated @Deprecated
@Override @Override
@SuppressWarnings({"rawtypes", "deprecation"})
public Iterator<?> iterate(String queryString, @Nullable Object... values) throws DataAccessException { public Iterator<?> iterate(String queryString, @Nullable Object... values) throws DataAccessException {
return nonNull(executeWithNativeSession((HibernateCallback<Iterator<?>>) session -> { return nonNull(executeWithNativeSession((HibernateCallback<Iterator<?>>) session -> {
org.hibernate.Query queryObject = queryObject( Query<?> queryObject = session.createQuery(queryString);
ReflectionUtils.invokeMethod(createQueryMethod, session, queryString));
prepareQuery(queryObject); prepareQuery(queryObject);
if (values != null) { if (values != null) {
for (int i = 0; i < values.length; i++) { for (int i = 0; i < values.length; i++) {
@ -1046,11 +1011,9 @@ public class HibernateTemplate implements HibernateOperations, InitializingBean
@Deprecated @Deprecated
@Override @Override
@SuppressWarnings({"rawtypes", "deprecation"})
public int bulkUpdate(String queryString, @Nullable Object... values) throws DataAccessException { public int bulkUpdate(String queryString, @Nullable Object... values) throws DataAccessException {
Integer result = executeWithNativeSession(session -> { Integer result = executeWithNativeSession(session -> {
org.hibernate.Query queryObject = queryObject( Query<?> queryObject = session.createQuery(queryString);
ReflectionUtils.invokeMethod(createQueryMethod, session, queryString));
prepareQuery(queryObject); prepareQuery(queryObject);
if (values != null) { if (values != null) {
for (int i = 0; i < values.length; i++) { for (int i = 0; i < values.length; i++) {
@ -1079,7 +1042,7 @@ public class HibernateTemplate implements HibernateOperations, InitializingBean
* @see FlushMode#MANUAL * @see FlushMode#MANUAL
*/ */
protected void checkWriteOperationAllowed(Session session) throws InvalidDataAccessApiUsageException { protected void checkWriteOperationAllowed(Session session) throws InvalidDataAccessApiUsageException {
if (isCheckWriteOperations() && SessionFactoryUtils.getFlushMode(session).lessThan(FlushMode.COMMIT)) { if (isCheckWriteOperations() && session.getHibernateFlushMode().lessThan(FlushMode.COMMIT)) {
throw new InvalidDataAccessApiUsageException( throw new InvalidDataAccessApiUsageException(
"Write operations are not allowed in read-only mode (FlushMode.MANUAL): "+ "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."); "Turn your Session into FlushMode.COMMIT/AUTO or remove 'readOnly' marker from transaction definition.");
@ -1121,8 +1084,7 @@ public class HibernateTemplate implements HibernateOperations, InitializingBean
* @see #setCacheQueries * @see #setCacheQueries
* @see #setQueryCacheRegion * @see #setQueryCacheRegion
*/ */
@SuppressWarnings({"rawtypes", "deprecation"}) protected void prepareQuery(Query<?> queryObject) {
protected void prepareQuery(org.hibernate.Query queryObject) {
if (isCacheQueries()) { if (isCacheQueries()) {
queryObject.setCacheable(true); queryObject.setCacheable(true);
if (getQueryCacheRegion() != null) { if (getQueryCacheRegion() != null) {
@ -1150,9 +1112,7 @@ public class HibernateTemplate implements HibernateOperations, InitializingBean
* @param value the value of the parameter * @param value the value of the parameter
* @throws HibernateException if thrown by the Query object * @throws HibernateException if thrown by the Query object
*/ */
@Deprecated 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 { throws HibernateException {
if (value instanceof Collection) { if (value instanceof Collection) {
@ -1166,13 +1126,6 @@ public class HibernateTemplate implements HibernateOperations, InitializingBean
} }
} }
@Deprecated
@SuppressWarnings({"rawtypes", "deprecation"})
private static org.hibernate.Query queryObject(@Nullable Object result) {
Assert.state(result != null, "No Hibernate Query");
return (org.hibernate.Query) result;
}
private static <T> T nonNull(@Nullable T result) { private static <T> T nonNull(@Nullable T result) {
Assert.state(result != null, "No result"); Assert.state(result != null, "No result");
return result; return result;
@ -1193,7 +1146,6 @@ public class HibernateTemplate implements HibernateOperations, InitializingBean
} }
@Override @Override
@SuppressWarnings({"rawtypes", "deprecation"})
@Nullable @Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// Invocation on Session interface coming in... // Invocation on Session interface coming in...
@ -1219,8 +1171,8 @@ public class HibernateTemplate implements HibernateOperations, InitializingBean
if (retVal instanceof Criteria) { if (retVal instanceof Criteria) {
prepareCriteria(((Criteria) retVal)); prepareCriteria(((Criteria) retVal));
} }
else if (retVal instanceof org.hibernate.Query) { else if (retVal instanceof Query) {
prepareQuery(((org.hibernate.Query) retVal)); prepareQuery(((Query<?>) retVal));
} }
return retVal; return retVal;

View File

@ -533,7 +533,7 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana
if (!definition.isReadOnly() && !txObject.isNewSession()) { if (!definition.isReadOnly() && !txObject.isNewSession()) {
// We need AUTO or COMMIT for a non-read-only transaction. // We need AUTO or COMMIT for a non-read-only transaction.
FlushMode flushMode = SessionFactoryUtils.getFlushMode(session); FlushMode flushMode = session.getHibernateFlushMode();
if (FlushMode.MANUAL.equals(flushMode)) { if (FlushMode.MANUAL.equals(flushMode)) {
session.setFlushMode(FlushMode.AUTO); session.setFlushMode(FlushMode.AUTO);
txObject.getSessionHolder().setPreviousFlushMode(flushMode); txObject.getSessionHolder().setPreviousFlushMode(flushMode);
@ -562,10 +562,7 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana
// Register the Hibernate Session's JDBC Connection for the DataSource, if set. // Register the Hibernate Session's JDBC Connection for the DataSource, if set.
if (getDataSource() != null) { if (getDataSource() != null) {
SessionImplementor sessionImpl = (SessionImplementor) session; SessionImplementor sessionImpl = (SessionImplementor) session;
// The following needs to use a lambda expression instead of a method reference ConnectionHolder conHolder = new ConnectionHolder(sessionImpl::connection);
// for compatibility with Hibernate ORM <5.2 where connection() is defined on
// SessionImplementor itself instead of on SharedSessionContractImplementor...
ConnectionHolder conHolder = new ConnectionHolder(() -> sessionImpl.connection());
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) { if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
conHolder.setTimeoutInSeconds(timeout); conHolder.setTimeoutInSeconds(timeout);
} }

View File

@ -58,8 +58,8 @@ import org.springframework.util.ClassUtils;
* way to set up a shared Hibernate SessionFactory in a Spring application context; the * way to set up a shared Hibernate SessionFactory in a Spring application context; the
* SessionFactory can then be passed to data access objects via dependency injection. * SessionFactory can then be passed to data access objects via dependency injection.
* *
* <p>Compatible with Hibernate 5.0/5.1 as well as 5.2/5.3/5.4, as of Spring 5.2. * <p>Compatible with Hibernate 5.2/5.3/5.4, as of Spring 5.3.
* Set up with Hibernate 5.2+, {@code LocalSessionFactoryBean} is an immediate alternative * This Hibernate-specific {@code LocalSessionFactoryBean} can be an immediate alternative
* to {@link org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean} for common * to {@link org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean} for common
* JPA purposes: In particular with Hibernate 5.3/5.4, the Hibernate {@code SessionFactory} * JPA purposes: In particular with Hibernate 5.3/5.4, the Hibernate {@code SessionFactory}
* will natively expose the JPA {@code EntityManagerFactory} interface as well, and * will natively expose the JPA {@code EntityManagerFactory} interface as well, and

View File

@ -47,6 +47,7 @@ import org.hibernate.cfg.Configuration;
import org.hibernate.context.spi.CurrentTenantIdentifierResolver; import org.hibernate.context.spi.CurrentTenantIdentifierResolver;
import org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider; import org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.core.InfrastructureProxy; import org.springframework.core.InfrastructureProxy;
@ -76,8 +77,8 @@ import org.springframework.util.ClassUtils;
* Typically combined with {@link HibernateTransactionManager} for declarative * Typically combined with {@link HibernateTransactionManager} for declarative
* transactions against the {@code SessionFactory} and its JDBC {@code DataSource}. * transactions against the {@code SessionFactory} and its JDBC {@code DataSource}.
* *
* <p>Compatible with Hibernate 5.0/5.1 as well as 5.2/5.3/5.4, as of Spring 5.2. * <p>Compatible with Hibernate 5.2/5.3/5.4, as of Spring 5.3.
* Set up with Hibernate 5.2+, this builder is also a convenient way to set up * This Hibernate-specific factory builder can also be a convenient way to set up
* a JPA {@code EntityManagerFactory} since the Hibernate {@code SessionFactory} * a JPA {@code EntityManagerFactory} since the Hibernate {@code SessionFactory}
* natively exposes the JPA {@code EntityManagerFactory} interface as well now. * natively exposes the JPA {@code EntityManagerFactory} interface as well now.
* *
@ -162,23 +163,8 @@ public class LocalSessionFactoryBuilder extends Configuration {
if (dataSource != null) { if (dataSource != null) {
getProperties().put(AvailableSettings.DATASOURCE, dataSource); getProperties().put(AvailableSettings.DATASOURCE, dataSource);
} }
getProperties().put(AvailableSettings.CONNECTION_HANDLING,
// Hibernate 5.1/5.2: manually enforce connection release mode ON_CLOSE (the former default) PhysicalConnectionHandlingMode.DELAYED_ACQUISITION_AND_HOLD);
try {
// Try Hibernate 5.2
AvailableSettings.class.getField("CONNECTION_HANDLING");
getProperties().put("hibernate.connection.handling_mode", "DELAYED_ACQUISITION_AND_HOLD");
}
catch (NoSuchFieldException ex) {
// Try Hibernate 5.1
try {
AvailableSettings.class.getField("ACQUIRE_CONNECTIONS");
getProperties().put("hibernate.connection.release_mode", "ON_CLOSE");
}
catch (NoSuchFieldException ex2) {
// on Hibernate 5.0.x or lower - no need to change the default there
}
}
getProperties().put(AvailableSettings.CLASSLOADERS, Collections.singleton(resourceLoader.getClassLoader())); getProperties().put(AvailableSettings.CLASSLOADERS, Collections.singleton(resourceLoader.getClassLoader()));
this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader); this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
@ -225,22 +211,8 @@ public class LocalSessionFactoryBuilder extends Configuration {
"Unknown transaction manager type: " + jtaTransactionManager.getClass().getName()); "Unknown transaction manager type: " + jtaTransactionManager.getClass().getName());
} }
// Hibernate 5.1/5.2: manually enforce connection release mode AFTER_STATEMENT (the JTA default) getProperties().put(AvailableSettings.CONNECTION_HANDLING,
try { PhysicalConnectionHandlingMode.DELAYED_ACQUISITION_AND_RELEASE_AFTER_STATEMENT);
// Try Hibernate 5.2
AvailableSettings.class.getField("CONNECTION_HANDLING");
getProperties().put("hibernate.connection.handling_mode", "DELAYED_ACQUISITION_AND_RELEASE_AFTER_STATEMENT");
}
catch (NoSuchFieldException ex) {
// Try Hibernate 5.1
try {
AvailableSettings.class.getField("ACQUIRE_CONNECTIONS");
getProperties().put("hibernate.connection.release_mode", "AFTER_STATEMENT");
}
catch (NoSuchFieldException ex2) {
// on Hibernate 5.0.x or lower - no need to change the default there
}
}
return this; return this;
} }

View File

@ -24,7 +24,6 @@ import javax.sql.DataSource;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.hibernate.FlushMode;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.JDBCException; import org.hibernate.JDBCException;
import org.hibernate.NonUniqueObjectException; import org.hibernate.NonUniqueObjectException;
@ -65,7 +64,6 @@ import org.springframework.dao.InvalidDataAccessResourceUsageException;
import org.springframework.dao.PessimisticLockingFailureException; import org.springframework.dao.PessimisticLockingFailureException;
import org.springframework.jdbc.datasource.DataSourceUtils; import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils; import org.springframework.util.ReflectionUtils;
@ -95,39 +93,6 @@ public abstract class SessionFactoryUtils {
static final Log logger = LogFactory.getLog(SessionFactoryUtils.class); 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(), "Could not find Hibernate getFlushMode method");
}
/**
* 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) {
FlushMode flushMode = (FlushMode) ReflectionUtils.invokeMethod(getFlushMode, session);
Assert.state(flushMode != null, "No FlushMode from Session");
return flushMode;
}
/** /**
* Trigger a flush on the given Hibernate Session, converting regular * Trigger a flush on the given Hibernate Session, converting regular
* {@link HibernateException} instances as well as Hibernate 5.2's * {@link HibernateException} instances as well as Hibernate 5.2's

View File

@ -16,8 +16,6 @@
package org.springframework.orm.hibernate5; package org.springframework.orm.hibernate5;
import javax.persistence.EntityManager;
import org.hibernate.FlushMode; import org.hibernate.FlushMode;
import org.hibernate.Session; import org.hibernate.Session;
import org.hibernate.Transaction; import org.hibernate.Transaction;
@ -40,8 +38,6 @@ import org.springframework.orm.jpa.EntityManagerHolder;
*/ */
public class SessionHolder extends EntityManagerHolder { public class SessionHolder extends EntityManagerHolder {
private final Session session;
@Nullable @Nullable
private Transaction transaction; private Transaction transaction;
@ -49,16 +45,13 @@ public class SessionHolder extends EntityManagerHolder {
private FlushMode previousFlushMode; private FlushMode previousFlushMode;
@SuppressWarnings("cast")
public SessionHolder(Session session) { public SessionHolder(Session session) {
// Check below is always true against Hibernate >= 5.2 but not against 5.0/5.1 at runtime super(session);
super(session instanceof EntityManager ? session : null);
this.session = session;
} }
public Session getSession() { public Session getSession() {
return this.session; return (Session) getEntityManager();
} }
public void setTransaction(@Nullable Transaction transaction) { public void setTransaction(@Nullable Transaction transaction) {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2018 the original author or authors. * Copyright 2002-2020 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -97,7 +97,7 @@ public class SpringSessionContext implements CurrentSessionContext {
sessionHolder.setSynchronizedWithTransaction(true); sessionHolder.setSynchronizedWithTransaction(true);
// Switch to FlushMode.AUTO, as we have to assume a thread-bound Session // Switch to FlushMode.AUTO, as we have to assume a thread-bound Session
// with FlushMode.MANUAL, which needs to allow flushing within the transaction. // with FlushMode.MANUAL, which needs to allow flushing within the transaction.
FlushMode flushMode = SessionFactoryUtils.getFlushMode(session); FlushMode flushMode = session.getHibernateFlushMode();
if (flushMode.equals(FlushMode.MANUAL) && if (flushMode.equals(FlushMode.MANUAL) &&
!TransactionSynchronizationManager.isCurrentTransactionReadOnly()) { !TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
session.setFlushMode(FlushMode.AUTO); session.setFlushMode(FlushMode.AUTO);

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2016 the original author or authors. * Copyright 2002-2020 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -91,7 +91,7 @@ public class SpringSessionSynchronization implements TransactionSynchronization,
Session session = getCurrentSession(); Session session = getCurrentSession();
// Read-write transaction -> flush the Hibernate Session. // Read-write transaction -> flush the Hibernate Session.
// Further check: only flush when not FlushMode.MANUAL. // Further check: only flush when not FlushMode.MANUAL.
if (!FlushMode.MANUAL.equals(SessionFactoryUtils.getFlushMode(session))) { if (!FlushMode.MANUAL.equals(session.getHibernateFlushMode())) {
SessionFactoryUtils.flush(getCurrentSession(), true); SessionFactoryUtils.flush(getCurrentSession(), true);
} }
} }

View File

@ -25,25 +25,28 @@ import javax.persistence.spi.PersistenceProvider;
import javax.persistence.spi.PersistenceUnitInfo; import javax.persistence.spi.PersistenceUnitInfo;
import javax.persistence.spi.PersistenceUnitTransactionType; import javax.persistence.spi.PersistenceUnitTransactionType;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.AvailableSettings;
import org.hibernate.dialect.DB2Dialect; import org.hibernate.dialect.DB2Dialect;
import org.hibernate.dialect.DerbyTenSevenDialect; import org.hibernate.dialect.DerbyTenSevenDialect;
import org.hibernate.dialect.H2Dialect; import org.hibernate.dialect.H2Dialect;
import org.hibernate.dialect.HANAColumnStoreDialect; import org.hibernate.dialect.HANAColumnStoreDialect;
import org.hibernate.dialect.HSQLDialect; import org.hibernate.dialect.HSQLDialect;
import org.hibernate.dialect.InformixDialect; import org.hibernate.dialect.Informix10Dialect;
import org.hibernate.dialect.MySQL5Dialect; import org.hibernate.dialect.MySQL57Dialect;
import org.hibernate.dialect.Oracle12cDialect; import org.hibernate.dialect.Oracle12cDialect;
import org.hibernate.dialect.PostgreSQL95Dialect; import org.hibernate.dialect.PostgreSQL95Dialect;
import org.hibernate.dialect.SQLServer2012Dialect; import org.hibernate.dialect.SQLServer2012Dialect;
import org.hibernate.dialect.SybaseDialect; import org.hibernate.dialect.SybaseDialect;
import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
/** /**
* {@link org.springframework.orm.jpa.JpaVendorAdapter} implementation for Hibernate * {@link org.springframework.orm.jpa.JpaVendorAdapter} implementation for Hibernate
* EntityManager. Developed and tested against Hibernate 5.0, 5.1, 5.2, 5.3, 5.4; * EntityManager. Developed and tested against Hibernate 5.3 and 5.4;
* backwards-compatible with Hibernate 4.3 at runtime on a best-effort basis. * backwards-compatible with Hibernate 5.2 at runtime on a best-effort basis.
* *
* <p>Exposes Hibernate's persistence provider and EntityManager extension interface, * <p>Exposes Hibernate's persistence provider and EntityManager extension interface,
* and adapts {@link AbstractJpaVendorAdapter}'s common configuration settings. * and adapts {@link AbstractJpaVendorAdapter}'s common configuration settings.
@ -77,11 +80,10 @@ public class HibernateJpaVendorAdapter extends AbstractJpaVendorAdapter {
private final Class<? extends EntityManager> entityManagerInterface; private final Class<? extends EntityManager> entityManagerInterface;
@SuppressWarnings("deprecation")
public HibernateJpaVendorAdapter() { public HibernateJpaVendorAdapter() {
this.persistenceProvider = new SpringHibernateJpaPersistenceProvider(); this.persistenceProvider = new SpringHibernateJpaPersistenceProvider();
this.entityManagerFactoryInterface = org.hibernate.jpa.HibernateEntityManagerFactory.class; this.entityManagerFactoryInterface = SessionFactory.class;
this.entityManagerInterface = org.hibernate.jpa.HibernateEntityManager.class; this.entityManagerInterface = Session.class;
} }
@ -100,7 +102,7 @@ public class HibernateJpaVendorAdapter extends AbstractJpaVendorAdapter {
* <p><b>NOTE: For a persistence unit with transaction type JTA e.g. on WebLogic, * <p><b>NOTE: For a persistence unit with transaction type JTA e.g. on WebLogic,
* the connection release mode will never be altered from its provider default, * the connection release mode will never be altered from its provider default,
* i.e. not be forced to {@code DELAYED_ACQUISITION_AND_HOLD} by this flag.</b> * i.e. not be forced to {@code DELAYED_ACQUISITION_AND_HOLD} by this flag.</b>
* Alternatively, set Hibernate 5.2's "hibernate.connection.handling_mode" * Alternatively, set Hibernate's "hibernate.connection.handling_mode"
* property to "DELAYED_ACQUISITION_AND_RELEASE_AFTER_TRANSACTION" or even * property to "DELAYED_ACQUISITION_AND_RELEASE_AFTER_TRANSACTION" or even
* "DELAYED_ACQUISITION_AND_RELEASE_AFTER_STATEMENT" in such a scenario. * "DELAYED_ACQUISITION_AND_RELEASE_AFTER_STATEMENT" in such a scenario.
* @since 4.3.1 * @since 4.3.1
@ -155,22 +157,8 @@ public class HibernateJpaVendorAdapter extends AbstractJpaVendorAdapter {
} }
if (connectionReleaseOnClose) { if (connectionReleaseOnClose) {
// Hibernate 5.1+: manually enforce connection release mode ON_CLOSE (the former default) jpaProperties.put(AvailableSettings.CONNECTION_HANDLING,
try { PhysicalConnectionHandlingMode.DELAYED_ACQUISITION_AND_HOLD);
// Try Hibernate 5.2+
AvailableSettings.class.getField("CONNECTION_HANDLING");
jpaProperties.put("hibernate.connection.handling_mode", "DELAYED_ACQUISITION_AND_HOLD");
}
catch (NoSuchFieldException ex) {
// Try Hibernate 5.1
try {
AvailableSettings.class.getField("ACQUIRE_CONNECTIONS");
jpaProperties.put("hibernate.connection.release_mode", "ON_CLOSE");
}
catch (NoSuchFieldException ex2) {
// on Hibernate 5.0.x or lower - no need to change the default there
}
}
} }
return jpaProperties; return jpaProperties;
@ -189,8 +177,8 @@ public class HibernateJpaVendorAdapter extends AbstractJpaVendorAdapter {
case H2: return H2Dialect.class; case H2: return H2Dialect.class;
case HANA: return HANAColumnStoreDialect.class; case HANA: return HANAColumnStoreDialect.class;
case HSQL: return HSQLDialect.class; case HSQL: return HSQLDialect.class;
case INFORMIX: return InformixDialect.class; case INFORMIX: return Informix10Dialect.class;
case MYSQL: return MySQL5Dialect.class; case MYSQL: return MySQL57Dialect.class;
case ORACLE: return Oracle12cDialect.class; case ORACLE: return Oracle12cDialect.class;
case POSTGRESQL: return PostgreSQL95Dialect.class; case POSTGRESQL: return PostgreSQL95Dialect.class;
case SQL_SERVER: return SQLServer2012Dialect.class; case SQL_SERVER: return SQLServer2012Dialect.class;