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 件
関連記事
- インポート後のPythonノートブックのキーエラーの解決
- 接続エラーが発生した場合でも、Pythonはリストをループし続けます
- エラー[ERR_HTTP_HEADERS_SENT]:APIにリクエストを送信するときに、クライアントに送信された後、ヘッダーを設定できません
- Ubuntuのアップグレード後に特異点を実行するとsquashfsエラーが発生する
- 圧縮後のRaspberryPiBootのエラー
- C:memsetを呼び出した後の奇妙なsysmallocアサーションエラー
- HttpClientシーケンシャルコール接続エラー
- Cloud Run/Gunicornが1分後に502エラーを出す
- CLASSPATH、Maven、およびpomxmlを設定した後、適切なドライバーエラーはありません
- yfinanceからの「ダウンロード」呼び出し後のSSLエラー
私も問題と解決策を得ました:
テナント接続が閉じられたのはなぜですか? spring-bootの自動構成(@ConfigurationProperties(prefix = "spring.datasource"))が、コードで作成していたテナントDataSourcesに適用されていなかったためです。
解決- Tomcat接続プールのプロパティを設定する新しいメソッドを追加しました。
これで問題が解決しました。しかし、スプリングブートでオブジェクトを作成するときにAutoConfigurationsを適用する方法があるかどうかを知りたいです。