我正在尝试设置一个多租户Web应用程序,理想情况下可以同时采用基于数据库和基于模式的方法。虽然我将从模式分离开始。我们目前使用:
- Spring 4.0.0
- Hibernate 4.2.8
- Hibernate-c3p0 4.2.8(使用c3p0-0.9.2.1)
- 以及PostgreSQL 9.3(我怀疑它对整体架构并不重要)
主要是按照此线程(因为解决了@Transactional
的问题)进行操作。但是,我在实现MultiTenantContextConnectionProvider
方面有点迷失。还有在SO上提出的类似问题,但是有一些方面我无法理解:
1)连接池会发生什么?我的意思是,它是由Spring还是Hibernate管理的?我猜测通过ConnectionProviderBuilder
或建议的任何实现,Hibernate是管理它的。
2)Spring不管理连接池是合适的方法吗?或者Spring甚至可以管理它吗?
3)这是未来同时实现基于数据库和模式分离的正确路径吗?
任何意见或说明都非常感激。
application-context.xml
<beans>
...
<bean id="dataSource" class="org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy">
<property name="targetDataSource" ref="c3p0DataSource" />
</bean>
<bean id="c3p0DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="org.postgresql.Driver" />
... other C3P0 related config
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="packagesToScan" value="com.webapp.domain.model" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</prop>
<prop key="hibernate.default_schema">public</prop>
<prop key="hibernate.multiTenancy">SCHEMA</prop>
<prop key="hibernate.tenant_identifier_resolver">com.webapp.persistence.utility.CurrentTenantContextIdentifierResolver</prop>
<prop key="hibernate.multi_tenant_connection_provider">com.webapp.persistence.utility.MultiTenantContextConnectionProvider</prop>
</props>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="autodetectDataSource" value="false" />
<property name="sessionFactory" ref="sessionFactory" />
</bean>
...
</beans>
CurrentTenantContextIdentifierResolver.java
public class CurrentTenantContextIdentifierResolver implements CurrentTenantIdentifierResolver {
@Override
public String resolveCurrentTenantIdentifier() {
return CurrentTenantIdentifier; // e.g.: public, tid130, tid456, ...
}
@Override
public boolean validateExistingCurrentSessions() {
return true;
}
}
MultiTenantContextConnectionProvider.java
public class MultiTenantContextConnectionProvider extends AbstractMultiTenantConnectionProvider {
// Do I need this and its configuratrion?
//private C3P0ConnectionProvider connectionProvider = null;
@Override
public ConnectionProvider getAnyConnectionProvider() {
// the main question is here.
}
@Override
public ConnectionProvider selectConnectionProvider(String tenantIdentifier) {
// and of course here.
}
}
编辑
关于@ben75的答案:
这是MultiTenantContextConnectionProvider
的新实现。它不再继承AbstractMultiTenantConnectionProvider
,而是实现MultiTenantConnectionProvider
,以便能够返回[Connection][4]
而不是[ConnectionProvider][5]
。
public class MultiTenantContextConnectionProvider implements MultiTenantConnectionProvider, ServiceRegistryAwareService {
private DataSource lazyDatasource;;
@Override
public void injectServices(ServiceRegistryImplementor serviceRegistry) {
Map lSettings = serviceRegistry.getService(ConfigurationService.class).getSettings();
lazyDatasource = (DataSource) lSettings.get( Environment.DATASOURCE );
}
@Override
public Connection getAnyConnection() throws SQLException {
return lazyDatasource.getConnection();
}
@Override
public Connection getConnection(String tenantIdentifier) throws SQLException {
final Connection connection = getAnyConnection();
try {
connection.createStatement().execute("SET SCHEMA '" + tenantIdentifier + "'");
}
catch (SQLException e) {
throw new HibernateException("Could not alter JDBC connection to specified schema [" + tenantIdentifier + "]", e);
}
return connection;
}
}
DataSource
级别上进行多租户设置。另一种选择是拥有一个巨大的模式,该模式具有多租户感知功能,并根据性能原因对应用程序进行分区/分片。 - Adam Gent