Migrate orm.hibernate5 to orm.jpa.hibernate package for Hibernate ORM 7.0
Closes gh-35111
This commit is contained in:
parent
f3f05da39b
commit
d47f1a1749
|
@ -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>
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
*/
|
|
@ -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 {
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
|||
* <property name="jpaPropertyMap">
|
||||
* <map>
|
||||
* <entry key="hibernate.resource.beans.container">
|
||||
* <bean class="org.springframework.orm.hibernate5.SpringBeanContainer"/>
|
||||
* <bean class="org.springframework.orm.jpa.hibernate.SpringBeanContainer"/>
|
||||
* </entry>
|
||||
* </map>
|
||||
* </property>
|
||||
|
@ -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
|
|
@ -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
|
|
@ -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);
|
|
@ -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 {
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue