|
|
|
@ -16,14 +16,13 @@ |
|
|
|
|
|
|
|
|
|
|
|
package org.springframework.orm.jpa.vendor; |
|
|
|
package org.springframework.orm.jpa.vendor; |
|
|
|
|
|
|
|
|
|
|
|
import java.lang.reflect.Method; |
|
|
|
|
|
|
|
import java.sql.Connection; |
|
|
|
import java.sql.Connection; |
|
|
|
import java.sql.SQLException; |
|
|
|
import java.sql.SQLException; |
|
|
|
|
|
|
|
|
|
|
|
import javax.persistence.EntityManager; |
|
|
|
import javax.persistence.EntityManager; |
|
|
|
import javax.persistence.PersistenceException; |
|
|
|
import javax.persistence.PersistenceException; |
|
|
|
|
|
|
|
|
|
|
|
import org.apache.commons.logging.LogFactory; |
|
|
|
import org.hibernate.ConnectionReleaseMode; |
|
|
|
import org.hibernate.FlushMode; |
|
|
|
import org.hibernate.FlushMode; |
|
|
|
import org.hibernate.HibernateException; |
|
|
|
import org.hibernate.HibernateException; |
|
|
|
import org.hibernate.JDBCException; |
|
|
|
import org.hibernate.JDBCException; |
|
|
|
@ -43,6 +42,7 @@ import org.hibernate.UnresolvableObjectException; |
|
|
|
import org.hibernate.WrongClassException; |
|
|
|
import org.hibernate.WrongClassException; |
|
|
|
import org.hibernate.dialect.lock.OptimisticEntityLockException; |
|
|
|
import org.hibernate.dialect.lock.OptimisticEntityLockException; |
|
|
|
import org.hibernate.dialect.lock.PessimisticEntityLockException; |
|
|
|
import org.hibernate.dialect.lock.PessimisticEntityLockException; |
|
|
|
|
|
|
|
import org.hibernate.engine.spi.SessionImplementor; |
|
|
|
import org.hibernate.exception.ConstraintViolationException; |
|
|
|
import org.hibernate.exception.ConstraintViolationException; |
|
|
|
import org.hibernate.exception.DataException; |
|
|
|
import org.hibernate.exception.DataException; |
|
|
|
import org.hibernate.exception.JDBCConnectionException; |
|
|
|
import org.hibernate.exception.JDBCConnectionException; |
|
|
|
@ -71,12 +71,10 @@ import org.springframework.transaction.InvalidIsolationLevelException; |
|
|
|
import org.springframework.transaction.TransactionDefinition; |
|
|
|
import org.springframework.transaction.TransactionDefinition; |
|
|
|
import org.springframework.transaction.TransactionException; |
|
|
|
import org.springframework.transaction.TransactionException; |
|
|
|
import org.springframework.transaction.support.ResourceTransactionDefinition; |
|
|
|
import org.springframework.transaction.support.ResourceTransactionDefinition; |
|
|
|
import org.springframework.util.Assert; |
|
|
|
|
|
|
|
import org.springframework.util.ReflectionUtils; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* {@link org.springframework.orm.jpa.JpaDialect} implementation for |
|
|
|
* {@link org.springframework.orm.jpa.JpaDialect} implementation for |
|
|
|
* Hibernate EntityManager. Developed against Hibernate 5.1/5.2/5.3/5.4. |
|
|
|
* Hibernate EntityManager. Developed against Hibernate 5.2/5.3/5.4. |
|
|
|
* |
|
|
|
* |
|
|
|
* @author Juergen Hoeller |
|
|
|
* @author Juergen Hoeller |
|
|
|
* @author Costin Leau |
|
|
|
* @author Costin Leau |
|
|
|
@ -88,27 +86,6 @@ import org.springframework.util.ReflectionUtils; |
|
|
|
@SuppressWarnings("serial") |
|
|
|
@SuppressWarnings("serial") |
|
|
|
public class HibernateJpaDialect extends DefaultJpaDialect { |
|
|
|
public class HibernateJpaDialect extends DefaultJpaDialect { |
|
|
|
|
|
|
|
|
|
|
|
private static Method getFlushMode; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static { |
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
// Hibernate 5.2+ getHibernateFlushMode()
|
|
|
|
|
|
|
|
getFlushMode = Session.class.getMethod("getHibernateFlushMode"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
catch (NoSuchMethodException ex) { |
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
// Classic Hibernate getFlushMode() with FlushMode return type
|
|
|
|
|
|
|
|
getFlushMode = Session.class.getMethod("getFlushMode"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
catch (NoSuchMethodException ex2) { |
|
|
|
|
|
|
|
throw new IllegalStateException("No compatible Hibernate getFlushMode signature found", ex2); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// Check that it is the Hibernate FlushMode type, not JPA's...
|
|
|
|
|
|
|
|
Assert.state(FlushMode.class == getFlushMode.getReturnType(), "Could not find Hibernate getFlushMode method"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
boolean prepareConnection = true; |
|
|
|
boolean prepareConnection = true; |
|
|
|
|
|
|
|
|
|
|
|
@Nullable |
|
|
|
@Nullable |
|
|
|
@ -159,7 +136,7 @@ public class HibernateJpaDialect extends DefaultJpaDialect { |
|
|
|
public Object beginTransaction(EntityManager entityManager, TransactionDefinition definition) |
|
|
|
public Object beginTransaction(EntityManager entityManager, TransactionDefinition definition) |
|
|
|
throws PersistenceException, SQLException, TransactionException { |
|
|
|
throws PersistenceException, SQLException, TransactionException { |
|
|
|
|
|
|
|
|
|
|
|
Session session = getSession(entityManager); |
|
|
|
SessionImplementor session = getSession(entityManager); |
|
|
|
|
|
|
|
|
|
|
|
if (definition.getTimeout() != TransactionDefinition.TIMEOUT_DEFAULT) { |
|
|
|
if (definition.getTimeout() != TransactionDefinition.TIMEOUT_DEFAULT) { |
|
|
|
session.getTransaction().setTimeout(definition.getTimeout()); |
|
|
|
session.getTransaction().setTimeout(definition.getTimeout()); |
|
|
|
@ -170,13 +147,16 @@ public class HibernateJpaDialect extends DefaultJpaDialect { |
|
|
|
Connection preparedCon = null; |
|
|
|
Connection preparedCon = null; |
|
|
|
|
|
|
|
|
|
|
|
if (isolationLevelNeeded || definition.isReadOnly()) { |
|
|
|
if (isolationLevelNeeded || definition.isReadOnly()) { |
|
|
|
if (this.prepareConnection) { |
|
|
|
if (this.prepareConnection && ConnectionReleaseMode.ON_CLOSE.equals( |
|
|
|
preparedCon = HibernateConnectionHandle.doGetConnection(session); |
|
|
|
session.getJdbcCoordinator().getLogicalConnection().getConnectionHandlingMode().getReleaseMode())) { |
|
|
|
|
|
|
|
preparedCon = session.connection(); |
|
|
|
previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(preparedCon, definition); |
|
|
|
previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(preparedCon, definition); |
|
|
|
} |
|
|
|
} |
|
|
|
else if (isolationLevelNeeded) { |
|
|
|
else if (isolationLevelNeeded) { |
|
|
|
throw new InvalidIsolationLevelException(getClass().getSimpleName() + |
|
|
|
throw new InvalidIsolationLevelException( |
|
|
|
" does not support custom isolation levels since the 'prepareConnection' flag is off."); |
|
|
|
"HibernateJpaDialect is not allowed to support custom isolation levels: " + |
|
|
|
|
|
|
|
"make sure that its 'prepareConnection' flag is on (the default) and that the " + |
|
|
|
|
|
|
|
"Hibernate connection release mode is set to ON_CLOSE."); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@ -195,34 +175,32 @@ public class HibernateJpaDialect extends DefaultJpaDialect { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
return new SessionTransactionData( |
|
|
|
return new SessionTransactionData( |
|
|
|
session, previousFlushMode, preparedCon, previousIsolationLevel, definition.isReadOnly()); |
|
|
|
session, previousFlushMode, (preparedCon != null), previousIsolationLevel, definition.isReadOnly()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public Object prepareTransaction(EntityManager entityManager, boolean readOnly, @Nullable String name) |
|
|
|
public Object prepareTransaction(EntityManager entityManager, boolean readOnly, @Nullable String name) |
|
|
|
throws PersistenceException { |
|
|
|
throws PersistenceException { |
|
|
|
|
|
|
|
|
|
|
|
Session session = getSession(entityManager); |
|
|
|
SessionImplementor session = getSession(entityManager); |
|
|
|
FlushMode previousFlushMode = prepareFlushMode(session, readOnly); |
|
|
|
FlushMode previousFlushMode = prepareFlushMode(session, readOnly); |
|
|
|
return new SessionTransactionData(session, previousFlushMode, null, null, readOnly); |
|
|
|
return new SessionTransactionData(session, previousFlushMode, false, null, readOnly); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@SuppressWarnings("deprecation") |
|
|
|
|
|
|
|
@Nullable |
|
|
|
@Nullable |
|
|
|
protected FlushMode prepareFlushMode(Session session, boolean readOnly) throws PersistenceException { |
|
|
|
protected FlushMode prepareFlushMode(Session session, boolean readOnly) throws PersistenceException { |
|
|
|
FlushMode flushMode = (FlushMode) ReflectionUtils.invokeMethod(getFlushMode, session); |
|
|
|
FlushMode flushMode = session.getHibernateFlushMode(); |
|
|
|
Assert.state(flushMode != null, "No FlushMode from Session"); |
|
|
|
|
|
|
|
if (readOnly) { |
|
|
|
if (readOnly) { |
|
|
|
// We should suppress flushing for a read-only transaction.
|
|
|
|
// We should suppress flushing for a read-only transaction.
|
|
|
|
if (!flushMode.equals(FlushMode.MANUAL)) { |
|
|
|
if (!flushMode.equals(FlushMode.MANUAL)) { |
|
|
|
session.setFlushMode(FlushMode.MANUAL); |
|
|
|
session.setHibernateFlushMode(FlushMode.MANUAL); |
|
|
|
return flushMode; |
|
|
|
return flushMode; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
|
else { |
|
|
|
// We need AUTO or COMMIT for a non-read-only transaction.
|
|
|
|
// We need AUTO or COMMIT for a non-read-only transaction.
|
|
|
|
if (flushMode.lessThan(FlushMode.COMMIT)) { |
|
|
|
if (flushMode.lessThan(FlushMode.COMMIT)) { |
|
|
|
session.setFlushMode(FlushMode.AUTO); |
|
|
|
session.setHibernateFlushMode(FlushMode.AUTO); |
|
|
|
return flushMode; |
|
|
|
return flushMode; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
@ -241,7 +219,7 @@ public class HibernateJpaDialect extends DefaultJpaDialect { |
|
|
|
public ConnectionHandle getJdbcConnection(EntityManager entityManager, boolean readOnly) |
|
|
|
public ConnectionHandle getJdbcConnection(EntityManager entityManager, boolean readOnly) |
|
|
|
throws PersistenceException, SQLException { |
|
|
|
throws PersistenceException, SQLException { |
|
|
|
|
|
|
|
|
|
|
|
Session session = getSession(entityManager); |
|
|
|
SessionImplementor session = getSession(entityManager); |
|
|
|
return new HibernateConnectionHandle(session); |
|
|
|
return new HibernateConnectionHandle(session); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@ -353,32 +331,31 @@ public class HibernateJpaDialect extends DefaultJpaDialect { |
|
|
|
return new JpaSystemException(ex); |
|
|
|
return new JpaSystemException(ex); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
protected Session getSession(EntityManager entityManager) { |
|
|
|
protected SessionImplementor getSession(EntityManager entityManager) { |
|
|
|
return entityManager.unwrap(Session.class); |
|
|
|
return entityManager.unwrap(SessionImplementor.class); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static class SessionTransactionData { |
|
|
|
private static class SessionTransactionData { |
|
|
|
|
|
|
|
|
|
|
|
private final Session session; |
|
|
|
private final SessionImplementor session; |
|
|
|
|
|
|
|
|
|
|
|
@Nullable |
|
|
|
@Nullable |
|
|
|
private final FlushMode previousFlushMode; |
|
|
|
private final FlushMode previousFlushMode; |
|
|
|
|
|
|
|
|
|
|
|
@Nullable |
|
|
|
private final boolean needsConnectionReset; |
|
|
|
private final Connection preparedCon; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Nullable |
|
|
|
@Nullable |
|
|
|
private final Integer previousIsolationLevel; |
|
|
|
private final Integer previousIsolationLevel; |
|
|
|
|
|
|
|
|
|
|
|
private final boolean readOnly; |
|
|
|
private final boolean readOnly; |
|
|
|
|
|
|
|
|
|
|
|
public SessionTransactionData(Session session, @Nullable FlushMode previousFlushMode, |
|
|
|
public SessionTransactionData(SessionImplementor session, @Nullable FlushMode previousFlushMode, |
|
|
|
@Nullable Connection preparedCon, @Nullable Integer previousIsolationLevel, boolean readOnly) { |
|
|
|
boolean connectionPrepared, @Nullable Integer previousIsolationLevel, boolean readOnly) { |
|
|
|
|
|
|
|
|
|
|
|
this.session = session; |
|
|
|
this.session = session; |
|
|
|
this.previousFlushMode = previousFlushMode; |
|
|
|
this.previousFlushMode = previousFlushMode; |
|
|
|
this.preparedCon = preparedCon; |
|
|
|
this.needsConnectionReset = connectionPrepared; |
|
|
|
this.previousIsolationLevel = previousIsolationLevel; |
|
|
|
this.previousIsolationLevel = previousIsolationLevel; |
|
|
|
this.readOnly = readOnly; |
|
|
|
this.readOnly = readOnly; |
|
|
|
} |
|
|
|
} |
|
|
|
@ -388,14 +365,9 @@ public class HibernateJpaDialect extends DefaultJpaDialect { |
|
|
|
if (this.previousFlushMode != null) { |
|
|
|
if (this.previousFlushMode != null) { |
|
|
|
this.session.setFlushMode(this.previousFlushMode); |
|
|
|
this.session.setFlushMode(this.previousFlushMode); |
|
|
|
} |
|
|
|
} |
|
|
|
if (this.preparedCon != null && this.session.isConnected()) { |
|
|
|
if (this.needsConnectionReset && |
|
|
|
Connection conToReset = HibernateConnectionHandle.doGetConnection(this.session); |
|
|
|
this.session.getJdbcCoordinator().getLogicalConnection().isPhysicallyConnected()) { |
|
|
|
if (conToReset != this.preparedCon) { |
|
|
|
Connection conToReset = this.session.connection(); |
|
|
|
LogFactory.getLog(HibernateJpaDialect.class).warn( |
|
|
|
|
|
|
|
"JDBC Connection to reset not identical to originally prepared Connection - please " + |
|
|
|
|
|
|
|
"make sure to use connection release mode ON_CLOSE (the default) and to run against " + |
|
|
|
|
|
|
|
"Hibernate 4.2+ (or switch HibernateJpaDialect's prepareConnection flag to false"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
DataSourceUtils.resetConnectionAfterTransaction( |
|
|
|
DataSourceUtils.resetConnectionAfterTransaction( |
|
|
|
conToReset, this.previousIsolationLevel, this.readOnly); |
|
|
|
conToReset, this.previousIsolationLevel, this.readOnly); |
|
|
|
} |
|
|
|
} |
|
|
|
@ -405,35 +377,15 @@ public class HibernateJpaDialect extends DefaultJpaDialect { |
|
|
|
|
|
|
|
|
|
|
|
private static class HibernateConnectionHandle implements ConnectionHandle { |
|
|
|
private static class HibernateConnectionHandle implements ConnectionHandle { |
|
|
|
|
|
|
|
|
|
|
|
@Nullable |
|
|
|
private final SessionImplementor session; |
|
|
|
private static volatile Method connectionMethodToUse; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private final Session session; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public HibernateConnectionHandle(Session session) { |
|
|
|
public HibernateConnectionHandle(SessionImplementor session) { |
|
|
|
this.session = session; |
|
|
|
this.session = session; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public Connection getConnection() { |
|
|
|
public Connection getConnection() { |
|
|
|
return doGetConnection(this.session); |
|
|
|
return this.session.connection(); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static Connection doGetConnection(Session session) { |
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
Method methodToUse = connectionMethodToUse; |
|
|
|
|
|
|
|
if (methodToUse == null) { |
|
|
|
|
|
|
|
// Reflective lookup to find SessionImpl's connection() method on Hibernate 4.x/5.x
|
|
|
|
|
|
|
|
methodToUse = session.getClass().getMethod("connection"); |
|
|
|
|
|
|
|
connectionMethodToUse = methodToUse; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
Connection con = (Connection) ReflectionUtils.invokeMethod(methodToUse, session); |
|
|
|
|
|
|
|
Assert.state(con != null, "No Connection from Session"); |
|
|
|
|
|
|
|
return con; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
catch (NoSuchMethodException ex) { |
|
|
|
|
|
|
|
throw new IllegalStateException("Cannot find connection() method on Hibernate Session", ex); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|