Я пытаюсь настроить «три» класса источника данных в приложении Spring. Я успешно настроил два, но «третий», который я хочу настроить на неопределенное количество источников данных, доставляет мне проблемы.
Вот моя идея (неработающий V1):
@Configuration
@EnableJpaRepositories(entityManagerFactoryRef = "referenceEntityManager",
transactionManagerRef = "referenceTransactionManager",
basePackages = "com.tes.domain.repositories.reference.repositories")
@PropertySource("file:${C:\\Some\\Path\\file.properties")
public class ManyConfigurations{
@Autowired
EnvironmentHelper environmentHelper;
//derived from primary datasource below
@Autowired
IReferenceRepo referenceRepo;
private DataSourceManager dataSourceManager = new DataSourceManager();
public DataSource makeDataSource(String name) throws PropertyVetoException {
ComboPooledDataSource ds = new ComboPooledDataSource();
ds.setDriverClass(environmentHelper.getEnvironment().getProperty("base_datasource_driver"));
ds.setJdbcUrl(environmentHelper.getEnvironment().getProperty("base_datasource_url") +";databaseName=reference" + name);
ds.setUser(environmentHelper.getEnvironment().getProperty("base_datasource_username"));
ds.setPassword(environmentHelper.getEnvironment().getProperty("base_datasource_password"));
ds.setAcquireIncrement(5);
ds.setAcquireRetryAttempts(10);
ds.setAcquireRetryDelay(5000);
ds.setInitialPoolSize(52);
ds.setMaxIdleTime(3600);
ds.setMaxIdleTimeExcessConnections(300);
ds.setMinPoolSize(52);
ds.setMaxPoolSize(125);
ds.setNumHelperThreads(6);
ds.setUnreturnedConnectionTimeout(0);
return ds;
}
@Bean(name = "referenceDataSource")
public DataSourceManager dataSource() throws PropertyVetoException {
List<Reference> references= referenceRepo.findAll();
if (references != null) {
for (Reference r : references) {
dataSourceManager.add(r.getId().toString(), makeDataSource(r.getId().toString()));
}
}
if (!references.isEmpty()) {
dataSourceManager.switchDataSource(references.get(0).getId().toString());
}
return dataSourceManager;
}
@Bean(name = "referenceEntityManager")
public LocalContainerEntityManagerFactoryBean getReferenceEntityManager(DataSourceManager dataSourceManager) throws PropertyVetoException {
for (Map.Entry<String, DataSource> entry : dataSourceManager.getDataSources().entrySet()) {
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(entry.getValue());
em.setPackagesToScan(new String[]{"com.tes.domain.model.reference.models"});
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaProperties(additionalJpaProperties());
em.setPersistenceUnitName("reference" + entry.getKey());
dataSourceManager.addEntityManager(entry.getKey(), em);
}
dataSourceManager.updateToMatch();
return dataSourceManager.getEntityManager();
}
Properties additionalJpaProperties(){
Properties properties = new Properties();
return properties;
}
@Bean(name = "referenceTransactionManager")
public JpaTransactionManager referenceTransactionManager(DataSourceManager dataSourceManager) throws PropertyVetoException {
for (Map.Entry<String, LocalContainerEntityManagerFactoryBean> entry : dataSourceManager.getEntityManagers().entrySet()) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setDataSource(dataSourceManager.getDataSources().get(entry.getKey()));
transactionManager.setEntityManagerFactory(entry.getValue().getObject());
transactionManager.setPersistenceUnitName("reference" + entry.getKey());
dataSourceManager.addTransactionalManager(entry.getKey(), transactionManager);
}
dataSourceManager.updateToMatch();
return dataSourceManager.getTransactionManager();
}
}
Мой менеджер источников данных выглядит так:
public class DataSourceManager implements DataSource {
private Map<String, DataSource> dataSources = new HashMap<>();
private Map<String, LocalContainerEntityManagerFactoryBean> entityManagers = new HashMap<>();
private Map<String, JpaTransactionManager> transactionalManagers = new HashMap<>();
private DataSource dataSource;
private LocalContainerEntityManagerFactoryBean entityManager;
private JpaTransactionManager transactionManager;
public DataSourceManager() {
}
public DataSourceManager(DataSource dataSource) {
this.dataSource = dataSource;
}
public void add(String name, DataSource dataSource) {
dataSources.put(name, dataSource);
}
public void addEntityManager(String name, LocalContainerEntityManagerFactoryBean entityManager) {
entityManagers.put(name, entityManager);
}
public void addTransactionalManager(String name, JpaTransactionManager transactionManager) {
transactionalManagers.put(name, transactionManager);
}
public void switchDataSource(String name) {
dataSource = dataSources.get(name);
entityManager = entityManagers.get(name);
transactionManager = transactionalManagers.get(name);
}
public void updateToMatch() {
for (Map.Entry<String, DataSource> entry : dataSources.entrySet()) {
if (entry.getValue().equals(dataSource)) {
String name = entry.getKey();
try {
entityManager = entityManagers.get(name);
transactionManager = transactionalManagers.get(name);
} catch (Exception e) {
}
}
}
}
@Override
public PrintWriter getLogWriter() throws SQLException {
return dataSource.getLogWriter();
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
dataSource.setLogWriter(out);
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
dataSource.setLoginTimeout(seconds);
}
@Override
public int getLoginTimeout() throws SQLException {
return dataSource.getLoginTimeout();
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return dataSource.getParentLogger();
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return dataSource.unwrap(iface);
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return dataSource.isWrapperFor(iface);
}
@Override
public Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
return dataSource.getConnection(username, password);
}
public Map<String, DataSource> getDataSources() {
return dataSources;
}
public Map<String, LocalContainerEntityManagerFactoryBean> getEntityManagers() {
return entityManagers;
}
public Map<String, JpaTransactionManager> getTransactionalManagers() {
return transactionalManagers;
}
public DataSource getDataSource() {
return dataSource;
}
public LocalContainerEntityManagerFactoryBean getEntityManager() {
return entityManager;
}
public JpaTransactionManager getTransactionManager() {
return transactionManager;
}
}
У меня есть несколько идей о том, что я хотел бы сделать. По сути, я хотел бы отобразить их активную предпочитаемую базу данных, на которую ссылаются, и предварительно сконфигурировать каждую базу данных, на которую ссылаются, в приложении весенней загрузки. Мы перезапускаем Tomcat каждый раз, когда добавляем новую базу данных (не большая проблема), и будет конечная точка для переключения предпочитаемой пользователем базы данных.
Звучит хорошо в гипотезе. Большое препятствие, с которым я столкнулся, было, когда я использовал:
@EnableJpaRepositories(entityManagerFactoryRef = "referenceEntityManager",
transactionManagerRef = "referenceTransactionManager",
basePackages = "com.tes.domain.repositories.reference.repositories")
Я столкнулся с проблемой, что настраивался только мой последний возвращенный JpaTransactionManager, и у меня не было возможности «переключать» источники данных как мой класс DataSourceManager. должен быть в состоянии сделать.
Я пытаюсь сказать, можно ли настроить расширенное количество JpaTransactionManager и переключаться между ними так же, как я переключаюсь между источником данных?
В качестве альтернативы, есть ли способ переключения между источниками данных, который также изменяет источники моего менеджера транзакций?