LocalSessionFactoryBuilder provides Hibernate 5.3 BeanContainer support

LocalSessionFactoryBean automatically registers its containing BeanFactory as a BeanContainer when Hibernate 5.3 or higher is on the classpath.

Issue: SPR-16305
This commit is contained in:
Juergen Hoeller
2018-07-04 19:23:19 +02:00
parent 5dc8b5de6d
commit c0d4cb55c7
6 changed files with 256 additions and 6 deletions

View File

@@ -33,9 +33,12 @@ import org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider;
import org.hibernate.integrator.spi.Integrator;
import org.hibernate.service.ServiceRegistry;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
@@ -46,6 +49,7 @@ import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
/**
* {@link FactoryBean} that creates a Hibernate {@link SessionFactory}. This is the usual
@@ -61,7 +65,7 @@ import org.springframework.lang.Nullable;
* @see LocalSessionFactoryBuilder
*/
public class LocalSessionFactoryBean extends HibernateExceptionTranslator
implements FactoryBean<SessionFactory>, ResourceLoaderAware, InitializingBean, DisposableBean {
implements FactoryBean<SessionFactory>, ResourceLoaderAware, BeanFactoryAware, InitializingBean, DisposableBean {
@Nullable
private DataSource dataSource;
@@ -131,6 +135,9 @@ public class LocalSessionFactoryBean extends HibernateExceptionTranslator
@Nullable
private ResourcePatternResolver resourcePatternResolver;
@Nullable
private ConfigurableListableBeanFactory beanFactory;
@Nullable
private Configuration configuration;
@@ -433,6 +440,23 @@ public class LocalSessionFactoryBean extends HibernateExceptionTranslator
return this.resourcePatternResolver;
}
/**
* Accept the containing {@link BeanFactory}, registering corresponding Hibernate
* {@link org.hibernate.resource.beans.container.spi.BeanContainer} integration for
* it if possible. This requires a Spring {@link ConfigurableListableBeanFactory}
* and Hibernate 5.3 or higher on the classpath.
* @since 5.1
* @see LocalSessionFactoryBuilder#setBeanContainer
*/
@Override
public void setBeanFactory(BeanFactory beanFactory) {
if (beanFactory instanceof ConfigurableListableBeanFactory &&
ClassUtils.isPresent("org.hibernate.resource.beans.container.spi.BeanContainer",
getClass().getClassLoader())) {
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
}
}
@Override
public void afterPropertiesSet() throws IOException {
@@ -508,6 +532,10 @@ public class LocalSessionFactoryBean extends HibernateExceptionTranslator
sfb.setJtaTransactionManager(this.jtaTransactionManager);
}
if (this.beanFactory != null) {
sfb.setBeanContainer(this.beanFactory);
}
if (this.multiTenantConnectionProvider != null) {
sfb.setMultiTenantConnectionProvider(this.multiTenantConnectionProvider);
}

View File

@@ -46,6 +46,7 @@ import org.hibernate.context.spi.CurrentTenantIdentifierResolver;
import org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.core.InfrastructureProxy;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
@@ -228,6 +229,19 @@ public class LocalSessionFactoryBuilder extends Configuration {
return this;
}
/**
* Set a Hibernate {@link org.hibernate.resource.beans.container.spi.BeanContainer}
* for the given Spring {@link ConfigurableListableBeanFactory}.
* <p>Note: Bean container integration requires Hibernate 5.3 or higher.
* It enables autowiring of Hibernate attribute converters and entity listeners.
* @since 5.1
* @see AvailableSettings#BEAN_CONTAINER
*/
public LocalSessionFactoryBuilder setBeanContainer(ConfigurableListableBeanFactory beanFactory) {
getProperties().put(AvailableSettings.BEAN_CONTAINER, new SpringBeanContainer(beanFactory));
return this;
}
/**
* Set a {@link MultiTenantConnectionProvider} to be passed on to the SessionFactory.
* @since 4.3

View File

@@ -0,0 +1,151 @@
/*
* Copyright 2002-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.orm.hibernate5;
import java.util.Map;
import java.util.function.Consumer;
import org.hibernate.resource.beans.container.spi.BeanContainer;
import org.hibernate.resource.beans.container.spi.ContainedBean;
import org.hibernate.resource.beans.spi.BeanInstanceProducer;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.lang.Nullable;
import org.springframework.util.ConcurrentReferenceHashMap;
/**
* Spring's implementation of Hibernate 5.3's {@link BeanContainer} SPI,
* delegating to a Spring {@link ConfigurableListableBeanFactory}.
*
* @author Juergen Hoeller
* @since 5.1
*/
final class SpringBeanContainer implements BeanContainer {
private final ConfigurableListableBeanFactory beanFactory;
private final Map<Object, SpringContainedBean<?>> beanCache = new ConcurrentReferenceHashMap<>();
public SpringBeanContainer(ConfigurableListableBeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Override
@SuppressWarnings("unchecked")
public <B> ContainedBean<B> getBean(
Class<B> beanType, LifecycleOptions lifecycleOptions, BeanInstanceProducer fallbackProducer) {
SpringContainedBean<?> bean;
if (lifecycleOptions.canUseCachedReferences()) {
bean = this.beanCache.get(beanType);
if (bean == null) {
bean = createBean(beanType, lifecycleOptions);
this.beanCache.put(beanType, bean);
}
}
else {
bean = createBean(beanType, lifecycleOptions);
}
return (SpringContainedBean<B>) bean;
}
@Override
@SuppressWarnings("unchecked")
public <B> ContainedBean<B> getBean(
String name, Class<B> beanType, LifecycleOptions lifecycleOptions, BeanInstanceProducer fallbackProducer) {
if (!this.beanFactory.containsBean(name)) {
return getBean(beanType, lifecycleOptions, fallbackProducer);
}
SpringContainedBean<?> bean;
if (lifecycleOptions.canUseCachedReferences()) {
bean = this.beanCache.get(name);
if (bean == null) {
bean = createBean(name, beanType, lifecycleOptions);
this.beanCache.put(name, bean);
}
}
else {
bean = createBean(name, beanType, lifecycleOptions);
}
return (SpringContainedBean<B>) bean;
}
@Override
public void stop() {
this.beanCache.values().forEach(SpringContainedBean::destroyIfNecessary);
this.beanCache.clear();
}
private SpringContainedBean<?> createBean(Class<?> beanType, LifecycleOptions lifecycleOptions) {
if (lifecycleOptions.useJpaCompliantCreation()) {
return new SpringContainedBean<>(
this.beanFactory.createBean(beanType, AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR, false),
this.beanFactory::destroyBean);
}
else {
return new SpringContainedBean<>(this.beanFactory.getBean(beanType));
}
}
private SpringContainedBean<?> createBean(String name, Class<?> beanType, LifecycleOptions lifecycleOptions) {
if (lifecycleOptions.useJpaCompliantCreation()) {
Object bean = this.beanFactory.autowire(beanType, AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR, false);
this.beanFactory.applyBeanPropertyValues(bean, name);
this.beanFactory.initializeBean(bean, name);
return new SpringContainedBean<>(bean, beanInstance -> this.beanFactory.destroyBean(name, beanInstance));
}
else {
return new SpringContainedBean<>(this.beanFactory.getBean(name, beanType));
}
}
private static final class SpringContainedBean<B> implements ContainedBean<B> {
private final B beanInstance;
@Nullable
private Consumer<B> destructionCallback;
public SpringContainedBean(B beanInstance) {
this.beanInstance = beanInstance;
}
public SpringContainedBean(B beanInstance, Consumer<B> destructionCallback) {
this.beanInstance = beanInstance;
this.destructionCallback = destructionCallback;
}
@Override
public B getBeanInstance() {
return this.beanInstance;
}
public void destroyIfNecessary() {
if (this.destructionCallback != null) {
this.destructionCallback.accept(this.beanInstance);
}
}
}
}