【问题标题】:Mysql with Hibernate AbstractMultiTenantConnectionProvider with C3P0ConnectionProvider throws GenericJDBCException: Could not open connectionMysql with Hibernate AbstractMultiTenantConnectionProvider with C3P0ConnectionProvider throws GenericJDBCException: Could not open connection
【发布时间】:2017-07-11 05:12:09
【问题描述】:

我正在努力实现 multitenancy="DATABASE",即每个租户数据库/模式的单一连接池(mysql 数据库和模式是同义词)。

我有 MultiTenantConnectionProviderImpl.java

public class MultiTenantConnectionProviderImpl extends AbstractMultiTenantConnectionProvider implements
    ServiceRegistryAwareService {

private static final long serialVersionUID = 1234567890L;

private final HashMap<String, C3P0ConnectionProvider> connProviderMap = new HashMap<>();
private Map<String, String> originalSettings;
private ServiceRegistryImplementor serviceRegistry;

public MultiTenantConnectionProviderImpl() {
}

@Override
protected C3P0ConnectionProvider getAnyConnectionProvider() {
    return selectConnectionProvider(TenantContext.getTenantId());
}

@Override
protected C3P0ConnectionProvider selectConnectionProvider(String tenantId) {
    if (tenantId == null || !tenantId.equals(TenantContext.getTenantId())) {
        throw new TenantStateException(
                "Cannot get connection. Cause: tenant_id is not defined.");
    }

    C3P0ConnectionProvider connectionProvider = connProviderMap.get(tenantId);
    if (connectionProvider == null) {
        // create the new connection and register it
        Map<String, String> settings = new HashMap<>(originalSettings);
        // alter connection by changing user / password of the connection
        Properties properties = new PropertiesBuilder().withScope("database").build();
        settings.put("hibernate.connection.user", DatabaseConnectionProperties.getUser());
        settings.put("hibernate.connection.password", DatabaseConnectionProperties.getPassword());
        settings.put("hibernate.connection.url", DatabaseConnectionProperties.getConnectionUrl());
        settings.put("hibernate.connection.driver_class", properties.getProperty("hibernate.connection.driver_class"));

        connectionProvider = new C3P0ConnectionProvider();
        connectionProvider.injectServices(serviceRegistry);
        connectionProvider.configure(settings);
        connProviderMap.put(tenantId, connectionProvider);
    }

    return connectionProvider;
}

@Override
public void injectServices(ServiceRegistryImplementor serviceRegistry) {
    this.serviceRegistry = serviceRegistry;
    originalSettings = serviceRegistry.getService(ConfigurationService.class).getSettings();
    C3P0ConnectionProvider connectionProvider = new C3P0ConnectionProvider();
    connectionProvider.injectServices(serviceRegistry);
    connectionProvider.configure(originalSettings);
    connProviderMap.put(TenantContext.getTenantId(), connectionProvider);
}
}

连接用户如下

String.format("jdbc:mysql://%s/%s", databaseHost, databaseName)

这失败了

rg.hibernate.exception.GenericJDBCException: Could not open connection
    at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:54)
...

  at org.hibernate.engine.jdbc.internal.LogicalConnectionImpl.obtainConnection(LogicalConnectionImpl.java:297) ~[hibernate-core-4.1.12.Final.jar:4.1.12.Final]
    ... 43 common frames omitted
   Caused by:     com.mchange.v2.resourcepool.CannotAcquireResourceException: A ResourcePool  could not acquire a resource from its primary factory or source.

我试图重写 getConnection(String tenantId) 来执行“使用tenantId”来获取正确的数据库。但是它没有帮助,我恢复到使用连接 url 指定数据库的原始方式。

还有其他人遇到过这个问题吗?

【问题讨论】:

    标签: mysql hibernate multi-tenant c3p0


    【解决方案1】:

    我最终改为实现 MultiTenantConnectionProvider,初始连接到默认架构,然后架构更改到租户架构

    public class MultiTenantConnectionProviderImpl implements MultiTenantConnectionProvider,
        ServiceRegistryAwareService {
    
    private static final long serialVersionUID = 12345567890;
    
    C3P0ConnectionProvider connectionProvider = null;
    
    @Override
    public void injectServices(ServiceRegistryImplementor serviceRegistry) {
        Map<String, String> originalSettings = serviceRegistry
            .getService(ConfigurationService.class).getSettings();
        connectionProvider = new C3P0ConnectionProvider();
        connectionProvider.injectServices(serviceRegistry);
        connectionProvider.configure(originalSettings);
    }
    
    @Override
    public Connection getAnyConnection() throws SQLException {
        try {
            Class.forName("com.mysql.jdbc.Driver");
            final Connection connection = DriverManager.getConnection(
                DatabaseConnectionProperties.getConnectionUrl(),
                DatabaseConnectionProperties.getUser(), DatabaseConnectionProperties.getPassword());
            return connection;
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return connectionProvider.getConnection();
    }
    
    @Override
    public void releaseAnyConnection(Connection connection) throws SQLException {
        try {
            connection.createStatement().execute("use default_tenant");
        }
        catch (SQLException e) {
            throw new HibernateException("Could not alter JDBC connection to specified schema [public]", e);
        }
        connectionProvider.closeConnection(connection);
    }
    
    @Override
    public Connection getConnection(String tenantIdentifier) throws SQLException {
        final Connection connection = getAnyConnection();
        try {
            connection.createStatement().execute("use " + tenantIdentifier);
        }
        catch (SQLException e) {
            throw new HibernateException("Could not alter JDBC connection to specified schema ["
                + tenantIdentifier + "]", e);
        }
        return connection;
    }
    
    @Override
    public void releaseConnection(String tenantIdentifier, Connection connection)
            throws SQLException {
        releaseAnyConnection(connection);
    }
    
    @Override
    public boolean supportsAggressiveRelease() {
        return false;
    }
    
    @Override
    public boolean isUnwrappableAs(Class unwrapType) {
        return false;
    }
    
    @Override
    public <T> T unwrap(Class<T> unwrapType) {
        return null;
    }
    }
    

    【讨论】:

    • 我知道这有点不相关,但是您在 injectServices 方法的实现方面帮助了我很多,了解如何将 C3P0 与多租户结合使用非常有用。对于不知道如何获得 C3P0 连接以用于多租户的所有其他人,我认为这是正确的解决方案。
    猜你喜欢
    • 1970-01-01
    • 2022-12-02
    • 2022-06-21
    • 2019-12-18
    • 1970-01-01
    • 2013-04-02
    • 2017-03-29
    • 2021-10-19
    • 1970-01-01
    相关资源
    最近更新 更多