Migrate orm.hibernate5 to orm.jpa.hibernate package for Hibernate ORM 7.0

Closes gh-35111
This commit is contained in:
Juergen Hoeller 2025-06-25 18:07:15 +02:00
parent f3f05da39b
commit d47f1a1749
34 changed files with 401 additions and 1648 deletions

View File

@ -1,7 +1,7 @@
[[orm-hibernate]]
= Hibernate
We start with a coverage of https://hibernate.org/[Hibernate 5] in a Spring environment,
We start with a coverage of https://hibernate.org/[Hibernate] in a Spring environment,
using it to demonstrate the approach that Spring takes towards integrating OR mappers.
This section covers many issues in detail and shows different variations of DAO
implementations and transaction demarcation. Most of these patterns can be directly
@ -10,13 +10,12 @@ cover the other ORM technologies and show brief examples.
[NOTE]
====
As of Spring Framework 6.0, Spring requires Hibernate ORM 5.5+ for Spring's
As of Spring Framework 7.0, Spring requires Hibernate ORM 7.0 for Spring's
`HibernateJpaVendorAdapter` as well as for a native Hibernate `SessionFactory` setup.
We recommend Hibernate ORM 5.6 as the last feature branch in that Hibernate generation.
Hibernate ORM 6.x is only supported as a JPA provider (`HibernateJpaVendorAdapter`).
Plain `SessionFactory` setup with the `orm.hibernate5` package is not supported anymore.
We recommend Hibernate ORM 6.1/6.2 with JPA-style setup for new development projects.
The `org.springframework.orm.jpa.hibernate` package supersedes the former `orm.hibernate5`:
now for use with Hibernate ORM 7.0, tightly integrated with `HibernateJpaVendorAdapter`
as well as supporting Hibernate's native `SessionFactory.getCurrentSession()` style.
====
@ -43,7 +42,7 @@ JDBC `DataSource` and a Hibernate `SessionFactory` on top of it:
<property name="password" value=""/>
</bean>
<bean id="mySessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<bean id="mySessionFactory" class="org.springframework.orm.jpa.hibernate.LocalSessionFactoryBean">
<property name="dataSource" ref="myDataSource"/>
<property name="mappingResources">
<list>
@ -271,7 +270,7 @@ processing at runtime. The following example shows how to do so:
<!-- SessionFactory, DataSource, etc. omitted -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate5.HibernateTransactionManager">
class="org.springframework.orm.jpa.hibernate.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
@ -301,7 +300,7 @@ and an example for a business method implementation:
----
<beans>
<bean id="myTxManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<bean id="myTxManager" class="org.springframework.orm.jpa.hibernate.HibernateTransactionManager">
<property name="sessionFactory" ref="mySessionFactory"/>
</bean>

View File

@ -212,7 +212,7 @@ example declares `sessionFactory` and `txManager` beans:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<bean id="sessionFactory" class="org.springframework.orm.jpa.hibernate.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mappingResources">
<list>
@ -226,7 +226,7 @@ example declares `sessionFactory` and `txManager` beans:
</property>
</bean>
<bean id="txManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<bean id="txManager" class="org.springframework.orm.jpa.hibernate.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
----
@ -238,7 +238,7 @@ transaction coordinator and possibly also its connection release mode configurat
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<bean id="sessionFactory" class="org.springframework.orm.jpa.hibernate.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mappingResources">
<list>
@ -262,7 +262,7 @@ for enforcing the same defaults:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<bean id="sessionFactory" class="org.springframework.orm.jpa.hibernate.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mappingResources">
<list>

View File

@ -172,7 +172,7 @@ shows this configuration:
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<bean id="sessionFactory" class="org.springframework.orm.jpa.hibernate.LocalSessionFactoryBean">
<!-- configuration elided for brevity -->
</bean>

View File

@ -1,101 +0,0 @@
/*
* Copyright 2002-present 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate5;
import jakarta.persistence.PersistenceException;
import org.hibernate.HibernateException;
import org.hibernate.JDBCException;
import org.jspecify.annotations.Nullable;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.jdbc.support.SQLExceptionTranslator;
import org.springframework.orm.jpa.EntityManagerFactoryUtils;
/**
* {@link PersistenceExceptionTranslator} capable of translating {@link HibernateException}
* instances to Spring's {@link DataAccessException} hierarchy. As of Spring 4.3.2 and
* Hibernate 5.2, it also converts standard JPA {@link PersistenceException} instances.
*
* <p>Extended by {@link LocalSessionFactoryBean}, so there is no need to declare this
* translator in addition to a {@code LocalSessionFactoryBean}.
*
* <p>When configuring the container with {@code @Configuration} classes, a {@code @Bean}
* of this type must be registered manually.
*
* @author Juergen Hoeller
* @since 4.2
* @see org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor
* @see SessionFactoryUtils#convertHibernateAccessException(HibernateException)
* @see EntityManagerFactoryUtils#convertJpaAccessExceptionIfPossible(RuntimeException)
*/
public class HibernateExceptionTranslator implements PersistenceExceptionTranslator {
private @Nullable SQLExceptionTranslator jdbcExceptionTranslator;
/**
* Set the JDBC exception translator for Hibernate exception translation purposes.
* <p>Applied to any detected {@link java.sql.SQLException} root cause of a Hibernate
* {@link JDBCException}, overriding Hibernate's own {@code SQLException} translation
* (which is based on a Hibernate Dialect for a specific target database).
* @since 5.1
* @see java.sql.SQLException
* @see org.hibernate.JDBCException
* @see org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator
* @see org.springframework.jdbc.support.SQLStateSQLExceptionTranslator
*/
public void setJdbcExceptionTranslator(SQLExceptionTranslator jdbcExceptionTranslator) {
this.jdbcExceptionTranslator = jdbcExceptionTranslator;
}
@Override
public @Nullable DataAccessException translateExceptionIfPossible(RuntimeException ex) {
if (ex instanceof HibernateException hibernateEx) {
return convertHibernateAccessException(hibernateEx);
}
if (ex instanceof PersistenceException) {
if (ex.getCause() instanceof HibernateException hibernateEx) {
return convertHibernateAccessException(hibernateEx);
}
return EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(ex);
}
return null;
}
/**
* Convert the given HibernateException to an appropriate exception from the
* {@code org.springframework.dao} hierarchy.
* <p>Will automatically apply a specified SQLExceptionTranslator to a
* Hibernate JDBCException, otherwise rely on Hibernate's default translation.
* @param ex the HibernateException that occurred
* @return a corresponding DataAccessException
* @see SessionFactoryUtils#convertHibernateAccessException
*/
protected DataAccessException convertHibernateAccessException(HibernateException ex) {
if (this.jdbcExceptionTranslator != null && ex instanceof JDBCException jdbcEx) {
DataAccessException dae = this.jdbcExceptionTranslator.translate(
"Hibernate operation: " + jdbcEx.getMessage(), jdbcEx.getSQL(), jdbcEx.getSQLException());
if (dae != null) {
return dae;
}
}
return SessionFactoryUtils.convertHibernateAccessException(ex);
}
}

View File

@ -1,58 +0,0 @@
/*
* Copyright 2002-present 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate5;
import java.sql.SQLException;
import org.hibernate.JDBCException;
import org.jspecify.annotations.Nullable;
import org.springframework.dao.UncategorizedDataAccessException;
/**
* Hibernate-specific subclass of UncategorizedDataAccessException,
* for JDBC exceptions that Hibernate wrapped.
*
* @author Juergen Hoeller
* @since 4.2
* @see SessionFactoryUtils#convertHibernateAccessException
*/
@SuppressWarnings("serial")
public class HibernateJdbcException extends UncategorizedDataAccessException {
public HibernateJdbcException(JDBCException ex) {
super("JDBC exception on Hibernate data access: SQLException for SQL [" + ex.getSQL() + "]; SQL state [" +
ex.getSQLState() + "]; error code [" + ex.getErrorCode() + "]; " + ex.getMessage(), ex);
}
/**
* Return the underlying SQLException.
*/
@SuppressWarnings("NullAway") // JDBCException instances always have a non null cause
public SQLException getSQLException() {
return ((JDBCException) getCause()).getSQLException();
}
/**
* Return the SQL that led to the problem.
*/
@SuppressWarnings("NullAway") // JDBCException instances always have a non null cause
public @Nullable String getSql() {
return ((JDBCException) getCause()).getSQL();
}
}

View File

@ -1,43 +0,0 @@
/*
* Copyright 2002-present 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate5;
import org.hibernate.UnresolvableObjectException;
import org.hibernate.WrongClassException;
import org.springframework.orm.ObjectRetrievalFailureException;
/**
* Hibernate-specific subclass of ObjectRetrievalFailureException.
* Converts Hibernate's UnresolvableObjectException and WrongClassException.
*
* @author Juergen Hoeller
* @since 4.2
* @see SessionFactoryUtils#convertHibernateAccessException
*/
@SuppressWarnings("serial")
public class HibernateObjectRetrievalFailureException extends ObjectRetrievalFailureException {
public HibernateObjectRetrievalFailureException(UnresolvableObjectException ex) {
super(ex.getEntityName(), ex.getIdentifier(), ex.getMessage(), ex);
}
public HibernateObjectRetrievalFailureException(WrongClassException ex) {
super(ex.getEntityName(), ex.getIdentifier(), ex.getMessage(), ex);
}
}

View File

@ -1,49 +0,0 @@
/*
* Copyright 2002-present 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate5;
import org.hibernate.StaleObjectStateException;
import org.hibernate.StaleStateException;
import org.hibernate.dialect.lock.OptimisticEntityLockException;
import org.springframework.orm.ObjectOptimisticLockingFailureException;
/**
* Hibernate-specific subclass of ObjectOptimisticLockingFailureException.
* Converts Hibernate's StaleObjectStateException, StaleStateException
* and OptimisticEntityLockException.
*
* @author Juergen Hoeller
* @since 4.2
* @see SessionFactoryUtils#convertHibernateAccessException
*/
@SuppressWarnings("serial")
public class HibernateOptimisticLockingFailureException extends ObjectOptimisticLockingFailureException {
public HibernateOptimisticLockingFailureException(StaleObjectStateException ex) {
super(ex.getEntityName(), ex.getIdentifier(), ex.getMessage(), ex);
}
public HibernateOptimisticLockingFailureException(StaleStateException ex) {
super(ex.getMessage(), ex);
}
public HibernateOptimisticLockingFailureException(OptimisticEntityLockException ex) {
super(ex.getMessage(), ex);
}
}

View File

@ -1,47 +0,0 @@
/*
* Copyright 2002-present 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate5;
import org.hibernate.QueryException;
import org.jspecify.annotations.Nullable;
import org.springframework.dao.InvalidDataAccessResourceUsageException;
/**
* Hibernate-specific subclass of InvalidDataAccessResourceUsageException,
* thrown on invalid HQL query syntax.
*
* @author Juergen Hoeller
* @since 4.2
* @see SessionFactoryUtils#convertHibernateAccessException
*/
@SuppressWarnings("serial")
public class HibernateQueryException extends InvalidDataAccessResourceUsageException {
public HibernateQueryException(QueryException ex) {
super(ex.getMessage(), ex);
}
/**
* Return the HQL query string that was invalid.
*/
public @Nullable String getQueryString() {
QueryException cause = (QueryException) getCause();
return (cause != null ? cause.getQueryString() : null);
}
}

View File

