From 068040e22731151fa4531a313c27839cf69ee8b6 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Tue, 2 Oct 2018 16:41:02 +0200 Subject: [PATCH] Register SpringBeanContainer with default Hibernate setup Closes gh-13717 --- .../orm/jpa/HibernateJpaConfiguration.java | 23 ++++++---- .../HibernateJpaAutoConfigurationTests.java | 33 ++++++++++++++ .../boot/autoconfigure/orm/jpa/test/City.java | 4 +- .../orm/jpa/test/CityListener.java | 43 +++++++++++++++++++ .../src/main/asciidoc/howto.adoc | 11 +++++ .../src/main/asciidoc/index.adoc | 2 +- 6 files changed, 106 insertions(+), 10 deletions(-) create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/test/CityListener.java diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.java index b468a6f875c..54359cb70e0 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.java @@ -16,10 +16,10 @@ package org.springframework.boot.autoconfigure.orm.jpa; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.LinkedHashMap; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.function.Supplier; @@ -31,8 +31,10 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hibernate.boot.model.naming.ImplicitNamingStrategy; import org.hibernate.boot.model.naming.PhysicalNamingStrategy; +import org.hibernate.cfg.AvailableSettings; 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.transaction.TransactionManagerCustomizers; 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.context.annotation.Configuration; import org.springframework.jndi.JndiLocatorDelegate; +import org.springframework.orm.hibernate5.SpringBeanContainer; import org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter; import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; import org.springframework.transaction.jta.JtaTransactionManager; @@ -85,6 +88,7 @@ class HibernateJpaConfiguration extends JpaBaseConfiguration { private final List hibernatePropertiesCustomizers; HibernateJpaConfiguration(DataSource dataSource, JpaProperties jpaProperties, + ConfigurableListableBeanFactory beanFactory, ObjectProvider jtaTransactionManager, ObjectProvider transactionManagerCustomizers, HibernateProperties hibernateProperties, @@ -101,22 +105,25 @@ class HibernateJpaConfiguration extends JpaBaseConfiguration { metadataProviders.getIfAvailable()); this.hibernatePropertiesCustomizers = determineHibernatePropertiesCustomizers( physicalNamingStrategy.getIfAvailable(), - implicitNamingStrategy.getIfAvailable(), hibernatePropertiesCustomizers - .orderedStream().collect(Collectors.toList())); + implicitNamingStrategy.getIfAvailable(), beanFactory, + hibernatePropertiesCustomizers.orderedStream() + .collect(Collectors.toList())); } private List determineHibernatePropertiesCustomizers( PhysicalNamingStrategy physicalNamingStrategy, ImplicitNamingStrategy implicitNamingStrategy, + ConfigurableListableBeanFactory beanFactory, List hibernatePropertiesCustomizers) { + List customizers = new ArrayList<>(); + customizers.add((properties) -> properties.put(AvailableSettings.BEAN_CONTAINER, + new SpringBeanContainer(beanFactory))); if (physicalNamingStrategy != null || implicitNamingStrategy != null) { - LinkedList customizers = new LinkedList<>( - hibernatePropertiesCustomizers); - customizers.addFirst(new NamingStrategiesHibernatePropertiesCustomizer( + customizers.add(new NamingStrategiesHibernatePropertiesCustomizer( physicalNamingStrategy, implicitNamingStrategy)); - return customizers; } - return hibernatePropertiesCustomizers; + customizers.addAll(hibernatePropertiesCustomizers); + return customizers; } @Override diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfigurationTests.java index 8bdf09c03e9..3fb394edbe6 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfigurationTests.java @@ -35,6 +35,7 @@ import javax.transaction.UserTransaction; import com.zaxxer.hikari.HikariDataSource; import org.hibernate.boot.model.naming.ImplicitNamingStrategy; 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.spi.JtaPlatform; 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 @TestAutoConfigurationPackage(City.class) 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 { @Override diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/test/City.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/test/City.java index 0afa47ffa10..a2f4537bd7f 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/test/City.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/test/City.java @@ -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"); * 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.Entity; +import javax.persistence.EntityListeners; import javax.persistence.GeneratedValue; import javax.persistence.Id; @Entity +@EntityListeners(CityListener.class) public class City implements Serializable { private static final long serialVersionUID = 1L; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/test/CityListener.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/test/CityListener.java new file mode 100644 index 00000000000..6632704ea4d --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/test/CityListener.java @@ -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); + } + } + +} diff --git a/spring-boot-project/spring-boot-docs/src/main/asciidoc/howto.adoc b/spring-boot-project/spring-boot-docs/src/main/asciidoc/howto.adoc index 183a00922fd..5299cc64187 100644 --- a/spring-boot-project/spring-boot-docs/src/main/asciidoc/howto.adoc +++ b/spring-boot-project/spring-boot-docs/src/main/asciidoc/howto.adoc @@ -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]] === Use a Custom EntityManagerFactory To take full control of the configuration of the `EntityManagerFactory`, you need to add diff --git a/spring-boot-project/spring-boot-docs/src/main/asciidoc/index.adoc b/spring-boot-project/spring-boot-docs/src/main/asciidoc/index.adoc index ada56e9adff..e88a7004744 100644 --- a/spring-boot-project/spring-boot-docs/src/main/asciidoc/index.adoc +++ b/spring-boot-project/spring-boot-docs/src/main/asciidoc/index.adoc @@ -66,7 +66,7 @@ Phillip Webb; Dave Syer; Josh Long; Stéphane Nicoll; Rob Winch; Andy Wilkinson; :code-examples: ../java/org/springframework/boot/docs :test-examples: ../../test/java/org/springframework/boot/docs :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 :jooq-manual: https://www.jooq.org/doc/{jooq-version}/manual-single-page :micrometer-concepts-documentation: https://micrometer.io/docs/concepts