Spring多租户:添加用户时,动态地向MultiTenantConnectionProvider添加数据源

时间:2016-12-24 08:17:51

标签: java spring multi-tenant

我有一个用户注册并为他们创建新数据库的场景。注册过程完成后,需要将此数据库添加到MultiTenantConnectionProvider(连接池)。一切正常,但无法将数据源动态添加到MultiTenantConnectionProvider。

多租户的Hibernate配置:

       <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
                <prop key="hibernate.multiTenancy">DATABASE</prop>
                <prop key="hibernate.tenant_identifier_resolver">com.company.multitenancy.MyCurrentTenantIdentifierResolverImpl
                </prop>
                <prop key="hibernate.multi_tenant_connection_provider">com.company.multitenancy.MyMultiTenantConnectionProvider
                </prop>
            </props>
        </property>

MyMultiTenantConnectionProvider的代码:

package com.company.multitenancy;

import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import org.hibernate.engine.jdbc.connections.spi.AbstractMultiTenantConnectionProvider;
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;    
import com.termbreak.constant.ConstantStrings;

public class MyMultiTenantConnectionProvider extends
        AbstractMultiTenantConnectionProvider {
    private static final long serialVersionUID = -8669630427906544663L;
    private HashMap<String, ConnectionProviderImpl> connProviderMap = new HashMap<String, ConnectionProviderImpl>();

    public MyMultiTenantConnectionProvider() {
        List<String> providerNames = new ArrayList<String>();
        providerNames.add(ConstantStrings.DEFAULT_TENANT_ID);
        try {
            String sqlUrl = "jdbc:mysql://localhost:3306/"+ConstantStrings.DEFAULT_TENANT_ID;
            Class.forName("com.mysql.jdbc.Driver");
            java.sql.Connection conn = DriverManager.getConnection(sqlUrl,
                    "root", "root");
            Statement st = conn.createStatement();
            ResultSet rs = st
                    .executeQuery("select DISTINCT TENANT_ID from User");
            while (rs.next()) {
                String tenantId = rs.getString(1);
                providerNames.add(tenantId);
            }
            conn.close();
        } catch (Exception e) {
            System.err.println("Got an exception! ");
            System.err.println(e.getMessage());
        }
        for (String providerName : providerNames) {
            connProviderMap.put(providerName, new ConnectionProviderImpl(
                    providerName));
        }
    }

    public ConnectionProvider getAnyConnectionProvider() {
        System.out
                .println("inside MultiTenantConnectionProvider::getAnyConnectionProvider");
        return connProviderMap.get(ConstantStrings.DEFAULT_TENANT_ID);
    }

    public ConnectionProvider selectConnectionProvider(String tenantId) {
        ConnectionProvider connectionProvider = connProviderMap.get(tenantId);
        if (connectionProvider == null)
            connectionProvider = new ConnectionProviderImpl(ConstantStrings.DEFAULT_TENANT_ID);
        return connectionProvider;
    }
}

MyCurrentTenantIdResolver的代码:

package com.company.multitenancy;

import org.hibernate.context.spi.CurrentTenantIdentifierResolver;

import com.termbreak.constant.ConstantStrings;

public class MyCurrentTenantIdentifierResolverImpl implements
        CurrentTenantIdentifierResolver {
    public ThreadLocal<String> _tenantIdentifier = new ThreadLocal<String>();
    public String DEFAULT_TENANT_ID = ConstantStrings.DEFAULT_TENANT_ID;

    public String resolveCurrentTenantIdentifier() {
        System.out.println("from inside resolveCurrentTenantIdentifier....");
        String tenantId = _tenantIdentifier.get();
        if (tenantId == null)
            tenantId = DEFAULT_TENANT_ID;
        System.out.println("threadlocal tenant id =" + tenantId);
        return tenantId;
    }

    public boolean validateExistingCurrentSessions() {
        return true;
    }

}

MyConnectionProviderImpl的代码:

package com.company.multitenancy;

import java.sql.Connection;
import java.sql.SQLException;

import org.apache.commons.dbcp2.BasicDataSource;
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;

public class ConnectionProviderImpl implements ConnectionProvider { 
    private static final long serialVersionUID = -8926112316994338537L;
    private BasicDataSource basicDataSource;

    public ConnectionProviderImpl(String database){
        //this should be read from properties file
        basicDataSource = new BasicDataSource();
        basicDataSource.setDriverClassName("com.mysql.jdbc.Driver");
        basicDataSource.setUrl("jdbc:mysql://localhost:3306/"+database);
        basicDataSource.setUsername("root");
        basicDataSource.setPassword("root");
        basicDataSource.setInitialSize(2);
    }
    public boolean isUnwrappableAs(Class arg0) {
        return false;
    }
    public Object unwrap(Class arg0) {
        return null;
    }
    public void closeConnection(Connection arg0) throws SQLException {
        arg0.close();
    }
    public Connection getConnection() throws SQLException {
        return basicDataSource.getConnection();
    }
    public boolean supportsAggressiveRelease() {
        return false;
    }
}

要连接到我正在使用的特定租户:

sessionFactory.withOptions().tenantIdentifier(tenantId).openSession();

1 个答案:

答案 0 :(得分:1)

除了ConnectionProviderImpl的实现之外,您的配置似乎是正确的。在此实现中,您需要提供具有租户标识符的hibernate。您需要实现AbstractDataSourceBasedMultiTenantConnectionProviderImpl,并根据您使用的Hibernate版本覆盖getConnection()或selectDataSource()。我建议您按照Hibernate用户指南https://docs.jboss.org/hibernate/orm/4.3/devguide/en-US/html/ch16.html示例16.3和此帖http://www.ticnfae.co.uk/blog/2014/07/16/hibernate-multi-tenancy-with-spring/进行操作。