@ -1,45 +0,0 @@
/*
* Copyright 2002-present 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate5;
import org.hibernate.HibernateException;
import org.jspecify.annotations.Nullable;
import org.springframework.dao.UncategorizedDataAccessException;
/**
* Hibernate-specific subclass of UncategorizedDataAccessException,
* for Hibernate system errors that do not match any concrete
* {@code org.springframework.dao} exceptions.
*
* @author Juergen Hoeller
* @since 4.2
* @see SessionFactoryUtils#convertHibernateAccessException
*/
@SuppressWarnings("serial")
public class HibernateSystemException extends UncategorizedDataAccessException {
/**
* Create a new HibernateSystemException,
* wrapping an arbitrary HibernateException.
* @param cause the HibernateException thrown
*/
public HibernateSystemException(@Nullable HibernateException cause) {
super(cause != null ? cause.getMessage() : null, cause);
}
}

View File

@ -1,256 +0,0 @@
/*
* Copyright 2002-present 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate5;
import java.util.Map;
import javax.sql.DataSource;
import jakarta.persistence.PersistenceException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.HibernateException;
import org.hibernate.JDBCException;
import org.hibernate.NonUniqueObjectException;
import org.hibernate.NonUniqueResultException;
import org.hibernate.ObjectDeletedException;
import org.hibernate.PersistentObjectException;
import org.hibernate.PessimisticLockException;
import org.hibernate.PropertyValueException;
import org.hibernate.QueryException;
import org.hibernate.QueryTimeoutException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.StaleObjectStateException;
import org.hibernate.StaleStateException;
import org.hibernate.TransientObjectException;
import org.hibernate.UnresolvableObjectException;
import org.hibernate.WrongClassException;
import org.hibernate.cfg.Environment;
import org.hibernate.dialect.lock.OptimisticEntityLockException;
import org.hibernate.dialect.lock.PessimisticEntityLockException;
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.exception.ConstraintViolationException;
import org.hibernate.exception.DataException;
import org.hibernate.exception.JDBCConnectionException;
import org.hibernate.exception.LockAcquisitionException;
import org.hibernate.exception.SQLGrammarException;
import org.hibernate.service.UnknownServiceException;
import org.jspecify.annotations.Nullable;
import org.springframework.dao.CannotAcquireLockException;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.dao.InvalidDataAccessResourceUsageException;
import org.springframework.dao.PessimisticLockingFailureException;
import org.springframework.jdbc.datasource.DataSourceUtils;
/**
* Helper class featuring methods for Hibernate Session handling.
* Also provides support for exception translation.
*
* <p>Used internally by {@link HibernateTransactionManager}.
* Can also be used directly in application code.
*
* @author Juergen Hoeller
* @since 4.2
* @see HibernateExceptionTranslator
* @see HibernateTransactionManager
*/
public abstract class SessionFactoryUtils {
/**
* Order value for TransactionSynchronization objects that clean up Hibernate Sessions.
* Returns {@code DataSourceUtils.CONNECTION_SYNCHRONIZATION_ORDER - 100}
* to execute Session cleanup before JDBC Connection cleanup, if any.
* @see DataSourceUtils#CONNECTION_SYNCHRONIZATION_ORDER
*/
public static final int SESSION_SYNCHRONIZATION_ORDER =
DataSourceUtils.CONNECTION_SYNCHRONIZATION_ORDER - 100;
static final Log logger = LogFactory.getLog(SessionFactoryUtils.class);
/**
* Trigger a flush on the given Hibernate Session, converting regular
* {@link HibernateException} instances as well as Hibernate 5.2's
* {@link PersistenceException} wrappers accordingly.
* @param session the Hibernate Session to flush
* @param synch whether this flush is triggered by transaction synchronization
* @throws DataAccessException in case of flush failures
* @since 4.3.2
*/
static void flush(Session session, boolean synch) throws DataAccessException {
if (synch) {
logger.debug("Flushing Hibernate Session on transaction synchronization");
}
else {
logger.debug("Flushing Hibernate Session on explicit request");
}
try {
session.flush();
}
catch (HibernateException ex) {
throw convertHibernateAccessException(ex);
}
catch (PersistenceException ex) {
if (ex.getCause() instanceof HibernateException hibernateException) {
throw convertHibernateAccessException(hibernateException);
}
throw ex;
}
}
/**
* 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(@Nullable Session session) {
if (session != null) {
try {
if (session.isOpen()) {
session.close();
}
}
catch (Throwable ex) {
logger.error("Failed to release Hibernate Session", ex);
}
}
}
/**
* Determine the DataSource of the given SessionFactory.
* @param sessionFactory the SessionFactory to check
* @return the DataSource, or {@code null} if none found
* @see ConnectionProvider
*/
public static @Nullable DataSource getDataSource(SessionFactory sessionFactory) {
Map<String, Object> props = sessionFactory.getProperties();
if (props != null) {
Object dataSourceValue = props.get(Environment.JAKARTA_NON_JTA_DATASOURCE);
if (dataSourceValue instanceof DataSource dataSource) {
return dataSource;
}
}
if (sessionFactory instanceof SessionFactoryImplementor sfi) {
try {
ConnectionProvider cp = sfi.getServiceRegistry().getService(ConnectionProvider.class);
if (cp != null) {
return cp.unwrap(DataSource.class);
}
}
catch (UnknownServiceException ex) {
if (logger.isDebugEnabled()) {
logger.debug("No ConnectionProvider found - cannot determine DataSource for SessionFactory: " + ex);
}
}
}
return null;
}
/**
* Convert the given HibernateException to an appropriate exception
* from the {@code org.springframework.dao} hierarchy.
* @param ex the HibernateException that occurred
* @return the corresponding DataAccessException instance
* @see HibernateExceptionTranslator#convertHibernateAccessException
* @see HibernateTransactionManager#convertHibernateAccessException
*/
public static DataAccessException convertHibernateAccessException(HibernateException ex) {
if (ex instanceof JDBCConnectionException) {
return new DataAccessResourceFailureException(ex.getMessage(), ex);
}
if (ex instanceof SQLGrammarException hibJdbcEx) {
return new InvalidDataAccessResourceUsageException(ex.getMessage() + "; SQL [" + hibJdbcEx.getSQL() + "]", ex);
}
if (ex instanceof QueryTimeoutException hibJdbcEx) {
return new org.springframework.dao.QueryTimeoutException(ex.getMessage() + "; SQL [" + hibJdbcEx.getSQL() + "]", ex);
}
if (ex instanceof LockAcquisitionException hibJdbcEx) {
return new CannotAcquireLockException(ex.getMessage() + "; SQL [" + hibJdbcEx.getSQL() + "]", ex);
}
if (ex instanceof PessimisticLockException hibJdbcEx) {
return new PessimisticLockingFailureException(ex.getMessage() + "; SQL [" + hibJdbcEx.getSQL() + "]", ex);
}
if (ex instanceof ConstraintViolationException hibJdbcEx) {
return new DataIntegrityViolationException(ex.getMessage() + "; SQL [" + hibJdbcEx.getSQL() +
"]; constraint [" + hibJdbcEx.getConstraintName() + "]", ex);
}
if (ex instanceof DataException hibJdbcEx) {
return new DataIntegrityViolationException(ex.getMessage() + "; SQL [" + hibJdbcEx.getSQL() + "]", ex);
}
if (ex instanceof JDBCException hibJdbcEx) {
return new HibernateJdbcException(hibJdbcEx);
}
// end of JDBCException (subclass) handling
if (ex instanceof QueryException queryException) {
return new HibernateQueryException(queryException);
}
if (ex instanceof NonUniqueResultException) {
return new IncorrectResultSizeDataAccessException(ex.getMessage(), 1, ex);
}
if (ex instanceof NonUniqueObjectException) {
return new DuplicateKeyException(ex.getMessage(), ex);
}
if (ex instanceof PropertyValueException) {
return new DataIntegrityViolationException(ex.getMessage(), ex);
}
if (ex instanceof PersistentObjectException) {
return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
}
if (ex instanceof TransientObjectException) {
return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
}
if (ex instanceof ObjectDeletedException) {
return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
}
if (ex instanceof UnresolvableObjectException unresolvableObjectException) {
return new HibernateObjectRetrievalFailureException(unresolvableObjectException);
}
if (ex instanceof WrongClassException wrongClassException) {
return new HibernateObjectRetrievalFailureException(wrongClassException);
}
if (ex instanceof StaleObjectStateException staleObjectStateException) {
return new HibernateOptimisticLockingFailureException(staleObjectStateException);
}
if (ex instanceof StaleStateException staleStateException) {
return new HibernateOptimisticLockingFailureException(staleStateException);
}
if (ex instanceof OptimisticEntityLockException optimisticEntityLockException) {
return new HibernateOptimisticLockingFailureException(optimisticEntityLockException);
}
if (ex instanceof PessimisticEntityLockException) {
if (ex.getCause() instanceof LockAcquisitionException) {
return new CannotAcquireLockException(ex.getMessage(), ex.getCause());
}
return new PessimisticLockingFailureException(ex.getMessage(), ex);
}
// fallback
return new HibernateSystemException(ex);
}
}

View File

@ -1,16 +0,0 @@
/**
* Package providing integration of
* <a href="https://hibernate.org/">Hibernate 5.x</a>
* with Spring concepts.
*
* <p>Contains an implementation of Spring's transaction SPI for local Hibernate transactions.
* This package is intentionally rather minimal, with no template classes or the like,
* in order to follow Hibernate recommendations as closely as possible. We recommend
* using Hibernate's native <code>sessionFactory.getCurrentSession()</code> style.
*
* <p><b>This package supports Hibernate 5.x only.</b>
*/
@NullMarked
package org.springframework.orm.hibernate5;
import org.jspecify.annotations.NullMarked;

View File

@ -1,124 +0,0 @@
/*
* Copyright 2002-present 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate5.support;
import java.util.concurrent.Callable;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.SessionFactory;
import org.jspecify.annotations.Nullable;
import org.springframework.orm.hibernate5.SessionFactoryUtils;
import org.springframework.orm.hibernate5.SessionHolder;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.async.CallableProcessingInterceptor;
import org.springframework.web.context.request.async.DeferredResult;
import org.springframework.web.context.request.async.DeferredResultProcessingInterceptor;
/**
* An interceptor with asynchronous web requests used in OpenSessionInViewFilter and
* OpenSessionInViewInterceptor.
*
* Ensures the following:
* 1) The session is bound/unbound when "callable processing" is started
* 2) The session is closed if an async request times out or an error occurred
*
* @author Rossen Stoyanchev
* @since 4.2
*/
class AsyncRequestInterceptor implements CallableProcessingInterceptor, DeferredResultProcessingInterceptor {
private static final Log logger = LogFactory.getLog(AsyncRequestInterceptor.class);
private final SessionFactory sessionFactory;
private final SessionHolder sessionHolder;
private volatile boolean timeoutInProgress;
private volatile boolean errorInProgress;
public AsyncRequestInterceptor(SessionFactory sessionFactory, SessionHolder sessionHolder) {
this.sessionFactory = sessionFactory;
this.sessionHolder = sessionHolder;
}
@Override
public <T> void preProcess(NativeWebRequest request, Callable<T> task) {
bindSession();
}
public void bindSession() {
this.timeoutInProgress = false;
this.errorInProgress = false;
TransactionSynchronizationManager.bindResource(this.sessionFactory, this.sessionHolder);
}
@Override
public <T> void postProcess(NativeWebRequest request, Callable<T> task, @Nullable Object concurrentResult) {
TransactionSynchronizationManager.unbindResource(this.sessionFactory);
}
@Override
public <T> Object handleTimeout(NativeWebRequest request, Callable<T> task) {
this.timeoutInProgress = true;
return RESULT_NONE; // give other interceptors a chance to handle the timeout
}
@Override
public <T> Object handleError(NativeWebRequest request, Callable<T> task, Throwable t) {
this.errorInProgress = true;
return RESULT_NONE; // give other interceptors a chance to handle the error
}
@Override
public <T> void afterCompletion(NativeWebRequest request, Callable<T> task) throws Exception {
closeSession();
}
private void closeSession() {
if (this.timeoutInProgress || this.errorInProgress) {
logger.debug("Closing Hibernate Session after async request timeout/error");
SessionFactoryUtils.closeSession(this.sessionHolder.getSession());
}
}
// Implementation of DeferredResultProcessingInterceptor methods
@Override
public <T> boolean handleTimeout(NativeWebRequest request, DeferredResult<T> deferredResult) {
this.timeoutInProgress = true;
return true; // give other interceptors a chance to handle the timeout
}
@Override
public <T> boolean handleError(NativeWebRequest request, DeferredResult<T> deferredResult, Throwable t) {
this.errorInProgress = true;
return true; // give other interceptors a chance to handle the error
}
@Override
public <T> void afterCompletion(NativeWebRequest request, DeferredResult<T> deferredResult) {
closeSession();
}
}

