我在使用Spring + JPA + Hibernate使用单独的数据库方法为我的Web应用程序设置多租户支持时遇到了问题。
我尝试使用自己的CurrentTenantIdentifierResolver
和AbstractMultiTenantConnectionProvider
实现Hibernate方式,而Spring使用AbstractRoutingDataSource
。我将使用我命名为AbstractRoutingDataSource
的{{1}}解决方案来解释我的问题。
我想要实现的目标如下:
UserRoutingDataSource
UserSession
UserRoutingDataSource
需要填充代表我主数据库targetDataSources
表的所有数据库的数据源。CustomDatabases
的{{1}}需要从用户的UserRoutingDataSource
实例中检索数据库ID。在这两种情况下(Hibernate方式/ Spring方式)我最终得到循环引用异常。当我尝试在determineCurrentLookupKey
中自动加载UserSession
和CustomDatabasesDAO
时,会出现此问题。
这是我得到的例外:
UserSession
我不理解的是,当UserRoutingDataSource
没有对org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userEntityManagerFactory' defined in class path resource [MyApp/webapp/WEB-INF/config/applicationContext.xml]: Cannot resolve reference to bean 'userRoutingDataSource' while setting bean property 'dataSource'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userRoutingDataSource' defined in class path resource [MyApp/webapp/WEB-INF/config/applicationContext.xml]: Unsatisfied dependency expressed through constructor argument with index 0 of type [myApp.java.data.dao.Global.CustomDatabasesDAO]: : Error creating bean with name 'customDatabasesDAO': Injection of persistence dependencies failed; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'userEntityManagerFactory': FactoryBean which is currently in creation returned null from getObject; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'customDatabasesDAO': Injection of persistence dependencies failed; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'userEntityManagerFactory': FactoryBean which is currently in creation returned null from getObject
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:359)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:108)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1481)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1226)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:543)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:305)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:301)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:196)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1051)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:828)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:537)
at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:446)
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:328)
at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:107)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4728)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5162)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1409)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1399)
at java.util.concurrent.FutureTask.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userRoutingDataSource' defined in class path resource [MyApp/webapp/WEB-INF/config/applicationContext.xml]: Unsatisfied dependency expressed through constructor argument with index 0 of type [myApp.java.data.dao.Global.CustomDatabasesDAO]: : Error creating bean with name 'customDatabasesDAO': Injection of persistence dependencies failed; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'userEntityManagerFactory': FactoryBean which is currently in creation returned null from getObject; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'customDatabasesDAO': Injection of persistence dependencies failed; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'userEntityManagerFactory': FactoryBean which is currently in creation returned null from getObject
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:749)
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:185)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1143)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1046)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:510)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:305)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:301)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:196)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:351)
... 24 more
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'customDatabasesDAO': Injection of persistence dependencies failed; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'userEntityManagerFactory': FactoryBean which is currently in creation returned null from getObject
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.postProcessPropertyValues(PersistenceAnnotationBeanPostProcessor.java:357)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1214)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:543)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:305)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:301)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:196)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1192)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1116)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1014)
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:813)
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:741)
... 34 more
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'userEntityManagerFactory': FactoryBean which is currently in creation returned null from getObject
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:181)
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getObjectFromFactoryBean(FactoryBeanRegistrySupport.java:127)
at org.springframework.beans.factory.support.AbstractBeanFactory.getObjectForBeanInstance(AbstractBeanFactory.java:1584)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:253)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:196)
at org.springframework.orm.jpa.EntityManagerFactoryUtils.findEntityManagerFactory(EntityManagerFactoryUtils.java:130)
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.findNamedEntityManagerFactory(PersistenceAnnotationBeanPostProcessor.java:556)
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.findEntityManagerFactory(PersistenceAnnotationBeanPostProcessor.java:538)
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor$PersistenceElement.resolveEntityManager(PersistenceAnnotationBeanPostProcessor.java:707)
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor$PersistenceElement.getResourceToInject(PersistenceAnnotationBeanPostProcessor.java:680)
at org.springframework.beans.factory.annotation.InjectionMetadata$InjectedElement.inject(InjectionMetadata.java:169)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.postProcessPropertyValues(PersistenceAnnotationBeanPostProcessor.java:354)
... 46 more
的单一引用时,我会收到此异常。我在CustomDatabasesDAO
中可以想到的唯一相关的事情是我对master数据库的entityManager的调用:
userEntityManagerFactory
有关更多上下文,请参阅我的CustomDatabasesDAO
文件以及应用程序上下文文件中的相关部分。
@PersistenceContext(unitName = "masterEntityManagerFactory")
private EntityManager masterEntityManager;
UserRoutingDataSource
我尝试了很多东西,我不确定我是否仍然理解依赖注入的工作原理。我不知道自己可能缺少什么。
谢谢你的帮助。
感谢Roman的回答,我解决了我的问题。我也放弃了使用我的会话范围bean来确定我当前的查找键的想法,而是使用我的Spring Security身份验证。当用户登录时,我执行以下操作:
public class UserRoutingDataSource extends AbstractRoutingDataSource {
/*private CustomDatabasesDAO customDatabasesDAO;
@Autowired
public void setCustomDatabasesDAO(final CustomDatabasesDAO customDatabasesDAO)
{
this.customDatabasesDAO = customDatabasesDAO;
}*/
@Autowired
private UserSession session;
/*@Autowired
public void setUserSession(final UserSession session)
{
this.session = session;
}*/
@Autowired
public UserRoutingDataSource(CustomDatabasesDAO customDatabasesDAO) {
Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
for(CustomDatabases database : customDatabasesDAO.findDatabasesByDeleted(0))
{
// All the information necessary for the datasource will eventually be retrieved from the database variable
DriverManagerDataSource datasource = new DriverManagerDataSource();
datasource.setDriverClassName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
datasource.setUrl("jdbc:sqlserver://localhost:1433;databaseName=" + database.getCdboAliasName() + ";");
datasource.setUsername("username");
datasource.setPassword("password");
targetDataSources.put(String.valueOf(database.getCdboDatabaseId()), datasource);
}
setTargetDataSources(targetDataSources);
}
@Override
protected Object determineCurrentLookupKey() {
return session.getCdboDatabaseId();
}
}
重要的部分是我的用户/*<!--<bean id="currentTenantIdentifierResolverImpl" class="myApp.java.config.CurrentTenantIdentifierResolverImpl" />
<bean id="multiTenantConnectionProvider" class="myApp.java.config.MultiTenantConnectionProvider" />-->*/
<bean id="masterDataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.microsoft.sqlserver.jdbc.SQLServerDriver" />
<property name="url" value="jdbc:sqlserver://localhost:1433;databaseName=myDatabase;" />
<property name="username" value="sa" />
<property name="password" value="password" />
</bean>
<bean id="masterEntityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="masterDataSource" />
<property name="packagesToScan" value="myApp.java.data.model" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.SQLServerDialect</prop>
<prop key="hibernate.show_sql">false</prop>
<prop key="hibernate.use_outer_join">true</prop>
</props>
</property>
</bean>
<bean id="userRoutingDataSource" class="myApp.java.config.UserRoutingDataSource">
<property name="targetDataSources">
<map />
</property>
</bean>
<bean id="userEntityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="userRoutingDataSource" />
<property name="packagesToScan" value="myApp.java.data.model" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
<property name="jpaProperties">
<map>
<entry key="hibernate.dialect" value="org.hibernate.dialect.SQLServerDialect" />
<entry key="hibernate.show_sql" value="false" />
<entry key="hibernate.use_outer_join" value="true" />
/*<!--<entry key="hibernate.tenant_identifier_resolver" value-ref="currentTenantIdentifierResolverImpl" />
<entry key="hibernate.multi_tenant_connection_provider" value-ref="multiTenantConnectionProvider" />
<entry key="hibernate.multiTenancy" value="DATABASE" />-->*/
</map>
</property>
</bean>
<!-- Transaction managers -->
<tx:annotation-driven />
<bean id="masterTransactionManager"
class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="masterEntityManagerFactory" />
<qualifier value="master" />
</bean>
<bean id="userTransactionManager"
class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="userEntityManagerFactory" />
<qualifier value="user" />
</bean>
上的Authentication auth = SecurityContextHolder.getContext().getAuthentication();
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
UsernamePasswordAuthenticationToken newAuth = new UsernamePasswordAuthenticationToken(auth.getPrincipal(), auth.getCredentials(), authorities);
HashMap<String, Object> details = new HashMap<String, Object>();
details.put("databaseId", session.getCdboDatabaseId());
newAuth.setDetails(details);
SecurityContextHolder.getContext().setAuthentication(newAuth);
。
以下是我(工作)配置的当前状态:
setDetails(details)
UsernamePasswordAuthenticationToken
答案 0 :(得分:0)
Spring迭代EntityManagerFactory
内的所有EntityManagerFactoryUtils.findEntityManagerFactory()
,找出属性persistenceUnitName
的值等于@PersistenceUnit
的属性unitName
值的工厂。仅当未找到候选者时,才会使用名称为unitName
的bean。因此userEntityManagerFactory
在此过程中被实例化。
作为一种解决方法,您可以尝试在@Autowired
课程中使用常规@PersistenceUnit
代替CustomDatabasesDAO
。