Browse Source

Derive StatelessSession from primary Session in HibernateJpaDialect

This is analogous to LocalSessionFactoryBean's SpringSessionContext.

See gh-36025
pull/36273/head
Juergen Hoeller 1 month ago
parent
commit
3666ad9d7f
  1. 22
      spring-orm/src/main/java/org/springframework/orm/jpa/DefaultJpaDialect.java
  2. 10
      spring-orm/src/main/java/org/springframework/orm/jpa/EntityManagerFactoryUtils.java
  3. 26
      spring-orm/src/main/java/org/springframework/orm/jpa/vendor/HibernateJpaDialect.java
  4. 2
      spring-orm/src/main/java/org/springframework/orm/jpa/vendor/HibernateJpaVendorAdapter.java

22
spring-orm/src/main/java/org/springframework/orm/jpa/DefaultJpaDialect.java

@ -18,6 +18,7 @@ package org.springframework.orm.jpa; @@ -18,6 +18,7 @@ package org.springframework.orm.jpa;
import java.io.Serializable;
import java.sql.SQLException;
import java.util.Map;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceException;
@ -113,6 +114,27 @@ public class DefaultJpaDialect implements JpaDialect, Serializable { @@ -113,6 +114,27 @@ public class DefaultJpaDialect implements JpaDialect, Serializable {
throws PersistenceException, SQLException {
}
/**
* Derive a new {@code EntityAgent} from the given {@code EntityManager} if possible,
* sharing the transactional context.
* <p><b>NOTE: This method is designed for Spring's early JPA 4.0 support.</b>
* For JPA 3.2 compatibility, the return type cannot be enforced as
* {@code jakarta.persistence.EntityAgent}. Subclasses should override it with
* their specific EntityAgent type if possible, or otherwise just EntityAgent,
* as a covariant return type. This will make it forward-compatible with a
* future variant of this method in the {@link JpaDialect} interface itself.
* @param entityManager the current JPA EntityManager
* @param properties the properties for the EntityAgent, if any
* @return the new EntityAgent instance, or {@code null} if none can be derived
* @throws jakarta.persistence.PersistenceException if thrown by JPA methods
* @since 7.0.4
*/
public @Nullable Object deriveEntityAgent(EntityManager entityManager, @Nullable Map<?, ?> properties)
throws PersistenceException {
return null;
}
//-----------------------------------------------------------------------------------
// Hook for exception translation (used by JpaTransactionManager)

10
spring-orm/src/main/java/org/springframework/orm/jpa/EntityManagerFactoryUtils.java

@ -334,7 +334,15 @@ public abstract class EntityManagerFactoryUtils { @@ -334,7 +334,15 @@ public abstract class EntityManagerFactoryUtils {
// Create a new EntityManager for use within the current transaction.
logger.debug("Opening JPA EntityAgent");
Object entityAgent = createEntityAgent(emf, properties);
Object entityAgent = null;
if (emHolder != null && emf instanceof EntityManagerFactoryInfo info &&
info.getJpaDialect() instanceof DefaultJpaDialect defaultJpaDialect) {
// For JpaTransactionManager: share transaction context with primary EntityManager.
entityAgent = defaultJpaDialect.deriveEntityAgent(emHolder.getEntityManager(), properties);
}
if (entityAgent == null) {
entityAgent = createEntityAgent(emf, properties);
}
if (emHolder != null) {
emHolder.setEntityAgent(entityAgent);

26
spring-orm/src/main/java/org/springframework/orm/jpa/vendor/HibernateJpaDialect.java vendored

@ -16,8 +16,10 @@ @@ -16,8 +16,10 @@
package org.springframework.orm.jpa.vendor;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Map;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceException;
@ -25,6 +27,7 @@ import org.hibernate.ConnectionReleaseMode; @@ -25,6 +27,7 @@ import org.hibernate.ConnectionReleaseMode;
import org.hibernate.FlushMode;
import org.hibernate.JDBCException;
import org.hibernate.Session;
import org.hibernate.StatelessSession;
import org.hibernate.engine.spi.SessionImplementor;
import org.jspecify.annotations.Nullable;
@ -39,10 +42,12 @@ import org.springframework.transaction.InvalidIsolationLevelException; @@ -39,10 +42,12 @@ 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.ClassUtils;
import org.springframework.util.ReflectionUtils;
/**
* {@link org.springframework.orm.jpa.JpaDialect} implementation for Hibernate.
* Compatible with Hibernate ORM 7.x.
* Compatible with Hibernate ORM 7.x and 8.x.
*
* @author Juergen Hoeller
* @author Costin Leau
@ -54,6 +59,10 @@ import org.springframework.transaction.support.ResourceTransactionDefinition; @@ -54,6 +59,10 @@ import org.springframework.transaction.support.ResourceTransactionDefinition;
@SuppressWarnings("serial")
public class HibernateJpaDialect extends DefaultJpaDialect {
/** Hibernate 8.0: inherited setProperty(String, Object) method from JPA 4.0 EntityAgent. */
private static final @Nullable Method STATELESS_SESSION_SET_PROPERTY = ClassUtils.getMethodIfAvailable(
StatelessSession.class, "setProperty", String.class, Object.class);
private final HibernateExceptionTranslator exceptionTranslator = new HibernateExceptionTranslator();
boolean prepareConnection = true;
@ -194,6 +203,21 @@ public class HibernateJpaDialect extends DefaultJpaDialect { @@ -194,6 +203,21 @@ public class HibernateJpaDialect extends DefaultJpaDialect {
return new HibernateConnectionHandle(entityManager.unwrap(SessionImplementor.class));
}
@Override
public StatelessSession deriveEntityAgent(EntityManager entityManager, @Nullable Map<?, ?> properties)
throws PersistenceException {
StatelessSession entityAgent = entityManager.unwrap(Session.class).statelessWithOptions().connection().open();
if (properties != null && STATELESS_SESSION_SET_PROPERTY != null) {
for (Map.Entry<?, ?> entry : properties.entrySet()) {
if (entry.getKey() instanceof String key) {
ReflectionUtils.invokeMethod(STATELESS_SESSION_SET_PROPERTY, entityAgent, key, entry.getValue());
}
}
}
return entityAgent;
}
@Override
public @Nullable DataAccessException translateExceptionIfPossible(RuntimeException ex) {
return this.exceptionTranslator.translateExceptionIfPossible(ex);

2
spring-orm/src/main/java/org/springframework/orm/jpa/vendor/HibernateJpaVendorAdapter.java vendored

@ -50,7 +50,7 @@ import org.springframework.transaction.jta.JtaTransactionManager; @@ -50,7 +50,7 @@ import org.springframework.transaction.jta.JtaTransactionManager;
/**
* {@link org.springframework.orm.jpa.JpaVendorAdapter} implementation for Hibernate.
* Compatible with Hibernate ORM 7.x.
* Compatible with Hibernate ORM 7.x and 8.x.
*
* <p>Exposes Hibernate's persistence provider and Hibernate's Session as extended
* EntityManager interface, and adapts {@link AbstractJpaVendorAdapter}'s common

Loading…
Cancel
Save