View File

@ -1,226 +0,0 @@
/*
* Copyright 2002-present 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate5.support;
import java.io.IOException;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.hibernate.FlushMode;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.orm.hibernate5.SessionFactoryUtils;
import org.springframework.orm.hibernate5.SessionHolder;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.async.CallableProcessingInterceptor;
import org.springframework.web.context.request.async.WebAsyncManager;
import org.springframework.web.context.request.async.WebAsyncUtils;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.filter.OncePerRequestFilter;
/**
* Servlet Filter that binds a Hibernate Session to the thread for the entire
* processing of the request. Intended for the "Open Session in View" pattern,
* i.e. to allow for lazy loading in web views despite the original transactions
* already being completed.
*
* <p>This filter makes Hibernate Sessions available via the current thread, which
* will be autodetected by transaction managers. It is suitable for service layer
* transactions via {@link org.springframework.orm.hibernate5.HibernateTransactionManager}
* as well as for non-transactional execution (if configured appropriately).
*
* <p><b>NOTE</b>: This filter will by default <i>not</i> flush the Hibernate Session,
* with the flush mode set to {@code FlushMode.MANUAL}. It assumes to be used
* in combination with service layer transactions that care for the flushing: The
* active transaction manager will temporarily change the flush mode to
* {@code FlushMode.AUTO} during a read-write transaction, with the flush
* mode reset to {@code FlushMode.MANUAL} at the end of each transaction.
*
* <p><b>WARNING:</b> Applying this filter to existing logic can cause issues that
* have not appeared before, through the use of a single Hibernate Session for the
* processing of an entire request. In particular, the reassociation of persistent
* objects with a Hibernate Session has to occur at the very beginning of request
* processing, to avoid clashes with already loaded instances of the same objects.
*
* <p>Looks up the SessionFactory in Spring's root web application context.
* Supports a "sessionFactoryBeanName" filter init-param in {@code web.xml};
* the default bean name is "sessionFactory".
*
* @author Juergen Hoeller
* @since 4.2
* @see #lookupSessionFactory
* @see OpenSessionInViewInterceptor
* @see OpenSessionInterceptor
* @see org.springframework.orm.hibernate5.HibernateTransactionManager
* @see TransactionSynchronizationManager
* @see SessionFactory#getCurrentSession()
*/
public class OpenSessionInViewFilter extends OncePerRequestFilter {
/**
* The default bean name used for the session factory.
*/
public static final String DEFAULT_SESSION_FACTORY_BEAN_NAME = "sessionFactory";
private String sessionFactoryBeanName = DEFAULT_SESSION_FACTORY_BEAN_NAME;
/**
* Set the bean name of the SessionFactory to fetch from Spring's
* root application context. Default is "sessionFactory".
* @see #DEFAULT_SESSION_FACTORY_BEAN_NAME
*/
public void setSessionFactoryBeanName(String sessionFactoryBeanName) {
this.sessionFactoryBeanName = sessionFactoryBeanName;
}
/**
* Return the bean name of the SessionFactory to fetch from Spring's
* root application context.
*/
protected String getSessionFactoryBeanName() {
return this.sessionFactoryBeanName;
}
/**
* Returns "false" so that the filter may re-bind the opened Hibernate
* {@code Session} to each asynchronously dispatched thread and postpone
* closing it until the very last asynchronous dispatch.
*/
@Override
protected boolean shouldNotFilterAsyncDispatch() {
return false;
}
/**
* Returns "false" so that the filter may provide a Hibernate
* {@code Session} to each error dispatches.
*/
@Override
protected boolean shouldNotFilterErrorDispatch() {
return false;
}
@Override
protected void doFilterInternal(
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
SessionFactory sessionFactory = lookupSessionFactory(request);
boolean participate = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
String key = getAlreadyFilteredAttributeName();
if (TransactionSynchronizationManager.hasResource(sessionFactory)) {
// Do not modify the Session: just set the participate flag.
participate = true;
}
else {
boolean isFirstRequest = !isAsyncDispatch(request);
if (isFirstRequest || !applySessionBindingInterceptor(asyncManager, key)) {
logger.debug("Opening Hibernate Session in OpenSessionInViewFilter");
Session session = openSession(sessionFactory);
SessionHolder sessionHolder = new SessionHolder(session);
TransactionSynchronizationManager.bindResource(sessionFactory, sessionHolder);
AsyncRequestInterceptor interceptor = new AsyncRequestInterceptor(sessionFactory, sessionHolder);
asyncManager.registerCallableInterceptor(key, interceptor);
asyncManager.registerDeferredResultInterceptor(key, interceptor);
}
}
try {
filterChain.doFilter(request, response);
}
finally {
if (!participate) {
SessionHolder sessionHolder =
(SessionHolder) TransactionSynchronizationManager.unbindResource(sessionFactory);
if (!isAsyncStarted(request)) {
logger.debug("Closing Hibernate Session in OpenSessionInViewFilter");
SessionFactoryUtils.closeSession(sessionHolder.getSession());
}
}
}
}
/**
* Look up the SessionFactory that this filter should use,
* taking the current HTTP request as argument.
* <p>The default implementation delegates to the {@link #lookupSessionFactory()}
* variant without arguments.
* @param request the current request
* @return the SessionFactory to use
*/
protected SessionFactory lookupSessionFactory(HttpServletRequest request) {
return lookupSessionFactory();
}
/**
* Look up the SessionFactory that this filter should use.
* <p>The default implementation looks for a bean with the specified name
* in Spring's root application context.
* @return the SessionFactory to use
* @see #getSessionFactoryBeanName
*/
protected SessionFactory lookupSessionFactory() {
if (logger.isDebugEnabled()) {
logger.debug("Using SessionFactory '" + getSessionFactoryBeanName() + "' for OpenSessionInViewFilter");
}
WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
return wac.getBean(getSessionFactoryBeanName(), SessionFactory.class);
}
/**
* Open a Session for the SessionFactory that this filter uses.
* <p>The default implementation delegates to the {@link SessionFactory#openSession}
* method and sets the {@link Session}'s flush mode to "MANUAL".
* @param sessionFactory the SessionFactory that this filter uses
* @return the Session to use
* @throws DataAccessResourceFailureException if the Session could not be created
* @see FlushMode#MANUAL
*/
protected Session openSession(SessionFactory sessionFactory) throws DataAccessResourceFailureException {
try {
Session session = sessionFactory.openSession();
session.setHibernateFlushMode(FlushMode.MANUAL);
return session;
}
catch (HibernateException ex) {
throw new DataAccessResourceFailureException("Could not open Hibernate Session", ex);
}
}
private boolean applySessionBindingInterceptor(WebAsyncManager asyncManager, String key) {
CallableProcessingInterceptor cpi = asyncManager.getCallableInterceptor(key);
if (cpi == null) {
return false;
}
((AsyncRequestInterceptor) cpi).bindSession();
return true;
}
}

View File

