Register SpringBeanContainer with default Hibernate setup

Closes gh-13717
This commit is contained in:
Stephane Nicoll 2018-10-02 16:41:02 +02:00
parent 7bf32290c6
commit 068040e227
6 changed files with 106 additions and 10 deletions

View File

@ -16,10 +16,10 @@
package org.springframework.boot.autoconfigure.orm.jpa; package org.springframework.boot.autoconfigure.orm.jpa;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Supplier; import java.util.function.Supplier;
@ -31,8 +31,10 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.hibernate.boot.model.naming.ImplicitNamingStrategy; import org.hibernate.boot.model.naming.ImplicitNamingStrategy;
import org.hibernate.boot.model.naming.PhysicalNamingStrategy; import org.hibernate.boot.model.naming.PhysicalNamingStrategy;
import org.hibernate.cfg.AvailableSettings;
import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers; import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
@ -43,6 +45,7 @@ import org.springframework.boot.jdbc.metadata.DataSourcePoolMetadataProvider;
import org.springframework.boot.orm.jpa.hibernate.SpringJtaPlatform; import org.springframework.boot.orm.jpa.hibernate.SpringJtaPlatform;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.jndi.JndiLocatorDelegate; import org.springframework.jndi.JndiLocatorDelegate;
import org.springframework.orm.hibernate5.SpringBeanContainer;
import org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter; import org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.jta.JtaTransactionManager; import org.springframework.transaction.jta.JtaTransactionManager;
@ -85,6 +88,7 @@ class HibernateJpaConfiguration extends JpaBaseConfiguration {
private final List<HibernatePropertiesCustomizer> hibernatePropertiesCustomizers; private final List<HibernatePropertiesCustomizer> hibernatePropertiesCustomizers;
HibernateJpaConfiguration(DataSource dataSource, JpaProperties jpaProperties, HibernateJpaConfiguration(DataSource dataSource, JpaProperties jpaProperties,
ConfigurableListableBeanFactory beanFactory,
ObjectProvider<JtaTransactionManager> jtaTransactionManager, ObjectProvider<JtaTransactionManager> jtaTransactionManager,
ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers, ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers,
HibernateProperties hibernateProperties, HibernateProperties hibernateProperties,
@ -101,22 +105,25 @@ class HibernateJpaConfiguration extends JpaBaseConfiguration {
metadataProviders.getIfAvailable()); metadataProviders.getIfAvailable());
this.hibernatePropertiesCustomizers = determineHibernatePropertiesCustomizers( this.hibernatePropertiesCustomizers = determineHibernatePropertiesCustomizers(
physicalNamingStrategy.getIfAvailable(), physicalNamingStrategy.getIfAvailable(),
implicitNamingStrategy.getIfAvailable(), hibernatePropertiesCustomizers implicitNamingStrategy.getIfAvailable(), beanFactory,
.orderedStream().collect(Collectors.toList())); hibernatePropertiesCustomizers.orderedStream()
.collect(Collectors.toList()));
} }
private List<HibernatePropertiesCustomizer> determineHibernatePropertiesCustomizers( private List<HibernatePropertiesCustomizer> determineHibernatePropertiesCustomizers(
PhysicalNamingStrategy physicalNamingStrategy, PhysicalNamingStrategy physicalNamingStrategy,
ImplicitNamingStrategy implicitNamingStrategy, ImplicitNamingStrategy implicitNamingStrategy,
ConfigurableListableBeanFactory beanFactory,
List<HibernatePropertiesCustomizer> hibernatePropertiesCustomizers) { List<HibernatePropertiesCustomizer> hibernatePropertiesCustomizers) {
List<HibernatePropertiesCustomizer> customizers = new ArrayList<>();
customizers.add((properties) -> properties.put(AvailableSettings.BEAN_CONTAINER,
new SpringBeanContainer(beanFactory)));
if (physicalNamingStrategy != null || implicitNamingStrategy != null) { if (physicalNamingStrategy != null || implicitNamingStrategy != null) {
LinkedList<HibernatePropertiesCustomizer> customizers = new LinkedList<>( customizers.add(new NamingStrategiesHibernatePropertiesCustomizer(
hibernatePropertiesCustomizers);
customizers.addFirst(new NamingStrategiesHibernatePropertiesCustomizer(
physicalNamingStrategy, implicitNamingStrategy)); physicalNamingStrategy, implicitNamingStrategy));
return customizers;
} }
return hibernatePropertiesCustomizers; customizers.addAll(hibernatePropertiesCustomizers);
return customizers;
} }
@Override @Override

View File

