bugfix> sql-server > 投稿

1つのマスターDBを持つテナントごとに1つのDBが構成されているマルチテナントアプリケーションがあります。次のようなアプリケーションですべてのデータソースをロードします。

@ConfigurationProperties(prefix = "spring.datasource")
@Bean
public DataSource dataSource() {
    if(LOGGER.isInfoEnabled())
        LOGGER.info("Loading datasources ...");
    DataSource ds = null;
    JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
    // load MASTER datasource
    ds = dataSourceLookup.getDataSource(properties.getJndiName());
    // load other TENANTs DB details
    JdbcTemplate jdbcTemplate = new JdbcTemplate(ds);
    List<GroupConfig> groupConfigs = jdbcTemplate.query(
            "select * from master.tblTenant where IsActive=1 and ConfigCode in ('DB_URL','DATASOURCE_CLASS','USER_NAME','DB_PASSWORD') order by 2",
            new ResultSetExtractor<List<GroupConfig>>() {
                public List<GroupConfig> extractData(ResultSet rs) throws SQLException, DataAccessException {
                    List<GroupConfig> list = new ArrayList<GroupConfig>();
                    while (rs.next()) {
                        GroupConfig groupConfig = new GroupConfig();
                        groupConfig.setGroupConfigId(rs.getLong(1));
                        groupConfig.setGroupCode(rs.getString(2));
                        groupConfig.setConfigCode(rs.getString(3));
                        groupConfig.setConfigValue(rs.getString(4));
                        groupConfig.setIsActive(rs.getBoolean(5));
                        list.add(groupConfig);
                    }
                    return list;
                }
            });
    int propCount = 1;
    Map<String, Map<String, String>> groups = new HashMap<String, Map<String, String>>();
    Map<String, String> temp = new HashMap<String, String>();
    for (GroupConfig config : groupConfigs) {
        temp.put(config.getConfigCode(), config.getConfigValue());
        if (propCount % 4 == 0) {
            groups.put(config.getGroupCode(), temp);
            temp = new HashMap<String, String>();
        }
        propCount++;
    }
    // Create TENANT dataSource
    Map<Object, Object> resolvedDataSources = new HashMap<Object, Object>();
    for (String tenantId : groups.keySet()) {
        Map<String, String> groupKV = groups.get(tenantId);
        DataSourceBuilder dataSourceBuilder = new DataSourceBuilder(this.getClass().getClassLoader());
        dataSourceBuilder.driverClassName(groupKV.get("DATASOURCE_CLASS")).url(groupKV.get("DB_URL"))
                .username(groupKV.get("USER_NAME")).password(groupKV.get("DB_PASSWORD"));
        //System.out.println(dataSourceBuilder.findType()); //class org.apache.tomcat.jdbc.pool.DataSource
        if (properties.getType() != null) {
            dataSourceBuilder.type(properties.getType());
        }
        if(LOGGER.isInfoEnabled())
            LOGGER.info("Building datasource : "+tenantId);
        resolvedDataSources.put(tenantId, dataSourceBuilder.build());
    }

    resolvedDataSources.put("MASTER", ds);

    MultitenantDataSource dataSource = new MultitenantDataSource();
    dataSource.setTargetDataSources(resolvedDataSources);
    dataSource.setDataSourceLookup(dataSourceLookup);       
    dataSource.afterPropertiesSet();
    if(LOGGER.isInfoEnabled())
        LOGGER.info("Datasources initialization finished !");
    return dataSource;
}

コントローラーで、それぞれのデータソースを(テナントデータソースと同様に)設定します:

TenantContext.setCurrentTenant("MASTER");

問題 : サーバーの起動時にすべてが正常に動作しますが(MASTER DBおよびTENANT固有のクエリの両方)、サーバーがしばらくアイドル状態になると(数時間)、テナント固有の呼び出しが失敗し始めます(MASTER DB接続は引き続き正常に動作します)がエラーで:

Could not open JPA EntityManager for transaction; nested exception is javax.persistence.PersistenceException: com.microsoft.sqlserver.jdbc.SQLServerException: The connection is closed.

この例外を取り除くために私を助けてください。前もって感謝します。

回答 1 件
  • 私も問題と解決策を得ました:

    テナント接続が閉じられたのはなぜですか? spring-bootの自動構成(@ConfigurationProperties(prefix = "spring.datasource"))が、コードで作成していたテナントDataSourcesに適用されていなかったためです。

    解決- Tomcat接続プールのプロパティを設定する新しいメソッドを追加しました。

    private DataSource buildDataSource(String driverClass, String url, String user, String pass){
        PoolProperties p = new PoolProperties();
        p.setUrl(url);
        p.setDriverClassName(driverClass);
        p.setUsername(user);
        p.setPassword(pass);
        p.setJmxEnabled(true);
        p.setTestWhileIdle(false);
        p.setTestOnBorrow(true); 
        p.setValidationQuery("SELECT 1");
        p.setTestOnReturn(false);
        p.setValidationInterval(30000);
        p.setTimeBetweenEvictionRunsMillis(30000);
        p.setMaxActive(100);
        p.setInitialSize(10);
        p.setMaxWait(10000);
        p.setRemoveAbandonedTimeout(60);
        p.setMinEvictableIdleTimeMillis(30000);
        p.setMinIdle(10);
        p.setLogAbandoned(true);
        p.setRemoveAbandoned(true);
        DataSource datasource = new DataSource();
        datasource.setPoolProperties(p);
        return  datasource;
    }
    
    

    これで問題が解決しました。しかし、スプリングブートでオブジェクトを作成するときにAutoConfigurationsを適用する方法があるかどうかを知りたいです。

あなたの答え