HibernateJpaVendorAdapter preserves connection release mode for JTA

Issue: SPR-16162
This commit is contained in:
Juergen Hoeller 2017-11-10 20:21:03 +01:00
parent de782026c4
commit a80fd9994a
5 changed files with 126 additions and 70 deletions

View File

@ -280,8 +280,8 @@ public abstract class AbstractEntityManagerFactoryBean implements
}
/**
* Return the JpaVendorAdapter implementation for this
* EntityManagerFactory, or {@code null} if not known.
* Return the JpaVendorAdapter implementation for this EntityManagerFactory,
* or {@code null} if not known.
*/
@Nullable
public JpaVendorAdapter getJpaVendorAdapter() {
@ -335,13 +335,16 @@ public abstract class AbstractEntityManagerFactoryBean implements
@Override
public final void afterPropertiesSet() throws PersistenceException {
if (this.jpaVendorAdapter != null) {
public void afterPropertiesSet() throws PersistenceException {
JpaVendorAdapter jpaVendorAdapter = getJpaVendorAdapter();
if (jpaVendorAdapter != null) {
if (this.persistenceProvider == null) {
this.persistenceProvider = this.jpaVendorAdapter.getPersistenceProvider();
this.persistenceProvider = jpaVendorAdapter.getPersistenceProvider();
}
Map<String, ?> vendorPropertyMap = this.jpaVendorAdapter.getJpaPropertyMap();
if (vendorPropertyMap != null) {
PersistenceUnitInfo pui = getPersistenceUnitInfo();
Map<String, ?> vendorPropertyMap = (pui != null ? jpaVendorAdapter.getJpaPropertyMap(pui) :
jpaVendorAdapter.getJpaPropertyMap());
if (!CollectionUtils.isEmpty(vendorPropertyMap)) {
vendorPropertyMap.forEach((key, value) -> {
if (!this.jpaPropertyMap.containsKey(key)) {
this.jpaPropertyMap.put(key, value);
@ -349,25 +352,25 @@ public abstract class AbstractEntityManagerFactoryBean implements
});
}
if (this.entityManagerFactoryInterface == null) {
this.entityManagerFactoryInterface = this.jpaVendorAdapter.getEntityManagerFactoryInterface();
this.entityManagerFactoryInterface = jpaVendorAdapter.getEntityManagerFactoryInterface();
if (!ClassUtils.isVisible(this.entityManagerFactoryInterface, this.beanClassLoader)) {
this.entityManagerFactoryInterface = EntityManagerFactory.class;
}
}
if (this.entityManagerInterface == null) {
this.entityManagerInterface = this.jpaVendorAdapter.getEntityManagerInterface();
this.entityManagerInterface = jpaVendorAdapter.getEntityManagerInterface();
if (!ClassUtils.isVisible(this.entityManagerInterface, this.beanClassLoader)) {
this.entityManagerInterface = EntityManager.class;
}
}
if (this.jpaDialect == null) {
this.jpaDialect = this.jpaVendorAdapter.getJpaDialect();
this.jpaDialect = jpaVendorAdapter.getJpaDialect();
}
}
if (this.bootstrapExecutor != null) {
this.nativeEntityManagerFactoryFuture = this.bootstrapExecutor.submit(
this::buildNativeEntityManagerFactory);
AsyncTaskExecutor bootstrapExecutor = getBootstrapExecutor();
if (bootstrapExecutor != null) {
this.nativeEntityManagerFactoryFuture = bootstrapExecutor.submit(this::buildNativeEntityManagerFactory);
}
else {
this.nativeEntityManagerFactory = buildNativeEntityManagerFactory();
@ -382,8 +385,9 @@ public abstract class AbstractEntityManagerFactoryBean implements
private EntityManagerFactory buildNativeEntityManagerFactory() {
EntityManagerFactory emf = createNativeEntityManagerFactory();
if (this.jpaVendorAdapter != null) {
this.jpaVendorAdapter.postProcessEntityManagerFactory(emf);
JpaVendorAdapter jpaVendorAdapter = getJpaVendorAdapter();
if (jpaVendorAdapter != null) {
jpaVendorAdapter.postProcessEntityManagerFactory(emf);
}
if (logger.isInfoEnabled()) {
logger.info("Initialized JPA EntityManagerFactory for persistence unit '" + getPersistenceUnitName() + "'");
@ -401,8 +405,9 @@ public abstract class AbstractEntityManagerFactoryBean implements
*/
protected EntityManagerFactory createEntityManagerFactoryProxy(@Nullable EntityManagerFactory emf) {
Set<Class<?>> ifcs = new LinkedHashSet<>();
if (this.entityManagerFactoryInterface != null) {
ifcs.add(this.entityManagerFactoryInterface);
Class<?> entityManagerFactoryInterface = this.entityManagerFactoryInterface;
if (entityManagerFactoryInterface != null) {
ifcs.add(entityManagerFactoryInterface);
}
else if (emf != null) {
ifcs.addAll(ClassUtils.getAllInterfacesForClassAsSet(emf.getClass(), this.beanClassLoader));
@ -417,8 +422,8 @@ public abstract class AbstractEntityManagerFactoryBean implements
new ManagedEntityManagerFactoryInvocationHandler(this));
}
catch (IllegalArgumentException ex) {
if (this.entityManagerFactoryInterface != null) {
throw new IllegalStateException("EntityManagerFactory interface [" + this.entityManagerFactoryInterface +
if (entityManagerFactoryInterface != null) {
throw new IllegalStateException("EntityManagerFactory interface [" + entityManagerFactoryInterface +
"] seems to conflict with Spring's EntityManagerFactoryInfo mixin - consider resetting the "+
"'entityManagerFactoryInterface' property to plain [javax.persistence.EntityManagerFactory]", ex);
}
@ -497,7 +502,8 @@ public abstract class AbstractEntityManagerFactoryBean implements
@Override
@Nullable
public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
return (this.jpaDialect != null ? this.jpaDialect.translateExceptionIfPossible(ex) :
JpaDialect jpaDialect = getJpaDialect();
return (jpaDialect != null ? jpaDialect.translateExceptionIfPossible(ex) :
EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(ex));
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2017 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.
@ -16,11 +16,12 @@
package org.springframework.orm.jpa;
import java.util.Collections;
import java.util.Map;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.spi.PersistenceProvider;
import javax.persistence.spi.PersistenceUnitInfo;
import org.springframework.lang.Nullable;
@ -48,29 +49,53 @@ public interface JpaVendorAdapter {
* @since 2.5.2
*/
@Nullable
String getPersistenceProviderRootPackage();
default String getPersistenceProviderRootPackage() {
return null;
}
/**
* Return a Map of vendor-specific JPA properties for the given persistence
* unit, typically based on settings in this JpaVendorAdapter instance.
* <p>Note that there might be further JPA properties defined on the
* EntityManagerFactory bean, which might potentially override individual
* JPA property values specified here.
* <p>This implementation delegates to {@link #getJpaPropertyMap()} for
* non-unit-dependent properties. Effectively, this PersistenceUnitInfo-based
* variant only needs to be implemented if there is an actual need to react
* to unit-specific characteristics such as the transaction type.
* @param pui the PersistenceUnitInfo for the current persistence unit
* @return a Map of JPA properties, as accepted by the standard JPA bootstrap
* facilities, or an empty Map if there are no properties to expose
* @since 4.3.13
* @see PersistenceUnitInfo#getTransactionType()
* @see javax.persistence.spi.PersistenceProvider#createContainerEntityManagerFactory(PersistenceUnitInfo, Map)
*/
default Map<String, ?> getJpaPropertyMap(PersistenceUnitInfo pui) {
return getJpaPropertyMap();
}
/**
* Return a Map of vendor-specific JPA properties,
* typically based on settings in this JpaVendorAdapter instance.
* <p>Note that there might be further JPA properties defined on
* the EntityManagerFactory bean, which might potentially override
* individual JPA property values specified here.
* @return a Map of JPA properties, as accepted by the standard
* JPA bootstrap facilities, or {@code null} or an empty Map
* if there are no such properties to expose
* @see javax.persistence.Persistence#createEntityManagerFactory(String, java.util.Map)
* @see javax.persistence.spi.PersistenceProvider#createContainerEntityManagerFactory(javax.persistence.spi.PersistenceUnitInfo, java.util.Map)
* <p>Note that there might be further JPA properties defined on the
* EntityManagerFactory bean, which might potentially override individual
* JPA property values specified here.
* @return a Map of JPA properties, as accepted by the standard JPA bootstrap
* facilities, or an empty Map if there are no properties to expose
* @see javax.persistence.Persistence#createEntityManagerFactory(String, Map)
*/
@Nullable
Map<String, ?> getJpaPropertyMap();
default Map<String, ?> getJpaPropertyMap() {
return Collections.emptyMap();
}
/**
* Return the vendor-specific JpaDialect implementation for this
* provider, or {@code null} if there is none.
*/
@Nullable
JpaDialect getJpaDialect();
default JpaDialect getJpaDialect() {
return null;
}
/**
* Return the vendor-specific EntityManagerFactory interface
@ -80,7 +105,9 @@ public interface JpaVendorAdapter {
* {@link javax.persistence.EntityManagerFactory} class here.
* @since 2.5.2
*/
Class<? extends EntityManagerFactory> getEntityManagerFactoryInterface();
default Class<? extends EntityManagerFactory> getEntityManagerFactoryInterface() {
return EntityManagerFactory.class;
}
/**
* Return the vendor-specific EntityManager interface
@ -89,7 +116,9 @@ public interface JpaVendorAdapter {
* the adapter should simply return the standard
* {@link javax.persistence.EntityManager} class here.
*/
Class<? extends EntityManager> getEntityManagerInterface();
default Class<? extends EntityManager> getEntityManagerInterface() {
return EntityManager.class;
}
/**
* Optional callback for post-processing the native EntityManagerFactory
@ -98,6 +127,7 @@ public interface JpaVendorAdapter {
* While this is not expected to be used for most providers, it is included
* here as a general extension hook.
*/
void postProcessEntityManagerFactory(EntityManagerFactory emf);
default void postProcessEntityManagerFactory(EntityManagerFactory emf) {
}
}

View File

@ -35,6 +35,7 @@ import org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager
import org.springframework.orm.jpa.persistenceunit.PersistenceUnitManager;
import org.springframework.orm.jpa.persistenceunit.PersistenceUnitPostProcessor;
import org.springframework.orm.jpa.persistenceunit.SmartPersistenceUnitInfo;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
@ -92,8 +93,7 @@ public class LocalContainerEntityManagerFactoryBean extends AbstractEntityManage
@Nullable
private PersistenceUnitManager persistenceUnitManager;
private final DefaultPersistenceUnitManager internalPersistenceUnitManager =
new DefaultPersistenceUnitManager();
private final DefaultPersistenceUnitManager internalPersistenceUnitManager = new DefaultPersistenceUnitManager();
@Nullable
private PersistenceUnitInfo persistenceUnitInfo;
@ -322,7 +322,7 @@ public class LocalContainerEntityManagerFactoryBean extends AbstractEntityManage
@Override
protected EntityManagerFactory createNativeEntityManagerFactory() throws PersistenceException {
public void afterPropertiesSet() throws PersistenceException {
PersistenceUnitManager managerToUse = this.persistenceUnitManager;
if (this.persistenceUnitManager == null) {
this.internalPersistenceUnitManager.afterPropertiesSet();
@ -338,6 +338,13 @@ public class LocalContainerEntityManagerFactoryBean extends AbstractEntityManage
}
}
super.afterPropertiesSet();
}
@Override
protected EntityManagerFactory createNativeEntityManagerFactory() throws PersistenceException {
Assert.state(this.persistenceUnitInfo != null, "PersistenceUnitInfo not initialized");
PersistenceProvider provider = getPersistenceProvider();
if (provider == null) {
String providerClassName = this.persistenceUnitInfo.getPersistenceProviderClassName();

View File

@ -16,9 +16,11 @@
package org.springframework.orm.jpa.vendor;
import java.util.Collections;
import java.util.Map;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.spi.PersistenceUnitInfo;
import org.springframework.lang.Nullable;
import org.springframework.orm.jpa.JpaDialect;
@ -124,9 +126,13 @@ public abstract class AbstractJpaVendorAdapter implements JpaVendorAdapter {
}
@Override
@Nullable
public Map<String, ?> getJpaPropertyMap(PersistenceUnitInfo pui) {
return getJpaPropertyMap();
}
@Override
public Map<String, ?> getJpaPropertyMap() {
return null;
return Collections.emptyMap();
}
@Override
@ -145,10 +151,6 @@ public abstract class AbstractJpaVendorAdapter implements JpaVendorAdapter {
return EntityManager.class;
}
/**
* Post-process the EntityManagerFactory after it has been initialized.
* @param emf the EntityManagerFactory to process
*/
@Override
public void postProcessEntityManagerFactory(EntityManagerFactory emf) {
}

View File

@ -21,6 +21,8 @@ import java.util.Map;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.spi.PersistenceProvider;
import javax.persistence.spi.PersistenceUnitInfo;
import javax.persistence.spi.PersistenceUnitTransactionType;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.dialect.DB2Dialect;
@ -85,13 +87,15 @@ public class HibernateJpaVendorAdapter extends AbstractJpaVendorAdapter {
* new connection handling mode {@code DELAYED_ACQUISITION_AND_HOLD} in that case
* unless a user-specified connection handling mode property indicates otherwise;
* switch this flag to {@code false} to avoid that interference.
* <p><b>NOTE: Per the explanation above, you may have to turn this flag off
* when using Hibernate in a JTA environment, e.g. on WebLogic.</b> Alternatively,
* set Hibernate 5.2's "hibernate.connection.handling_mode" property to
* "DELAYED_ACQUISITION_AND_RELEASE_AFTER_TRANSACTION" or even
* <p><b>NOTE: For a persistence unit with transaction type JTA e.g. on WebLogic,
* the connection release mode will never be altered from its provider default,
* i.e. not be forced to {@code DELAYED_ACQUISITION_AND_HOLD} by this flag.</b>
* Alternatively, set Hibernate 5.2's "hibernate.connection.handling_mode"
* property to "DELAYED_ACQUISITION_AND_RELEASE_AFTER_TRANSACTION" or even
* "DELAYED_ACQUISITION_AND_RELEASE_AFTER_STATEMENT" in such a scenario.
* @since 4.3.1
* @see #getJpaPropertyMap()
* @see PersistenceUnitInfo#getTransactionType()
* @see #getJpaPropertyMap(PersistenceUnitInfo)
* @see HibernateJpaDialect#beginTransaction
*/
public void setPrepareConnection(boolean prepareConnection) {
@ -109,6 +113,32 @@ public class HibernateJpaVendorAdapter extends AbstractJpaVendorAdapter {
return "org.hibernate";
}
@Override
public Map<String, Object> getJpaPropertyMap(PersistenceUnitInfo pui) {
Map<String, Object> jpaProperties = getJpaPropertyMap();
if (this.jpaDialect.prepareConnection && pui.getTransactionType() != PersistenceUnitTransactionType.JTA) {
// Hibernate 5.1/5.2: manually enforce connection release mode ON_CLOSE (the former default)
try {
// Try Hibernate 5.2
AvailableSettings.class.getField("CONNECTION_HANDLING");
jpaProperties.put("hibernate.connection.handling_mode", "DELAYED_ACQUISITION_AND_HOLD");
}
catch (NoSuchFieldException ex) {
// Try Hibernate 5.1
try {
AvailableSettings.class.getField("ACQUIRE_CONNECTIONS");
jpaProperties.put("hibernate.connection.release_mode", "ON_CLOSE");
}
catch (NoSuchFieldException ex2) {
// on Hibernate 5.0.x or lower - no need to change the default there
}
}
}
return jpaProperties;
}
@Override
public Map<String, Object> getJpaPropertyMap() {
Map<String, Object> jpaProperties = new HashMap<>();
@ -130,25 +160,6 @@ public class HibernateJpaVendorAdapter extends AbstractJpaVendorAdapter {
jpaProperties.put(AvailableSettings.SHOW_SQL, "true");
}
if (this.jpaDialect.prepareConnection) {
// Hibernate 5.1/5.2: manually enforce connection release mode ON_CLOSE (the former default)
try {
// Try Hibernate 5.2
AvailableSettings.class.getField("CONNECTION_HANDLING");
jpaProperties.put("hibernate.connection.handling_mode", "DELAYED_ACQUISITION_AND_HOLD");
}
catch (NoSuchFieldException ex) {
// Try Hibernate 5.1
try {
AvailableSettings.class.getField("ACQUIRE_CONNECTIONS");
jpaProperties.put("hibernate.connection.release_mode", "ON_CLOSE");
}
catch (NoSuchFieldException ex2) {
// on Hibernate 5.0.x or lower - no need to change the default there
}
}
}
return jpaProperties;
}