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:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user