我们使用 Spring 3.2.0 和 Hibernate 4.1.12 开发 Web 应用程序。
该应用程序支持“基于数据源”的多租户,这意味着每个应用程序租户都使用一个专用的数据源。
数据源 JNDI 名称在运行时根据请求应用程序的用户名(用户名说明必须使用哪个租户)解析。解析是实现了Hibernate的org.hibernate.service.jdbc.connections.spi.ConnectionProvider接口。
Spring 配置(摘录)是:
<bean id="tenantHibernateSessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="packagesToScan">
<list>
<value>com.foo.bar.model</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.show_sql">false</prop>
<prop key="hibernate.dialect">org.hibernate.dialect.DB2Dialect</prop>
<prop key="hibernate.format_sql">false</prop>
<prop key="hibernate.connection.autocommit">true</prop>
<prop key="hibernate.connection.provider_class">com.foo.bar.dao.hibernate.CustomConnectionProvider</prop>
</props>
</property>
</bean>
在应用程序部署期间,Spring 初始化应用程序和 org.springframework.orm.hibernate4.LocalSessionFactoryBean 实例。这最终会执行数据库访问。此数据库访问失败,因为我们的 CustomConnectionProvider 返回一个空连接。我们返回一个空连接,因为在部署时,我们无法识别当前租户。在应用程序部署期间访问数据库毫无意义。我们得到的堆栈跟踪是:
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'tenantHibernateSessionFactory' defined in file [D:\Work\Java\apache-tomcat-6.0.39\webapps\ams-gate\WEB-INF\classes\META-INF\spring\persistence.xml]: Invocation of init method failed; nested exception is java.lang.NullPointerException
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1486)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:524)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:461)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:295)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:292)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:873)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:815)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:730)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:558)
... 129 more
Caused by: java.lang.NullPointerException
at org.hibernate.engine.jdbc.internal.JdbcServicesImpl.configure(JdbcServicesImpl.java:119)
at org.hibernate.service.internal.StandardServiceRegistryImpl.configureService(StandardServiceRegistryImpl.java:75)
at org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:159)
at org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:131)
at org.hibernate.cfg.SettingsFactory.buildSettings(SettingsFactory.java:77)
at org.hibernate.cfg.Configuration.buildSettingsInternal(Configuration.java:2287)
at org.hibernate.cfg.Configuration.buildSettings(Configuration.java:2283)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1752)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1792)
at org.springframework.orm.hibernate4.LocalSessionFactoryBuilder.buildSessionFactory(LocalSessionFactoryBuilder.java:242)
at org.springframework.orm.hibernate4.LocalSessionFactoryBean.buildSessionFactory(LocalSessionFactoryBean.java:372)
at org.springframework.orm.hibernate4.LocalSessionFactoryBean.afterPropertiesSet(LocalSessionFactoryBean.java:357)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1545)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1483)
... 139 more
我查看了 Hibernate 源代码,发现了一个可以在部署期间禁用数据库访问的属性:
<prop key="hibernate.temp.use_jdbc_metadata_defaults">false</prop>
但它似乎在这里不起作用。
更新 1:这个 Hibernate 属性确实在应用程序部署期间禁用了数据库访问。但是根据 Hibernate 4.1.12 中的 org.hibernate.engine.jdbc.internal.JdbcServicesImpl,这个属性是一个未记录的临时魔法值。
是否有推荐的方法在部署期间禁用数据库访问以正确支持多租户?
感谢您的时间