@ -1,217 +0,0 @@
/*
* Copyright 2002-present 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate5.support;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.FlushMode;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.jspecify.annotations.Nullable;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.orm.hibernate5.SessionFactoryUtils;
import org.springframework.orm.hibernate5.SessionHolder;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.ui.ModelMap;
import org.springframework.util.Assert;
import org.springframework.web.context.request.AsyncWebRequestInterceptor;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.context.request.async.CallableProcessingInterceptor;
import org.springframework.web.context.request.async.WebAsyncManager;
import org.springframework.web.context.request.async.WebAsyncUtils;
/**
* Spring web request interceptor that binds a Hibernate {@code Session} to the
* thread for the entire processing of the request.
*
* <p>This class is a concrete expression of the "Open Session in View" pattern, which
* is a pattern that allows for the lazy loading of associations in web views despite
* the original transactions already being completed.
*
* <p>This interceptor makes Hibernate Sessions available via the current thread,
* which will be autodetected by transaction managers. It is suitable for service layer
* transactions via {@link org.springframework.orm.hibernate5.HibernateTransactionManager}
* as well as for non-transactional execution (if configured appropriately).
*
* <p>In contrast to {@link OpenSessionInViewFilter}, this interceptor is configured
* in a Spring application context and can thus take advantage of bean wiring.
*
* <p><b>WARNING:</b> Applying this interceptor to existing logic can cause issues
* that have not appeared before, through the use of a single Hibernate
* {@code Session} for the processing of an entire request. In particular, the
* reassociation of persistent objects with a Hibernate {@code Session} has to
* occur at the very beginning of request processing, to avoid clashes with already
* loaded instances of the same objects.
*
* @author Juergen Hoeller
* @since 4.2
* @see OpenSessionInViewFilter
* @see OpenSessionInterceptor
* @see org.springframework.orm.hibernate5.HibernateTransactionManager
* @see TransactionSynchronizationManager
* @see SessionFactory#getCurrentSession()
*/
public class OpenSessionInViewInterceptor implements AsyncWebRequestInterceptor {
/**
* Suffix that gets appended to the {@code SessionFactory}
* {@code toString()} representation for the "participate in existing
* session handling" request attribute.
* @see #getParticipateAttributeName
*/
public static final String PARTICIPATE_SUFFIX = ".PARTICIPATE";
protected final Log logger = LogFactory.getLog(getClass());
private @Nullable SessionFactory sessionFactory;
/**
* Set the Hibernate SessionFactory that should be used to create Hibernate Sessions.
*/
public void setSessionFactory(@Nullable SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
/**
* Return the Hibernate SessionFactory that should be used to create Hibernate Sessions.
*/
public @Nullable SessionFactory getSessionFactory() {
return this.sessionFactory;
}
private SessionFactory obtainSessionFactory() {
SessionFactory sf = getSessionFactory();
Assert.state(sf != null, "No SessionFactory set");
return sf;
}
/**
* Open a new Hibernate {@code Session} according and bind it to the thread via the
* {@link TransactionSynchronizationManager}.
*/
@Override
public void preHandle(WebRequest request) throws DataAccessException {
String key = getParticipateAttributeName();
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
if (asyncManager.hasConcurrentResult() && applySessionBindingInterceptor(asyncManager, key)) {
return;
}
if (TransactionSynchronizationManager.hasResource(obtainSessionFactory())) {
// Do not modify the Session: just mark the request accordingly.
Integer count = (Integer) request.getAttribute(key, WebRequest.SCOPE_REQUEST);
int newCount = (count != null ? count + 1 : 1);
request.setAttribute(getParticipateAttributeName(), newCount, WebRequest.SCOPE_REQUEST);
}
else {
logger.debug("Opening Hibernate Session in OpenSessionInViewInterceptor");
Session session = openSession();
SessionHolder sessionHolder = new SessionHolder(session);
TransactionSynchronizationManager.bindResource(obtainSessionFactory(), sessionHolder);
AsyncRequestInterceptor asyncRequestInterceptor =
new AsyncRequestInterceptor(obtainSessionFactory(), sessionHolder);
asyncManager.registerCallableInterceptor(key, asyncRequestInterceptor);
asyncManager.registerDeferredResultInterceptor(key, asyncRequestInterceptor);
}
}
@Override
public void postHandle(WebRequest request, @Nullable ModelMap model) {
}
/**
* Unbind the Hibernate {@code Session} from the thread and close it.
* @see TransactionSynchronizationManager
*/
@Override
public void afterCompletion(WebRequest request, @Nullable Exception ex) throws DataAccessException {
if (!decrementParticipateCount(request)) {
SessionHolder sessionHolder =
(SessionHolder) TransactionSynchronizationManager.unbindResource(obtainSessionFactory());
logger.debug("Closing Hibernate Session in OpenSessionInViewInterceptor");
SessionFactoryUtils.closeSession(sessionHolder.getSession());
}
}
private boolean decrementParticipateCount(WebRequest request) {
String participateAttributeName = getParticipateAttributeName();
Integer count = (Integer) request.getAttribute(participateAttributeName, WebRequest.SCOPE_REQUEST);
if (count == null) {
return false;
}
// Do not modify the Session: just clear the marker.
if (count > 1) {
request.setAttribute(participateAttributeName, count - 1, WebRequest.SCOPE_REQUEST);
}
else {
request.removeAttribute(participateAttributeName, WebRequest.SCOPE_REQUEST);
}
return true;
}
@Override
public void afterConcurrentHandlingStarted(WebRequest request) {
if (!decrementParticipateCount(request)) {
TransactionSynchronizationManager.unbindResource(obtainSessionFactory());
}
}
/**
* Open a Session for the SessionFactory that this interceptor uses.
* <p>The default implementation delegates to the {@link SessionFactory#openSession}
* method and sets the {@link Session}'s flush mode to "MANUAL".
* @return the Session to use
* @throws DataAccessResourceFailureException if the Session could not be created
* @see FlushMode#MANUAL
*/
protected Session openSession() throws DataAccessResourceFailureException {
try {
Session session = obtainSessionFactory().openSession();
session.setHibernateFlushMode(FlushMode.MANUAL);
return session;
}
catch (HibernateException ex) {
throw new DataAccessResourceFailureException("Could not open Hibernate Session", ex);
}
}
/**
* Return the name of the request attribute that identifies that a request is
* already intercepted.
* <p>The default implementation takes the {@code toString()} representation
* of the {@code SessionFactory} instance and appends {@link #PARTICIPATE_SUFFIX}.
*/
protected String getParticipateAttributeName() {
return obtainSessionFactory().toString() + PARTICIPATE_SUFFIX;
}
private boolean applySessionBindingInterceptor(WebAsyncManager asyncManager, String key) {
CallableProcessingInterceptor cpi = asyncManager.getCallableInterceptor(key);
if (cpi == null) {
return false;
}
((AsyncRequestInterceptor) cpi).bindSession();
return true;
}
}

View File

@ -1,122 +0,0 @@
/*
* Copyright 2002-present 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate5.support;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.hibernate.FlushMode;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.jspecify.annotations.Nullable;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.orm.hibernate5.SessionFactoryUtils;
import org.springframework.orm.hibernate5.SessionHolder;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.Assert;
/**
* Simple AOP Alliance {@link MethodInterceptor} implementation that binds a new
* Hibernate {@link Session} for each method invocation, if none bound before.
*
* <p>This is a simple Hibernate Session scoping interceptor along the lines of
* {@link OpenSessionInViewInterceptor}, just for use with AOP setup instead of
* MVC setup. It opens a new {@link Session} with flush mode "MANUAL" since the
* Session is only meant for reading, except when participating in a transaction.
*
* @author Juergen Hoeller
* @since 4.2
* @see OpenSessionInViewInterceptor
* @see OpenSessionInViewFilter
* @see org.springframework.orm.hibernate5.HibernateTransactionManager
* @see TransactionSynchronizationManager
* @see SessionFactory#getCurrentSession()
*/
public class OpenSessionInterceptor implements MethodInterceptor, InitializingBean {
private @Nullable SessionFactory sessionFactory;
/**
* Set the Hibernate SessionFactory that should be used to create Hibernate Sessions.
*/
public void setSessionFactory(@Nullable SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
/**
* Return the Hibernate SessionFactory that should be used to create Hibernate Sessions.
*/
public @Nullable SessionFactory getSessionFactory() {
return this.sessionFactory;
}
@Override
public void afterPropertiesSet() {
if (getSessionFactory() == null) {
throw new IllegalArgumentException("Property 'sessionFactory' is required");
}
}
@Override
public @Nullable Object invoke(MethodInvocation invocation) throws Throwable {
SessionFactory sf = getSessionFactory();
Assert.state(sf != null, "No SessionFactory set");
if (!TransactionSynchronizationManager.hasResource(sf)) {
// New Session to be bound for the current method's scope...
Session session = openSession(sf);
try {
TransactionSynchronizationManager.bindResource(sf, new SessionHolder(session));
return invocation.proceed();
}
finally {
SessionFactoryUtils.closeSession(session);
TransactionSynchronizationManager.unbindResource(sf);
}
}
else {
// Pre-bound Session found -> simply proceed.
return invocation.proceed();
}
}
/**
* Open a Session for the given SessionFactory.
* <p>The default implementation delegates to the {@link SessionFactory#openSession}
* method and sets the {@link Session}'s flush mode to "MANUAL".
* @param sessionFactory the SessionFactory to use
* @return the Session to use
* @throws DataAccessResourceFailureException if the Session could not be created
* @since 5.0
* @see FlushMode#MANUAL
*/
protected Session openSession(SessionFactory sessionFactory) throws DataAccessResourceFailureException {
try {
Session session = sessionFactory.openSession();
session.setHibernateFlushMode(FlushMode.MANUAL);
return session;
}
catch (HibernateException ex) {
throw new DataAccessResourceFailureException("Could not open Hibernate Session", ex);
}
}
}

View File

@ -1,7 +0,0 @@
/**
* Classes supporting the {@code org.springframework.orm.hibernate5} package.
*/
@NullMarked
package org.springframework.orm.hibernate5.support;
import org.jspecify.annotations.NullMarked;

View File

@ -28,9 +28,6 @@ import org.springframework.util.Assert;
* {@link JpaTransactionManager} binds instances of this class to the thread,
* for a given {@link jakarta.persistence.EntityManagerFactory}.
*
* <p>Also serves as a base class for {@link org.springframework.orm.hibernate5.SessionHolder},
* as of 5.1.
*
* <p>Note: This is an SPI class, not intended to be used by applications.
*
* @author Juergen Hoeller

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.orm.hibernate5;
package org.springframework.orm.jpa.hibernate;
import jakarta.transaction.Status;
import jakarta.transaction.Synchronization;
@ -35,7 +35,7 @@ import org.springframework.util.Assert;
* {@link UserTransaction} and {@link TransactionSynchronizationRegistry} references.
*
* @author Juergen Hoeller
* @since 4.2
* @since 7.0
*/
@SuppressWarnings("serial")
class ConfigurableJtaPlatform implements JtaPlatform {

View File

@ -0,0 +1,227 @@
/*
* Copyright 2002-present 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.jpa.hibernate;
import java.sql.SQLException;
import jakarta.persistence.PersistenceException;
import org.hibernate.HibernateException;
import org.hibernate.JDBCException;
import org.hibernate.NonUniqueObjectException;
import org.hibernate.NonUniqueResultException;
import org.hibernate.ObjectDeletedException;
import org.hibernate.PersistentObjectException;
import org.hibernate.PessimisticLockException;
import org.hibernate.PropertyValueException;
import org.hibernate.QueryException;
import org.hibernate.QueryTimeoutException;
import org.hibernate.StaleObjectStateException;
import org.hibernate.StaleStateException;
import org.hibernate.TransientObjectException;
import org.hibernate.UnresolvableObjectException;
import org.hibernate.WrongClassException;
import org.hibernate.dialect.lock.OptimisticEntityLockException;
import org.hibernate.dialect.lock.PessimisticEntityLockException;
import org.hibernate.exception.ConstraintViolationException;
import org.hibernate.exception.DataException;
import org.hibernate.exception.JDBCConnectionException;
import org.hibernate.exception.LockAcquisitionException;
import org.hibernate.exception.SQLGrammarException;
import org.jspecify.annotations.Nullable;
import org.springframework.dao.CannotAcquireLockException;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.dao.InvalidDataAccessResourceUsageException;
import org.springframework.dao.PessimisticLockingFailureException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.jdbc.support.SQLExceptionSubclassTranslator;
import org.springframework.jdbc.support.SQLExceptionTranslator;
import org.springframework.orm.ObjectOptimisticLockingFailureException;
import org.springframework.orm.ObjectRetrievalFailureException;
import org.springframework.orm.jpa.EntityManagerFactoryUtils;
import org.springframework.orm.jpa.JpaSystemException;
/**
* {@link PersistenceExceptionTranslator} capable of translating {@link HibernateException}
* and standard JPA {@link PersistenceException} instances to Spring'
* {@link DataAccessException} hierarchy.
*
* <p>Extended by {@link LocalSessionFactoryBean}, so there is no need to declare this
* translator in addition to a {@code LocalSessionFactoryBean}.
*
* <p>When configuring the container with {@code @Configuration} classes, a {@code @Bean}
* of this type must be registered manually.
*
* @author Juergen Hoeller
* @since 7.0
* @see org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor
* @see EntityManagerFactoryUtils#convertJpaAccessExceptionIfPossible(RuntimeException)
*/
public class HibernateExceptionTranslator implements PersistenceExceptionTranslator {
private @Nullable SQLExceptionTranslator jdbcExceptionTranslator;
private @Nullable SQLExceptionTranslator transactionExceptionTranslator = new SQLExceptionSubclassTranslator();
/**
* Set the JDBC exception translator for Hibernate exception translation purposes.
* <p>Applied to any detected {@link java.sql.SQLException} root cause of a Hibernate
* {@link JDBCException}, overriding Hibernate's own {@code SQLException} translation
* (which is based on a Hibernate Dialect for a specific target database).
* @see java.sql.SQLException
* @see org.hibernate.JDBCException
* @see org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator
* @see org.springframework.jdbc.support.SQLStateSQLExceptionTranslator
*/
public void setJdbcExceptionTranslator(@Nullable SQLExceptionTranslator exceptionTranslator) {
this.jdbcExceptionTranslator = exceptionTranslator;
this.transactionExceptionTranslator = exceptionTranslator;
}
@Override
public @Nullable DataAccessException translateExceptionIfPossible(RuntimeException ex) {
if (ex instanceof HibernateException hibernateEx) {
return convertHibernateAccessException(hibernateEx);
}
if (ex instanceof PersistenceException) {
if (ex.getCause() instanceof HibernateException hibernateEx) {
return convertHibernateAccessException(hibernateEx);
}
return EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(ex);
}
return null;
}
/**
* Convert the given HibernateException to an appropriate exception from the
* {@code org.springframework.dao} hierarchy.
* <p>Will automatically apply a specified SQLExceptionTranslator to a
* Hibernate JDBCException, otherwise rely on Hibernate's default translation.
* @param ex the HibernateException that occurred
* @return a corresponding DataAccessException
*/
protected DataAccessException convertHibernateAccessException(HibernateException ex) {
if (this.jdbcExceptionTranslator != null && ex instanceof JDBCException jdbcEx) {
DataAccessException dae = this.jdbcExceptionTranslator.translate(
"Hibernate operation: " + jdbcEx.getMessage(), jdbcEx.getSQL(), jdbcEx.getSQLException());
if (dae != null) {
return dae;
}
}
return convertHibernateAccessException(ex, ex);
}
private DataAccessException convertHibernateAccessException(HibernateException ex, HibernateException exToCheck) {
if (this.jdbcExceptionTranslator != null && exToCheck instanceof JDBCException jdbcEx) {
DataAccessException dae = this.jdbcExceptionTranslator.translate(
"Hibernate operation: " + jdbcEx.getMessage(), jdbcEx.getSQL(), jdbcEx.getSQLException());
if (dae != null) {
return dae;
}
}
if (this.transactionExceptionTranslator != null && exToCheck instanceof org.hibernate.TransactionException) {
if (ex.getCause() instanceof SQLException sqlEx) {
DataAccessException dae = this.transactionExceptionTranslator.translate(
"Hibernate transaction: " + ex.getMessage(), null, sqlEx);
if (dae != null) {
return dae;
}
}
}
if (exToCheck instanceof JDBCConnectionException) {
return new DataAccessResourceFailureException(ex.getMessage(), ex);
}
if (exToCheck instanceof SQLGrammarException hibEx) {
return new InvalidDataAccessResourceUsageException(ex.getMessage() + "; SQL [" + hibEx.getSQL() + "]", ex);
}
if (exToCheck instanceof QueryTimeoutException hibEx) {
return new org.springframework.dao.QueryTimeoutException(ex.getMessage() + "; SQL [" + hibEx.getSQL() + "]", ex);
}
if (exToCheck instanceof LockAcquisitionException hibEx) {
return new CannotAcquireLockException(ex.getMessage() + "; SQL [" + hibEx.getSQL() + "]", ex);
}
if (exToCheck instanceof PessimisticLockException hibEx) {
return new PessimisticLockingFailureException(ex.getMessage() + "; SQL [" + hibEx.getSQL() + "]", ex);
}
if (exToCheck instanceof ConstraintViolationException hibEx) {
return new DataIntegrityViolationException(ex.getMessage() + "; SQL [" + hibEx.getSQL() +
"]; constraint [" + hibEx.getConstraintName() + "]", ex);
}
if (exToCheck instanceof DataException hibEx) {
return new DataIntegrityViolationException(ex.getMessage() + "; SQL [" + hibEx.getSQL() + "]", ex);
}
// end of JDBCException subclass handling
if (exToCheck instanceof QueryException) {
return new InvalidDataAccessResourceUsageException(ex.getMessage(), ex);
}
if (exToCheck instanceof NonUniqueResultException) {
return new IncorrectResultSizeDataAccessException(ex.getMessage(), 1, ex);
}
if (exToCheck instanceof NonUniqueObjectException) {
return new DuplicateKeyException(ex.getMessage(), ex);
}
if (exToCheck instanceof PropertyValueException) {
return new DataIntegrityViolationException(ex.getMessage(), ex);
}
if (exToCheck instanceof PersistentObjectException) {
return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
}
if (exToCheck instanceof TransientObjectException) {
return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
}
if (exToCheck instanceof ObjectDeletedException) {
return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
}
if (exToCheck instanceof UnresolvableObjectException hibEx) {
return new ObjectRetrievalFailureException(hibEx.getEntityName(), hibEx.getIdentifier(), ex.getMessage(), ex);
}
if (exToCheck instanceof WrongClassException hibEx) {
return new ObjectRetrievalFailureException(hibEx.getEntityName(), hibEx.getIdentifier(), ex.getMessage(), ex);
}
if (exToCheck instanceof StaleObjectStateException hibEx) {
return new ObjectOptimisticLockingFailureException(hibEx.getEntityName(), hibEx.getIdentifier(), ex.getMessage(), ex);
}
if (exToCheck instanceof StaleStateException) {
return new ObjectOptimisticLockingFailureException(ex.getMessage(), ex);
}
if (exToCheck instanceof OptimisticEntityLockException) {
return new ObjectOptimisticLockingFailureException(ex.getMessage(), ex);
}
if (exToCheck instanceof PessimisticEntityLockException) {
if (ex.getCause() instanceof LockAcquisitionException) {
return new CannotAcquireLockException(ex.getMessage(), ex.getCause());
}
return new PessimisticLockingFailureException(ex.getMessage(), ex);
}
// Fallback: check potentially more specific cause, otherwise JpaSystemException
if (exToCheck.getCause() instanceof HibernateException causeToCheck) {
return convertHibernateAccessException(ex, causeToCheck);
}
return new JpaSystemException(ex);
}
}

View File

@ -14,15 +14,14 @@
* limitations under the License.
*/
package org.springframework.orm.hibernate5;
package org.springframework.orm.jpa.hibernate;
import java.sql.Connection;
import java.sql.ResultSet;
import java.util.Map;
import java.util.function.Consumer;
import javax.sql.DataSource;
import jakarta.persistence.PersistenceException;
import org.hibernate.ConnectionReleaseMode;
import org.hibernate.FlushMode;
import org.hibernate.HibernateException;
@ -30,8 +29,12 @@ import org.hibernate.Interceptor;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Environment;
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.resource.transaction.spi.TransactionStatus;
import org.hibernate.service.UnknownServiceException;
import org.jspecify.annotations.Nullable;
import org.springframework.beans.BeansException;
@ -40,10 +43,12 @@ import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.dao.support.DataAccessUtils;
import org.springframework.jdbc.datasource.ConnectionHolder;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.jdbc.datasource.JdbcTransactionObjectSupport;
import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
import org.springframework.orm.jpa.EntityManagerFactoryUtils;
import org.springframework.transaction.CannotCreateTransactionException;
import org.springframework.transaction.IllegalTransactionStateException;
import org.springframework.transaction.InvalidIsolationLevelException;
@ -74,9 +79,9 @@ import org.springframework.util.Assert;
* and services which use plain JDBC (without being aware of Hibernate)!
* Application code needs to stick to the same simple Connection lookup pattern as
* with {@link org.springframework.jdbc.datasource.DataSourceTransactionManager}
* (i.e. {@link org.springframework.jdbc.datasource.DataSourceUtils#getConnection}
* (i.e. {@link DataSourceUtils#getConnection}
* or going through a
* {@link org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy}).
* {@link TransactionAwareDataSourceProxy}).
*
* <p>Note: To be able to register a DataSource's Connection for plain JDBC code,
* this instance needs to be aware of the DataSource ({@link #setDataSource}).
@ -97,12 +102,8 @@ import org.springframework.util.Assert;
* support nested transactions! Hence, do not expect Hibernate access code to
* semantically participate in a nested transaction.</i>
*
* <p><b>NOTE: Hibernate ORM 6.x is officially only supported as a JPA provider.
* Please use {@link org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean}
* with {@link org.springframework.orm.jpa.JpaTransactionManager} there instead.</b>
*
* @author Juergen Hoeller
* @since 4.2
* @since 7.0
* @see #setSessionFactory
* @see SessionFactory#getCurrentSession()
* @see org.springframework.jdbc.core.JdbcTemplate
@ -114,6 +115,8 @@ import org.springframework.util.Assert;
public class HibernateTransactionManager extends AbstractPlatformTransactionManager
implements ResourceTransactionManager, BeanFactoryAware, InitializingBean {
private final HibernateExceptionTranslator exceptionTranslator = new HibernateExceptionTranslator();
private @Nullable SessionFactory sessionFactory;
private @Nullable DataSource dataSource;
@ -122,8 +125,6 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana
private boolean prepareConnection = true;
private boolean allowResultAccessAfterCompletion = false;
private boolean hibernateManagedSession = false;
private @Nullable Consumer<Session> sessionInitializer;
@ -173,7 +174,6 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana
* Obtain the SessionFactory for actual use.
* @return the SessionFactory (never {@code null})
* @throws IllegalStateException in case of no SessionFactory set
* @since 5.0
*/
protected final SessionFactory obtainSessionFactory() {
SessionFactory sessionFactory = getSessionFactory();
@ -257,31 +257,12 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana
this.prepareConnection = prepareConnection;
}
/**
* Set whether to allow result access after completion, typically via Hibernate's
* ScrollableResults mechanism.
* <p>Default is "false". Turning this flag on enforces over-commit holdability on the
* underlying JDBC Connection (if {@link #prepareConnection "prepareConnection"} is on)
* and skips the disconnect-on-completion step.
* @see Connection#setHoldability
* @see ResultSet#HOLD_CURSORS_OVER_COMMIT
* @see #disconnectOnCompletion(Session)
* @deprecated as of 5.3.29 since Hibernate 5.x aggressively closes ResultSets on commit,
* making it impossible to rely on ResultSet holdability. Also, Spring does not provide
* an equivalent setting on {@link org.springframework.orm.jpa.JpaTransactionManager}.
*/
@Deprecated(since = "5.3.29")
public void setAllowResultAccessAfterCompletion(boolean allowResultAccessAfterCompletion) {
this.allowResultAccessAfterCompletion = allowResultAccessAfterCompletion;
}
/**
* Set whether to operate on a Hibernate-managed Session instead of a
* Spring-managed Session, that is, whether to obtain the Session through
* Hibernate's {@link SessionFactory#getCurrentSession()}
* instead of {@link SessionFactory#openSession()} (with a Spring
* {@link TransactionSynchronizationManager}
* check preceding it).
* Hibernate's {@link SessionFactory#getCurrentSession()} instead of
* {@link SessionFactory#openSession()} (with a Spring
* {@link TransactionSynchronizationManager} check preceding it).
* <p>Default is "false", i.e. using a Spring-managed Session: taking the current
* thread-bound Session if available (for example, in an Open-Session-in-View scenario),
* creating a new Session for the current transaction otherwise.
@ -308,7 +289,6 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana
* created for a new transaction managed by this {@code HibernateTransactionManager}.
* <p>This enables convenient customizations for application purposes, for example,
* setting Hibernate filters.
* @since 5.3
* @see Session#enableFilter
*/
public void setSessionInitializer(Consumer<Session> sessionInitializer) {
@ -392,7 +372,7 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana
// Check for SessionFactory's DataSource.
if (this.autodetectDataSource && getDataSource() == null) {
DataSource sfds = SessionFactoryUtils.getDataSource(getSessionFactory());
DataSource sfds = determineDataSource();
if (sfds != null) {
// Use the SessionFactory's DataSource for exposing transactions to JDBC code.
if (logger.isDebugEnabled()) {
@ -404,6 +384,36 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana
}
}
/**
* Determine the DataSource of the given SessionFactory.
* @return the DataSource, or {@code null} if none found
* @see ConnectionProvider
*/
protected @Nullable DataSource determineDataSource() {
SessionFactory sessionFactory = obtainSessionFactory();
Map<String, Object> props = sessionFactory.getProperties();
if (props != null) {
Object dataSourceValue = props.get(Environment.JAKARTA_NON_JTA_DATASOURCE);
if (dataSourceValue instanceof DataSource dataSourceToUse) {
return dataSourceToUse;
}
}
if (sessionFactory instanceof SessionFactoryImplementor sfi) {
try {
ConnectionProvider cp = sfi.getServiceRegistry().getService(ConnectionProvider.class);
if (cp != null) {
return cp.unwrap(DataSource.class);
}
}
catch (UnknownServiceException ex) {
if (logger.isDebugEnabled()) {
logger.debug("No ConnectionProvider found - cannot determine DataSource for SessionFactory: " + ex);
}
}
}
return null;
}
@Override
public Object getResourceFactory() {
@ -485,9 +495,8 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana
session = txObject.getSessionHolder().getSession().unwrap(SessionImplementor.class);
boolean holdabilityNeeded = (this.allowResultAccessAfterCompletion && !txObject.isNewSession());
boolean isolationLevelNeeded = (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT);
if (holdabilityNeeded || isolationLevelNeeded || definition.isReadOnly()) {
if (isolationLevelNeeded || definition.isReadOnly()) {
if (this.prepareConnection && ConnectionReleaseMode.ON_CLOSE.equals(
session.getJdbcCoordinator().getLogicalConnection().getConnectionHandlingMode().getReleaseMode())) {
// We're allowed to change the transaction settings of the JDBC Connection.
@ -498,13 +507,6 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
txObject.setPreviousIsolationLevel(previousIsolationLevel);
txObject.setReadOnly(definition.isReadOnly());
if (holdabilityNeeded) {
int currentHoldability = con.getHoldability();
if (currentHoldability != ResultSet.HOLD_CURSORS_OVER_COMMIT) {
txObject.setPreviousHoldability(currentHoldability);
con.setHoldability(ResultSet.HOLD_CURSORS_OVER_COMMIT);
}
}
txObject.connectionPrepared();
}
else {
@ -525,7 +527,7 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana
if (definition.isReadOnly() && txObject.isNewSession()) {
// Just set to MANUAL in case of a new Session for this transaction.
session.setHibernateFlushMode(FlushMode.MANUAL);
// As of 5.1, we're also setting Hibernate's read-only entity mode by default.
// We're also setting Hibernate's read-only entity mode by default.
session.setDefaultReadOnly(true);
}
@ -543,8 +545,6 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana
// Register transaction timeout.
int timeout = determineTimeout(definition);
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
// Use Hibernate's own transaction timeout mechanism on Hibernate 3.1+
// Applies to all statements, also to inserts, updates and deletes!
hibTx = session.getTransaction();
hibTx.setTimeout(timeout);
hibTx.begin();
@ -590,7 +590,7 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana
logger.debug("Could not rollback Session after failed transaction begin", ex);
}
finally {
SessionFactoryUtils.closeSession(session);
EntityManagerFactoryUtils.closeEntityManager(session);
txObject.setSessionHolder(null);
}
}
@ -643,18 +643,15 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana
hibTx.commit();
}
catch (org.hibernate.TransactionException ex) {
// assumably from commit call to the underlying JDBC connection
DataAccessException dae = this.exceptionTranslator.translateExceptionIfPossible(ex);
if (dae != null) {
throw dae;
}
throw new TransactionSystemException("Could not commit Hibernate transaction", ex);
}
catch (HibernateException ex) {
// assumably failed to flush changes to database
throw convertHibernateAccessException(ex);
}
catch (PersistenceException ex) {
if (ex.getCause() instanceof HibernateException hibernateEx) {
throw convertHibernateAccessException(hibernateEx);
}
throw ex;
catch (RuntimeException ex) {
// Assumably failed to flush changes to database.
throw DataAccessUtils.translateIfNecessary(ex, this.exceptionTranslator);
}
}
@ -672,17 +669,15 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana
hibTx.rollback();
}
catch (org.hibernate.TransactionException ex) {
DataAccessException dae = this.exceptionTranslator.translateExceptionIfPossible(ex);
if (dae != null) {
throw dae;
}
throw new TransactionSystemException("Could not roll back Hibernate transaction", ex);
}
catch (HibernateException ex) {
catch (RuntimeException ex) {
// Shouldn't really happen, as a rollback doesn't cause a flush.
throw convertHibernateAccessException(ex);
}
catch (PersistenceException ex) {
if (ex.getCause() instanceof HibernateException hibernateEx) {
throw convertHibernateAccessException(hibernateEx);
}
throw ex;
throw DataAccessUtils.translateIfNecessary(ex, this.exceptionTranslator);
}
finally {
if (!txObject.isNewSession() && !this.hibernateManagedSession) {
@ -744,7 +739,7 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana
if (logger.isDebugEnabled()) {
logger.debug("Closing Hibernate Session [" + session + "] after transaction");
}
SessionFactoryUtils.closeSession(session);
EntityManagerFactoryUtils.closeEntityManager(session);
}
else {
if (logger.isDebugEnabled()) {
@ -753,7 +748,7 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana
if (txObject.getSessionHolder().getPreviousFlushMode() != null) {
session.setHibernateFlushMode(txObject.getSessionHolder().getPreviousFlushMode());
}
if (!this.allowResultAccessAfterCompletion && !this.hibernateManagedSession) {
if (!this.hibernateManagedSession) {
disconnectOnCompletion(session);
}
}
@ -773,17 +768,6 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana
}
}
/**
* Convert the given HibernateException to an appropriate exception
* from the {@code org.springframework.dao} hierarchy.
* @param ex the HibernateException that occurred
* @return a corresponding DataAccessException
* @see SessionFactoryUtils#convertHibernateAccessException
*/
protected DataAccessException convertHibernateAccessException(HibernateException ex) {
return SessionFactoryUtils.convertHibernateAccessException(ex);
}
/**
* Hibernate transaction object, representing a SessionHolder.
@ -879,14 +863,8 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana
try {
getSessionHolder().getSession().flush();
}
catch (HibernateException ex) {
throw convertHibernateAccessException(ex);
}
catch (PersistenceException ex) {
if (ex.getCause() instanceof HibernateException hibernateEx) {
throw convertHibernateAccessException(hibernateEx);
}
throw ex;
catch (RuntimeException ex) {
throw DataAccessUtils.translateIfNecessary(ex, HibernateTransactionManager.this.exceptionTranslator);
}
}
}

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.orm.hibernate5;
package org.springframework.orm.jpa.hibernate;
import java.io.File;
import java.io.IOException;
@ -59,7 +59,7 @@ import org.springframework.core.type.filter.TypeFilter;
* 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.
*
* <p>Compatible with Hibernate ORM 5.5/5.6, as of Spring Framework 6.0.
* <p>Compatible with Hibernate ORM 7.0, as of Spring Framework 7.0.
* This Hibernate-specific {@code LocalSessionFactoryBean} can be an immediate alternative
* to {@link org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean} for
* common JPA purposes: The Hibernate {@code SessionFactory} will natively expose the JPA
@ -68,12 +68,8 @@ import org.springframework.core.type.filter.TypeFilter;
* {@link HibernateTransactionManager}, this naturally allows for mixing JPA access code
* with native Hibernate access code within the same transaction.
*
* <p><b>NOTE: Hibernate ORM 6.x is officially only supported as a JPA provider.
* Please use {@link org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean}
* with {@link org.springframework.orm.jpa.JpaTransactionManager} there instead.</b>
*
* @author Juergen Hoeller
* @since 4.2
* @since 7.0
* @see #setDataSource
* @see #setPackagesToScan
* @see HibernateTransactionManager
@ -274,7 +270,6 @@ public class LocalSessionFactoryBean extends HibernateExceptionTranslator
* Allows for using a Spring-managed {@code RegionFactory} instance.
* <p>Note: If this is set, the Hibernate settings should not define a
* cache provider to avoid meaningless double configuration.
* @since 5.1
* @see LocalSessionFactoryBuilder#setCacheRegionFactory
*/
public void setCacheRegionFactory(RegionFactory cacheRegionFactory) {
@ -283,7 +278,6 @@ public class LocalSessionFactoryBean extends HibernateExceptionTranslator
/**
* Set a {@link MultiTenantConnectionProvider} to be passed on to the SessionFactory.
* @since 4.3
* @see LocalSessionFactoryBuilder#setMultiTenantConnectionProvider
*/
public void setMultiTenantConnectionProvider(MultiTenantConnectionProvider<?> multiTenantConnectionProvider) {
@ -369,7 +363,6 @@ public class LocalSessionFactoryBean extends HibernateExceptionTranslator
* in init methods of related beans, even for metadata introspection purposes.
* <p>As of 6.2, Hibernate initialization is enforced before context refresh
* completion, waiting for asynchronous bootstrapping to complete by then.
* @since 4.3
* @see LocalSessionFactoryBuilder#buildSessionFactory(AsyncTaskExecutor)
*/
public void setBootstrapExecutor(AsyncTaskExecutor bootstrapExecutor) {
@ -381,7 +374,6 @@ public class LocalSessionFactoryBean extends HibernateExceptionTranslator
* <p>This will only be applied for an internally built {@link MetadataSources}
* instance. {@link #setMetadataSources} effectively overrides such settings,
* with integrators to be applied to the externally built {@link MetadataSources}.
* @since 5.1
* @see #setMetadataSources
* @see BootstrapServiceRegistryBuilder#applyIntegrator
*/
@ -392,8 +384,7 @@ public class LocalSessionFactoryBean extends HibernateExceptionTranslator
/**
* Specify a Hibernate {@link MetadataSources} service to use (for example, reusing an
* existing one), potentially populated with a custom Hibernate bootstrap
* {@link org.hibernate.service.ServiceRegistry} as well.
* @since 4.3
* {@link ServiceRegistry} as well.
* @see MetadataSources#MetadataSources(ServiceRegistry)
* @see BootstrapServiceRegistryBuilder#build()
*/
@ -407,7 +398,6 @@ public class LocalSessionFactoryBean extends HibernateExceptionTranslator
* <p>Can also be externally called to initialize and pre-populate a {@link MetadataSources}
* instance which is then going to be used for {@link SessionFactory} building.
* @return the MetadataSources to use (never {@code null})
* @since 4.3
* @see LocalSessionFactoryBuilder#LocalSessionFactoryBuilder(DataSource, ResourceLoader, MetadataSources)
*/
public MetadataSources getMetadataSources() {
@ -439,7 +429,6 @@ public class LocalSessionFactoryBean extends HibernateExceptionTranslator
/**
* Determine the Spring {@link ResourceLoader} to use for Hibernate metadata.
* @return the ResourceLoader to use (never {@code null})
* @since 4.3
*/
public ResourceLoader getResourceLoader() {
if (this.resourcePatternResolver == null) {
@ -452,7 +441,6 @@ public class LocalSessionFactoryBean extends HibernateExceptionTranslator
* Accept the containing {@link BeanFactory}, registering corresponding Hibernate
* {@link org.hibernate.resource.beans.container.spi.BeanContainer} integration for
* it if possible. This requires a Spring {@link ConfigurableListableBeanFactory}.
* @since 5.1
* @see SpringBeanContainer
* @see LocalSessionFactoryBuilder#setBeanContainer
*/

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.orm.hibernate5;
package org.springframework.orm.jpa.hibernate;
import java.io.FileNotFoundException;
import java.io.IOException;
@ -80,7 +80,7 @@ import org.springframework.util.ClassUtils;
* Typically combined with {@link HibernateTransactionManager} for declarative
* transactions against the {@code SessionFactory} and its JDBC {@code DataSource}.
*
* <p>Compatible with Hibernate ORM 5.5/5.6, as of Spring Framework 6.0.
* <p>Compatible with Hibernate ORM 7.0, as of Spring Framework 7.0.
* This Hibernate-specific factory builder can also be a convenient way to set up
* a JPA {@code EntityManagerFactory} since the Hibernate {@code SessionFactory}
* natively exposes the JPA {@code EntityManagerFactory} interface as well now.
@ -91,7 +91,7 @@ import org.springframework.util.ClassUtils;
* standard JPA bootstrap contract.
*
* @author Juergen Hoeller
* @since 4.2
* @since 7.0
* @see HibernateTransactionManager
* @see LocalSessionFactoryBean
* @see #setBeanContainer
@ -159,7 +159,6 @@ public class LocalSessionFactoryBuilder extends Configuration {
* (may be {@code null})
* @param resourceLoader the ResourceLoader to load application classes from
* @param metadataSources the Hibernate MetadataSources service to use (for example, reusing an existing one)
* @since 4.3
*/
public LocalSessionFactoryBuilder(
@Nullable DataSource dataSource, ResourceLoader resourceLoader, MetadataSources metadataSources) {
@ -228,7 +227,6 @@ public class LocalSessionFactoryBuilder extends Configuration {
* Set a Hibernate {@link org.hibernate.resource.beans.container.spi.BeanContainer}
* for the given Spring {@link ConfigurableListableBeanFactory}.
* <p>This enables autowiring of Hibernate attribute converters and entity listeners.
* @since 5.1
* @see SpringBeanContainer
* @see AvailableSettings#BEAN_CONTAINER
*/
@ -242,7 +240,6 @@ public class LocalSessionFactoryBuilder extends Configuration {
* Allows for using a Spring-managed {@code RegionFactory} instance.
* <p>Note: If this is set, the Hibernate settings should not define a
* cache provider to avoid meaningless double configuration.
* @since 5.1
* @see AvailableSettings#CACHE_REGION_FACTORY
*/
public LocalSessionFactoryBuilder setCacheRegionFactory(RegionFactory cacheRegionFactory) {
@ -252,7 +249,6 @@ public class LocalSessionFactoryBuilder extends Configuration {
/**
* Set a {@link MultiTenantConnectionProvider} to be passed on to the SessionFactory.
* @since 4.3
* @see AvailableSettings#MULTI_TENANT_CONNECTION_PROVIDER
*/
public LocalSessionFactoryBuilder setMultiTenantConnectionProvider(MultiTenantConnectionProvider<?> multiTenantConnectionProvider) {
@ -262,7 +258,6 @@ public class LocalSessionFactoryBuilder extends Configuration {
/**
* Overridden to reliably pass a {@link CurrentTenantIdentifierResolver} to the SessionFactory.
* @since 4.3.2
* @see AvailableSettings#MULTI_TENANT_IDENTIFIER_RESOLVER
*/
@Override
@ -386,7 +381,6 @@ public class LocalSessionFactoryBuilder extends Configuration {
* then block until Hibernate's bootstrapping completed, if not ready by then.
* For maximum benefit, make sure to avoid early {@code SessionFactory} calls
* in init methods of related beans, even for metadata introspection purposes.
* @since 4.3
* @see #buildSessionFactory()
*/
public SessionFactory buildSessionFactory(AsyncTaskExecutor bootstrapExecutor) {
@ -400,7 +394,6 @@ public class LocalSessionFactoryBuilder extends Configuration {
/**
* Proxy invocation handler for background bootstrapping, only enforcing
* a fully initialized target {@code SessionFactory} when actually needed.
* @since 4.3
*/
private class BootstrapSessionFactoryInvocationHandler implements InvocationHandler {

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.orm.hibernate5;
package org.springframework.orm.jpa.hibernate;
import org.hibernate.FlushMode;
import org.hibernate.Session;
@ -26,17 +26,16 @@ import org.springframework.orm.jpa.EntityManagerHolder;
/**
* Resource holder wrapping a Hibernate {@link Session} (plus an optional {@link Transaction}).
* {@link HibernateTransactionManager} binds instances of this class to the thread,
* for a given {@link org.hibernate.SessionFactory}. Extends {@link EntityManagerHolder}
* as of 5.1, automatically exposing an {@code EntityManager} handle on Hibernate 5.2+.
* for a given {@link org.hibernate.SessionFactory}. Extends {@link EntityManagerHolder},
* automatically exposing an {@code EntityManager} handle.
*
* <p>Note: This is an SPI class, not intended to be used by applications.
*
* @author Juergen Hoeller
* @since 4.2
* @since 7.0
* @see HibernateTransactionManager
* @see SessionFactoryUtils
*/
public class SessionHolder extends EntityManagerHolder {
class SessionHolder extends EntityManagerHolder {
private @Nullable Transaction transaction;

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.orm.hibernate5;
package org.springframework.orm.jpa.hibernate;
import java.util.Map;
import java.util.function.Consumer;
@ -49,7 +49,7 @@ import org.springframework.util.ConcurrentReferenceHashMap;
* &lt;property name="jpaPropertyMap"&gt;
* &lt;map&gt;
* &lt;entry key="hibernate.resource.beans.container"&gt;
* &lt;bean class="org.springframework.orm.hibernate5.SpringBeanContainer"/&gt;
* &lt;bean class="org.springframework.orm.jpa.hibernate.SpringBeanContainer"/&gt;
* &lt;/entry&gt;
* &lt;/map&gt;
* &lt;/property&gt;
@ -69,7 +69,7 @@ import org.springframework.util.ConcurrentReferenceHashMap;
* integration will be registered out of the box.
*
* @author Juergen Hoeller
* @since 5.1
* @since 7.0
* @see LocalSessionFactoryBean#setBeanFactory
* @see LocalSessionFactoryBuilder#setBeanContainer
* @see org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean#setJpaPropertyMap

View File

@ -14,11 +14,12 @@
* limitations under the License.
*/
package org.springframework.orm.hibernate5;
package org.springframework.orm.jpa.hibernate;
import org.hibernate.Session;
import org.jspecify.annotations.Nullable;
import org.springframework.dao.support.DataAccessUtils;
import org.springframework.transaction.support.TransactionSynchronization;
/**
@ -26,9 +27,11 @@ import org.springframework.transaction.support.TransactionSynchronization;
* to the underlying Hibernate Session. Used in combination with JTA.
*
* @author Juergen Hoeller
* @since 4.2
* @since 7.0
*/
public class SpringFlushSynchronization implements TransactionSynchronization {
class SpringFlushSynchronization implements TransactionSynchronization {
static final HibernateExceptionTranslator exceptionTranslator = new HibernateExceptionTranslator();
private final Session session;
@ -40,7 +43,12 @@ public class SpringFlushSynchronization implements TransactionSynchronization {
@Override
public void flush() {
SessionFactoryUtils.flush(this.session, false);
try {
this.session.flush();
}
catch (RuntimeException ex) {
throw DataAccessUtils.translateIfNecessary(ex, exceptionTranslator);
}
}
@Override

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.orm.hibernate5;
package org.springframework.orm.jpa.hibernate;
import org.hibernate.FlushMode;
import org.hibernate.Session;
@ -28,10 +28,10 @@ import org.springframework.transaction.support.TransactionSynchronizationManager
* setting {@code FlushMode.MANUAL} for read-only transactions.
*
* @author Juergen Hoeller
* @since 4.2
* @since 7.0
*/
@SuppressWarnings("serial")
public class SpringJtaSessionContext extends JTASessionContext {
class SpringJtaSessionContext extends JTASessionContext {
public SpringJtaSessionContext(SessionFactoryImplementor factory) {
super(factory);

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.orm.hibernate5;
package org.springframework.orm.jpa.hibernate;
import jakarta.transaction.Status;
import jakarta.transaction.SystemException;
@ -32,16 +32,15 @@ import org.springframework.orm.jpa.EntityManagerHolder;
import org.springframework.transaction.support.TransactionSynchronizationManager;
/**
* Implementation of Hibernate 3.1's {@link CurrentSessionContext} interface
* that delegates to Spring's {@link SessionFactoryUtils} for providing a
* Spring-managed current {@link Session}.
* Implementation of Hibernate's {@link CurrentSessionContext} interface
* that provides a Spring-managed current {@link Session}.
*
* <p>This CurrentSessionContext implementation can also be specified in custom
* SessionFactory setup through the "hibernate.current_session_context_class"
* property, with the fully qualified name of this class as value.
*
* @author Juergen Hoeller
* @since 4.2
* @since 7.0
*/
@SuppressWarnings("serial")
public class SpringSessionContext implements CurrentSessionContext {

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.orm.hibernate5;
package org.springframework.orm.jpa.hibernate;
import org.hibernate.FlushMode;
import org.hibernate.Session;
@ -23,6 +23,9 @@ import org.hibernate.engine.spi.SessionImplementor;
import org.springframework.core.Ordered;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.DataAccessUtils;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.orm.jpa.EntityManagerFactoryUtils;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
@ -31,9 +34,18 @@ import org.springframework.transaction.support.TransactionSynchronizationManager
* for a pre-bound Hibernate Session.
*
* @author Juergen Hoeller
* @since 4.2
* @since 7.0
*/
public class SpringSessionSynchronization implements TransactionSynchronization, Ordered {
class SpringSessionSynchronization implements TransactionSynchronization, Ordered {
/**
* Order value for TransactionSynchronization objects that clean up Hibernate Sessions.
* Returns {@code DataSourceUtils.CONNECTION_SYNCHRONIZATION_ORDER - 100}
* to execute Session cleanup before JDBC Connection cleanup, if any.
* @see DataSourceUtils#CONNECTION_SYNCHRONIZATION_ORDER
*/
private static final int SESSION_SYNCHRONIZATION_ORDER =
DataSourceUtils.CONNECTION_SYNCHRONIZATION_ORDER - 100;
private final SessionHolder sessionHolder;
@ -62,7 +74,7 @@ public class SpringSessionSynchronization implements TransactionSynchronization,
@Override
public int getOrder() {
return SessionFactoryUtils.SESSION_SYNCHRONIZATION_ORDER;
return SESSION_SYNCHRONIZATION_ORDER;
}
@Override
@ -86,7 +98,12 @@ public class SpringSessionSynchronization implements TransactionSynchronization,
@Override
public void flush() {
SessionFactoryUtils.flush(getCurrentSession(), false);
try {
getCurrentSession().flush();
}
catch (RuntimeException ex) {
throw DataAccessUtils.translateIfNecessary(ex, SpringFlushSynchronization.exceptionTranslator);
}
}
@Override
@ -96,7 +113,12 @@ public class SpringSessionSynchronization implements TransactionSynchronization,
// Read-write transaction -> flush the Hibernate Session.
// Further check: only flush when not FlushMode.MANUAL.
if (!FlushMode.MANUAL.equals(session.getHibernateFlushMode())) {
SessionFactoryUtils.flush(getCurrentSession(), true);
try {
getCurrentSession().flush();
}
catch (RuntimeException ex) {
throw DataAccessUtils.translateIfNecessary(ex, SpringFlushSynchronization.exceptionTranslator);
}
}
}
}
@ -140,7 +162,7 @@ public class SpringSessionSynchronization implements TransactionSynchronization,
this.sessionHolder.setSynchronizedWithTransaction(false);
// Call close() at this point if it's a new Session...
if (this.newSession) {
SessionFactoryUtils.closeSession(this.sessionHolder.getSession());
EntityManagerFactoryUtils.closeEntityManager(this.sessionHolder.getSession());
}
}
}

View File

@ -0,0 +1,14 @@
/**
* Hibernate-specific support classes, integrated with JPA.
*
* <p>Contains Hibernate-specific setup options as an alternative to JPA bootstrapping,
* primarily for use with Hibernate's native {@code sessionFactory.getCurrentSession()}
* but potentially also for JPA repositories or mixed use of native Hibernate and JPA.
*
* <p>As of Spring Framework 7.0, this package supersedes {@code orm.hibernate5} -
* now for use with Hibernate ORM 7.0, tightly integrated with JPA.
*/
@NullMarked
package org.springframework.orm.jpa.hibernate;
import org.jspecify.annotations.NullMarked;

View File

@ -23,59 +23,26 @@ import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceException;
import org.hibernate.ConnectionReleaseMode;
import org.hibernate.FlushMode;
import org.hibernate.HibernateException;
import org.hibernate.JDBCException;
import org.hibernate.NonUniqueObjectException;
import org.hibernate.NonUniqueResultException;
import org.hibernate.ObjectDeletedException;
import org.hibernate.PersistentObjectException;
import org.hibernate.PessimisticLockException;
import org.hibernate.PropertyValueException;
import org.hibernate.QueryException;
import org.hibernate.QueryTimeoutException;
import org.hibernate.Session;
import org.hibernate.StaleObjectStateException;
import org.hibernate.StaleStateException;
import org.hibernate.TransientObjectException;
import org.hibernate.UnresolvableObjectException;
import org.hibernate.WrongClassException;
import org.hibernate.dialect.lock.OptimisticEntityLockException;
import org.hibernate.dialect.lock.PessimisticEntityLockException;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.exception.ConstraintViolationException;
import org.hibernate.exception.DataException;
import org.hibernate.exception.JDBCConnectionException;
import org.hibernate.exception.LockAcquisitionException;
import org.hibernate.exception.SQLGrammarException;
import org.jspecify.annotations.Nullable;
import org.springframework.dao.CannotAcquireLockException;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.dao.InvalidDataAccessResourceUsageException;
import org.springframework.dao.PessimisticLockingFailureException;
import org.springframework.jdbc.datasource.ConnectionHandle;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.jdbc.support.SQLExceptionSubclassTranslator;
import org.springframework.jdbc.support.SQLExceptionTranslator;
import org.springframework.orm.ObjectOptimisticLockingFailureException;
import org.springframework.orm.ObjectRetrievalFailureException;
import org.springframework.orm.jpa.DefaultJpaDialect;
import org.springframework.orm.jpa.EntityManagerFactoryUtils;
import org.springframework.orm.jpa.JpaSystemException;
import org.springframework.orm.jpa.hibernate.HibernateExceptionTranslator;
import org.springframework.transaction.InvalidIsolationLevelException;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionException;
import org.springframework.transaction.support.ResourceTransactionDefinition;
import org.springframework.util.ReflectionUtils;
/**
* {@link org.springframework.orm.jpa.JpaDialect} implementation for Hibernate.
* Compatible with Hibernate ORM 5.5/5.6 as well as 6.0/6.1/6.2/6.3.
* Compatible with Hibernate ORM 7.0.
*
* @author Juergen Hoeller
* @author Costin Leau
@ -87,12 +54,10 @@ import org.springframework.util.ReflectionUtils;
@SuppressWarnings("serial")
public class HibernateJpaDialect extends DefaultJpaDialect {
private final HibernateExceptionTranslator exceptionTranslator = new HibernateExceptionTranslator();
boolean prepareConnection = true;
private @Nullable SQLExceptionTranslator jdbcExceptionTranslator;
private @Nullable SQLExceptionTranslator transactionExceptionTranslator = new SQLExceptionSubclassTranslator();
/**
* Set whether to prepare the underlying JDBC Connection of a transactional
@ -137,8 +102,7 @@ public class HibernateJpaDialect extends DefaultJpaDialect {
* @see org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator
*/
public void setJdbcExceptionTranslator(@Nullable SQLExceptionTranslator exceptionTranslator) {
this.jdbcExceptionTranslator = exceptionTranslator;
this.transactionExceptionTranslator = exceptionTranslator;
this.exceptionTranslator.setJdbcExceptionTranslator(exceptionTranslator);
}
@ -146,7 +110,7 @@ public class HibernateJpaDialect extends DefaultJpaDialect {
public Object beginTransaction(EntityManager entityManager, TransactionDefinition definition)
throws PersistenceException, SQLException, TransactionException {
SessionImplementor session = getSession(entityManager);
SessionImplementor session = entityManager.unwrap(SessionImplementor.class);
if (definition.getTimeout() != TransactionDefinition.TIMEOUT_DEFAULT) {
session.getTransaction().setTimeout(definition.getTimeout());
@ -191,7 +155,7 @@ public class HibernateJpaDialect extends DefaultJpaDialect {
public Object prepareTransaction(EntityManager entityManager, boolean readOnly, @Nullable String name)
throws PersistenceException {
SessionImplementor session = getSession(entityManager);
SessionImplementor session = entityManager.unwrap(SessionImplementor.class);
FlushMode previousFlushMode = prepareFlushMode(session, readOnly);
return new SessionTransactionData(session, previousFlushMode, false, null, readOnly);
}
@ -227,136 +191,12 @@ public class HibernateJpaDialect extends DefaultJpaDialect {
public ConnectionHandle getJdbcConnection(EntityManager entityManager, boolean readOnly)
throws PersistenceException, SQLException {
SessionImplementor session = getSession(entityManager);
return new HibernateConnectionHandle(session);
return new HibernateConnectionHandle(entityManager.unwrap(SessionImplementor.class));
}
@Override
public @Nullable DataAccessException translateExceptionIfPossible(RuntimeException ex) {
if (ex instanceof HibernateException hibernateEx) {
return convertHibernateAccessException(hibernateEx);
}
if (ex instanceof PersistenceException && ex.getCause() instanceof HibernateException hibernateEx) {
return convertHibernateAccessException(hibernateEx);
}
return EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(ex);
}
/**
* Convert the given HibernateException to an appropriate exception
* from the {@code org.springframework.dao} hierarchy.
* @param ex the HibernateException that occurred
* @return the corresponding DataAccessException instance
*/
protected DataAccessException convertHibernateAccessException(HibernateException ex) {
return convertHibernateAccessException(ex, ex);
}
private DataAccessException convertHibernateAccessException(HibernateException ex, HibernateException exToCheck) {
if (this.jdbcExceptionTranslator != null && exToCheck instanceof JDBCException jdbcEx) {
DataAccessException dae = this.jdbcExceptionTranslator.translate(
"Hibernate operation: " + jdbcEx.getMessage(), jdbcEx.getSQL(), jdbcEx.getSQLException());
if (dae != null) {
return dae;
}
}
if (this.transactionExceptionTranslator != null && exToCheck instanceof org.hibernate.TransactionException) {
if (ex.getCause() instanceof SQLException sqlEx) {
DataAccessException dae = this.transactionExceptionTranslator.translate(
"Hibernate transaction: " + ex.getMessage(), null, sqlEx);
if (dae != null) {
return dae;
}
}
}
if (exToCheck instanceof JDBCConnectionException) {
return new DataAccessResourceFailureException(ex.getMessage(), ex);
}
if (exToCheck instanceof SQLGrammarException hibEx) {
return new InvalidDataAccessResourceUsageException(ex.getMessage() + "; SQL [" + hibEx.getSQL() + "]", ex);
}
if (exToCheck instanceof QueryTimeoutException hibEx) {
return new org.springframework.dao.QueryTimeoutException(ex.getMessage() + "; SQL [" + hibEx.getSQL() + "]", ex);
}
if (exToCheck instanceof LockAcquisitionException hibEx) {
return new CannotAcquireLockException(ex.getMessage() + "; SQL [" + hibEx.getSQL() + "]", ex);
}
if (exToCheck instanceof PessimisticLockException hibEx) {
return new PessimisticLockingFailureException(ex.getMessage() + "; SQL [" + hibEx.getSQL() + "]", ex);
}
if (exToCheck instanceof ConstraintViolationException hibEx) {
return new DataIntegrityViolationException(ex.getMessage() + "; SQL [" + hibEx.getSQL() +
"]; constraint [" + hibEx.getConstraintName() + "]", ex);
}
if (exToCheck instanceof DataException hibEx) {
return new DataIntegrityViolationException(ex.getMessage() + "; SQL [" + hibEx.getSQL() + "]", ex);
}
// end of JDBCException subclass handling
if (exToCheck instanceof QueryException) {
return new InvalidDataAccessResourceUsageException(ex.getMessage(), ex);
}
if (exToCheck instanceof NonUniqueResultException) {
return new IncorrectResultSizeDataAccessException(ex.getMessage(), 1, ex);
}
if (exToCheck instanceof NonUniqueObjectException) {
return new DuplicateKeyException(ex.getMessage(), ex);
}
if (exToCheck instanceof PropertyValueException) {
return new DataIntegrityViolationException(ex.getMessage(), ex);
}
if (exToCheck instanceof PersistentObjectException) {
return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
}
if (exToCheck instanceof TransientObjectException) {
return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
}
if (exToCheck instanceof ObjectDeletedException) {
return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
}
if (exToCheck instanceof UnresolvableObjectException hibEx) {
return new ObjectRetrievalFailureException(hibEx.getEntityName(), getIdentifier(hibEx), ex.getMessage(), ex);
}
if (exToCheck instanceof WrongClassException hibEx) {
return new ObjectRetrievalFailureException(hibEx.getEntityName(), getIdentifier(hibEx), ex.getMessage(), ex);
}
if (exToCheck instanceof StaleObjectStateException hibEx) {
return new ObjectOptimisticLockingFailureException(hibEx.getEntityName(), getIdentifier(hibEx), ex.getMessage(), ex);
}
if (exToCheck instanceof StaleStateException) {
return new ObjectOptimisticLockingFailureException(ex.getMessage(), ex);
}
if (exToCheck instanceof OptimisticEntityLockException) {
return new ObjectOptimisticLockingFailureException(ex.getMessage(), ex);
}
if (exToCheck instanceof PessimisticEntityLockException) {
if (ex.getCause() instanceof LockAcquisitionException) {
return new CannotAcquireLockException(ex.getMessage(), ex.getCause());
}
return new PessimisticLockingFailureException(ex.getMessage(), ex);
}
// Fallback: check potentially more specific cause, otherwise JpaSystemException
if (exToCheck.getCause() instanceof HibernateException causeToCheck) {
return convertHibernateAccessException(ex, causeToCheck);
}
return new JpaSystemException(ex);
}
protected SessionImplementor getSession(EntityManager entityManager) {
return entityManager.unwrap(SessionImplementor.class);
}
protected @Nullable Object getIdentifier(HibernateException hibEx) {
try {
// getIdentifier declares Serializable return value on 5.x but Object on 6.x
// -> not binary compatible, let's invoke it reflectively for the time being
return ReflectionUtils.invokeMethod(hibEx.getClass().getMethod("getIdentifier"), hibEx);
}
catch (NoSuchMethodException ex) {
return null;
}
return this.exceptionTranslator.translateExceptionIfPossible(ex);
}

View File

@ -41,7 +41,7 @@ import org.jspecify.annotations.Nullable;
/**
* {@link org.springframework.orm.jpa.JpaVendorAdapter} implementation for Hibernate.
* Compatible with Hibernate ORM 5.5/5.6 as well as 6.0/6.1/6.2/6.3.
* Compatible with Hibernate ORM 7.0.
*
* <p>Exposes Hibernate's persistence provider and Hibernate's Session as extended
* EntityManager interface, and adapts {@link AbstractJpaVendorAdapter}'s common

View File

@ -3,7 +3,7 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="entityManagerFactory" name="Person" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean" primary="true">
<bean id="entityManagerFactory" name="Person" class="org.springframework.orm.jpa.hibernate.LocalSessionFactoryBean" primary="true">
<property name="annotatedClasses">
<list>
<value>org.springframework.orm.jpa.domain.DriversLicense</value>
@ -26,7 +26,7 @@
<qualifier type="org.springframework.orm.jpa.domain.MyDomain"/>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<bean id="transactionManager" class="org.springframework.orm.jpa.hibernate.HibernateTransactionManager">
<property name="sessionFactory" ref="entityManagerFactory"/>
</bean>

View File

@ -15,7 +15,7 @@
</property>
<property name="jpaPropertyMap">
<props>
<prop key="hibernate.current_session_context_class">org.springframework.orm.hibernate5.SpringSessionContext</prop>
<prop key="hibernate.current_session_context_class">org.springframework.orm.jpa.hibernate.SpringSessionContext</prop>
<prop key="hibernate.cache.provider_class">org.hibernate.cache.HashtableCacheProvider</prop>
</props>
</property>

View File

@ -17,7 +17,7 @@
<jdbc:script location="classpath:/org/springframework/test/context/orm/hibernate/db-test-data.sql"/>
</jdbc:embedded-database>
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean"
<bean id="sessionFactory" class="org.springframework.orm.jpa.hibernate.LocalSessionFactoryBean"
p:dataSource-ref="dataSource">
<property name="hibernateProperties">
<props>
@ -33,7 +33,7 @@
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager"
<bean id="transactionManager" class="org.springframework.orm.jpa.hibernate.HibernateTransactionManager"
p:sessionFactory-ref="sessionFactory"/>
</beans>