#mysql #spring-boot #spring-data-jpa #multiple-databases
#mysql #весенняя загрузка #spring-data-jpa #несколько баз данных
Вопрос:
Здесь я пытаюсь настроить четыре источника данных MySQL с помощью приложения spring boot, используя четыре JPA entitymanager. Я новичок в spring и spring boot. Я погуглил это, чтобы получить подсказку, но результаты были очень запутанными, существует множество подходов к конфигурациям (но не для обработки нескольких источников данных), я не уверен, чему следовать. В конце концов, я пытаюсь настроить четыре отдельных менеджера сущностей для каждого источника данных MySQL и использовать их отдельно в транзакциях.
application.properties
# Database Properties
db.driver: com.mysql.jdbc.Driver
db.urlHotelPos: jdbc:mysql://localhost:3306/hotelwebpos
db.urlHotelFinance: jdbc:mysql://localhost:3306/hotelfinance
db.urlHotelInventory: jdbc:mysql://localhost:3306/hotelinventory
db.urlReservation: jdbc:mysql://localhost:3306/reservation
db.username: user
db.password: passwd
# Hibernate Properties
hibernate.dialect: org.hibernate.dialect.MySQL5Dialect
hibernate.show_sql: true
hibernate.hbm2ddl.auto: update
entitymanager.packagesToScan: com.test.poswebservice.model.*
DBconfigs.java
@Configuration
@EnableTransactionManagement
public class DBconfigs {
@Bean
public DataSource dataSourceHotelPos() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("db.driver"));
dataSource.setUrl(env.getProperty("db.urlHotelPos"));
dataSource.setUsername(env.getProperty("db.username"));
dataSource.setPassword(env.getProperty("db.password"));
return dataSource;
}
@Bean
public DataSource dataSourceHotelFinance() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("db.driver"));
dataSource.setUrl(env.getProperty("db.urlHotelFinance"));
dataSource.setUsername(env.getProperty("db.username"));
dataSource.setPassword(env.getProperty("db.password"));
return dataSource;
}
@Bean
public DataSource dataSourceHotelInventory() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("db.driver"));
dataSource.setUrl(env.getProperty("db.urlHotelInventory"));
dataSource.setUsername(env.getProperty("db.username"));
dataSource.setPassword(env.getProperty("db.password"));
return dataSource;
}
@Bean
public DataSource dataSourceReservation() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("db.driver"));
dataSource.setUrl(env.getProperty("db.urlReservation"));
dataSource.setUsername(env.getProperty("db.username"));
dataSource.setPassword(env.getProperty("db.password"));
return dataSource;
}
@Bean
@Qualifier("dataSourceHotelPos")
public LocalContainerEntityManagerFactoryBean entityManagerFactoryHotelPos() {
LocalContainerEntityManagerFactoryBean entityManagerFactory = new LocalContainerEntityManagerFactoryBean();
entityManagerFactory.setDataSource(dataSourceHotelPos());
entityManagerFactory.setPackagesToScan(env.getProperty("entitymanager.packagesToScan"));
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
entityManagerFactory.setJpaVendorAdapter(vendorAdapter);
Properties additionalProperties = new Properties();
additionalProperties.put("hibernate.dialect", env.getProperty("hibernate.dialect"));
additionalProperties.put("hibernate.show_sql", env.getProperty("hibernate.show_sql"));
additionalProperties.put("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
entityManagerFactory.setJpaProperties(additionalProperties);
return entityManagerFactory;
}
@Bean
@Qualifier("dataSourceHotelFinance")
public LocalContainerEntityManagerFactoryBean entityManagerFactoryHotelFinance() {
LocalContainerEntityManagerFactoryBean entityManagerFactory = new LocalContainerEntityManagerFactoryBean();
entityManagerFactory.setDataSource(dataSourceHotelFinance());
entityManagerFactory.setPackagesToScan(env.getProperty("entitymanager.packagesToScan"));
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
entityManagerFactory.setJpaVendorAdapter(vendorAdapter);
Properties additionalProperties = new Properties();
additionalProperties.put("hibernate.dialect", env.getProperty("hibernate.dialect"));
additionalProperties.put("hibernate.show_sql", env.getProperty("hibernate.show_sql"));
additionalProperties.put("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
entityManagerFactory.setJpaProperties(additionalProperties);
return entityManagerFactory;
}
@Bean
@Qualifier("dataSourceHotelInventory")
public LocalContainerEntityManagerFactoryBean entityManagerFactoryHotelInventory(){
LocalContainerEntityManagerFactoryBean entityManagerFactory = new LocalContainerEntityManagerFactoryBean();
entityManagerFactory.setDataSource(dataSourceHotelInventory());
entityManagerFactory.setPackagesToScan(env.getProperty("entitymanager.packagesToScan"));
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
entityManagerFactory.setJpaVendorAdapter(vendorAdapter);
Properties additionalProperties = new Properties();
additionalProperties.put("hibernate.dialect", env.getProperty("hibernate.dialect"));
additionalProperties.put("hibernate.show_sql", env.getProperty("hibernate.show_sql"));
additionalProperties.put("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
entityManagerFactory.setJpaProperties(additionalProperties);
return entityManagerFactory;
}
@Bean
@Qualifier("dataSourceReservation")
public LocalContainerEntityManagerFactoryBean entityManagerFactoryReservation() {
LocalContainerEntityManagerFactoryBean entityManagerFactory = new LocalContainerEntityManagerFactoryBean();
entityManagerFactory.setDataSource(dataSourceReservation());
entityManagerFactory.setPackagesToScan(env.getProperty("entitymanager.packagesToScan"));
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
entityManagerFactory.setJpaVendorAdapter(vendorAdapter);
Properties additionalProperties = new Properties();
additionalProperties.put("hibernate.dialect", env.getProperty("hibernate.dialect"));
additionalProperties.put("hibernate.show_sql", env.getProperty("hibernate.show_sql"));
additionalProperties.put("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
entityManagerFactory.setJpaProperties(additionalProperties);
return entityManagerFactory;
}
@Bean
@Qualifier("entityManagerFactoryHotelPos")
public JpaTransactionManager transactionManagerHotelPos() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactoryHotelPos().getObject());
return transactionManager;
}
@Bean
@Qualifier("entityManagerFactoryHotelFinance")
public JpaTransactionManager transactionManagerHotelFinance() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactoryHotelFinance().getObject());
return transactionManager;
}
@Bean
@Qualifier("entityManagerFactoryHotelInventory")
public JpaTransactionManager transactionManagerHotelInventory() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactoryHotelInventory().getObject());
return transactionManager;
}
@Bean
@Qualifier("entityManagerFactoryReservation")
public JpaTransactionManager transactionManagerReservation() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactoryReservation().getObject());
return transactionManager;
}
@Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
@Autowired
private Environment env;
}
Вывод >>
***************************
APPLICATION FAILED TO START
***************************
Description:
Method entityManagerFactoryHotelPos in com.test.poswebservice.configs.DBconfigs required a single bean, but 4 were found:
- dataSourceHotelPos: defined by method 'dataSourceHotelPos' in class path resource [com/test/poswebservice/configs/DBconfigs.class]
- dataSourceHotelFinance: defined by method 'dataSourceHotelFinance' in class path resource [com/test/poswebservice/configs/DBconfigs.class]
- dataSourceHotelInventory: defined by method 'dataSourceHotelInventory' in class path resource [com/test/poswebservice/configs/DBconfigs.class]
- dataSourceReservation: defined by method 'dataSourceReservation' in class path resource [com/test/poswebservice/configs/DBconfigs.class]
Action:
Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed
Аннотация @Qualifier должна избегать этой проблемы, но здесь она как-то не работает. Что я здесь делаю не так? Пожалуйста, кто-нибудь, помогите мне, это меня почти убивает:-( Любая помощь будет высоко оценена.
Спасибо.
Ответ №1:
В моей настройке это работает так, что:
- Удалите
@Qualifier
из ваших компонентов EMF - Добавьте к каждому компоненту EMF:
@DependsOn({"dataSource"})
с правильнымDataSource
- Действительно, сделайте один из
DataSource
@Primary
Комментарии:
1. почему мы должны создавать здесь один из компонентов источника данных
@Primary
? (PS даже в моей предыдущей настройке ошибки исчезли при создании компонента источника данных@primary
, я предполагаю, что он использует один и тот же основной источник данных для каждого компонента EMF, я подтверждаю эту проверку еще раз.) создание@primary
означает, что один компонент EMF будет приниматьнесколько компонентов источника данных, верно? один как основной, а другие как необязательные? Но в моем случае я просто хочу, чтобы компонент EMF использовал один конкретный компонент источника данных. я что-то не так понял? заранее спасибо за подсказку.2. Я могу подтвердить, что эта настройка работает не так, как предполагалось. Компонент EMF всегда выбирает
@Primary
источник данных. Никакого эффекта от@DependsOn
.@Primary
является диктатором.
Ответ №2:
следует написать такой код:
@Bean(name = "dataSourceHotelPos")
public DataSource dataSourceHotelPos() {
...
Ответ №3:
Я не знаю, решит ли это вашу проблему (и моя репутация не позволяет мне это комментировать), но вы не должны вызывать методы EMF внутри ваших определений компонентов, потому что spring уже инициирует компонент для этого. Вызов метода создаст второй компонент с точно такими же настройками.
Вместо этого вы можете попробовать что-то вроде этого:
@Bean
@Qualifier("entityManagerFactoryHotelPos")
public JpaTransactionManager transactionManagerHotelPos(@Qualifier("dataSourceHotelPos") LocalContainerEntityManagerFactoryBean entityManagerFactoryHotelPos) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactoryHotelPos.getObject());
return transactionManager;
}
Поскольку spring попытается инициировать этот компонент, он должен автоматически подключать параметры конструктора.
Надеюсь, это поможет.