@ -35,6 +35,7 @@ import javax.transaction.UserTransaction;
import com.zaxxer.hikari.HikariDataSource; import com.zaxxer.hikari.HikariDataSource;
import org.hibernate.boot.model.naming.ImplicitNamingStrategy; import org.hibernate.boot.model.naming.ImplicitNamingStrategy;
import org.hibernate.boot.model.naming.PhysicalNamingStrategy; import org.hibernate.boot.model.naming.PhysicalNamingStrategy;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform; import org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform;
import org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform; import org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform;
import org.hibernate.internal.SessionFactoryImpl; import org.hibernate.internal.SessionFactoryImpl;
@ -361,6 +362,27 @@ public class HibernateJpaAutoConfigurationTests
}); });
} }
@Test
public void eventListenerCanBeRegisteredAsBeans() {
contextRunner().withUserConfiguration(TestInitializedJpaConfiguration.class)
.withClassLoader(new HideDataScriptClassLoader())
.withPropertyValues("spring.jpa.show-sql=true",
"spring.jpa.hibernate.ddl-auto:create-drop",
"spring.datasource.data:classpath:/city.sql")
.run((context) -> {
// See CityListener
assertThat(context).hasSingleBean(City.class);
assertThat(context.getBean(City.class).getName())
.isEqualTo("Washington");
});
}
@Test
public void hibernatePropertiesCustomizerCanDisableBeanContainer() {
contextRunner().withUserConfiguration(DisableBeanContainerConfiguration.class)
.run((context) -> assertThat(context).doesNotHaveBean(City.class));
}
@Configuration @Configuration
@TestAutoConfigurationPackage(City.class) @TestAutoConfigurationPackage(City.class)
static class TestInitializedJpaConfiguration { static class TestInitializedJpaConfiguration {
@ -420,6 +442,17 @@ public class HibernateJpaAutoConfigurationTests
} }
@Configuration
static class DisableBeanContainerConfiguration {
@Bean
public HibernatePropertiesCustomizer disableBeanContainerHibernatePropertiesCustomizer() {
return (hibernateProperties) -> hibernateProperties
.remove(AvailableSettings.BEAN_CONTAINER);
}
}
public static class TestJtaPlatform implements JtaPlatform { public static class TestJtaPlatform implements JtaPlatform {
@Override @Override

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2017 the original author or authors. * Copyright 2012-2018 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -20,10 +20,12 @@ import java.io.Serializable;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.GeneratedValue; import javax.persistence.GeneratedValue;
import javax.persistence.Id; import javax.persistence.Id;
@Entity @Entity
@EntityListeners(CityListener.class)
public class City implements Serializable { public class City implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;

View File

@ -0,0 +1,43 @@
/*
* Copyright 2012-2018 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
*
* http://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.boot.autoconfigure.orm.jpa.test;
import javax.persistence.PostLoad;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
public class CityListener {
private ConfigurableBeanFactory beanFactory;
public CityListener() {
}
@Autowired
public CityListener(ConfigurableBeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@PostLoad
public void postLoad(City city) {
if (this.beanFactory != null) {
this.beanFactory.registerSingleton(City.class.getName(), city);
}
}
}

View File

@ -2000,6 +2000,17 @@ for more details.
[[howto-use-dependency-injection-hibernate-components]]
=== Use Dependency Injection in Hibernate Components
By default, Spring Boot registers a `BeanContainer` implementation that uses the
`BeanFactory` so that converters and entity listeners can use regular dependency
injection.
You can disable or tune this behaviour by registering a `HibernatePropertiesCustomizer`
that remove or change the `hibernate.resource.beans.container` property.
[[howto-use-custom-entity-manager]] [[howto-use-custom-entity-manager]]
=== Use a Custom EntityManagerFactory === Use a Custom EntityManagerFactory
To take full control of the configuration of the `EntityManagerFactory`, you need to add To take full control of the configuration of the `EntityManagerFactory`, you need to add

View File

@ -66,7 +66,7 @@ Phillip Webb; Dave Syer; Josh Long; Stéphane Nicoll; Rob Winch; Andy Wilkinson;
:code-examples: ../java/org/springframework/boot/docs :code-examples: ../java/org/springframework/boot/docs
:test-examples: ../../test/java/org/springframework/boot/docs :test-examples: ../../test/java/org/springframework/boot/docs
:gradle-user-guide: https://docs.gradle.org/4.2.1/userguide :gradle-user-guide: https://docs.gradle.org/4.2.1/userguide
:hibernate-documentation: https://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html :hibernate-documentation: https://docs.jboss.org/hibernate/orm/5.3/userguide/html_single/Hibernate_User_Guide.html
:jetty-documentation: https://www.eclipse.org/jetty/documentation/9.4.x :jetty-documentation: https://www.eclipse.org/jetty/documentation/9.4.x
:jooq-manual: https://www.jooq.org/doc/{jooq-version}/manual-single-page :jooq-manual: https://www.jooq.org/doc/{jooq-version}/manual-single-page
:micrometer-concepts-documentation: https://micrometer.io/docs/concepts :micrometer-concepts-documentation: https://micrometer.io/docs/concepts