Spring Data JPA - несколько включенных репозиториев
Мое приложение имеет несколько источников данных, поэтому я создал два класса конфигурации источника данных на основе этого URL.
Но при запуске весеннего загрузочного приложения я получаю сообщение об ошибке
Описание: для поля userDataRepo в com.cavion.services.UserDataService требуется компонент с именем entityManagerFactory, который не может быть найден. Действие: Рассмотрите возможность определения bean-компонента с именем entityManagerFactory в вашей конфигурации.
Этот вопрос о Stackru помог мне выяснить проблему. Мне нужно указать entityManagerFactoryRef в моих репозиториях JPA.
Но у меня есть много классов репозитория, некоторые из них используют Entitymanager 'A', а некоторые из них используют 'B' . мой текущий класс приложения весенней загрузки выглядит так
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class })
@EnableTransactionManagement
@EntityScan("com.info.entity")
@ComponentScan({"com.info.services","com.info.restcontroller"})
@EnableJpaRepositories("com.info.repositories")
public class CavionApplication {
public static void main(String[] args) {
SpringApplication.run(CavionApplication.class, args);
}
@Bean
public CommandLineRunner commandLineRunner(ApplicationContext ctx) {
return args -> {
System.out.println("Let's inspect the beans provided by Spring Boot:");
String[] beanNames = ctx.getBeanDefinitionNames();
Arrays.sort(beanNames);
for (String beanName : beanNames) {
System.out.println(beanName);
}
};
}}
Я дал EnableJpaRepositories в классе начальной загрузки, так как я могу настроить несколько EnableJpaRepositories, чтобы я мог настроить несколько entityManagerFactory?
Пожалуйста, предложите лучший способ настройки нескольких источников данных.
1 ответ
Чтобы весна знала, что DataSource
связано с чем Repository
Вы должны определить это на @EnableJpaRepositories
аннотаций. Давайте предположим, что у нас есть две сущности, Servers
сущность и Domains
объект и каждый имеет свой собственный репо, тогда каждый репозиторий имеет свою собственную конфигурацию JpaDataSource.
1. Сгруппируйте все репозитории на основе источника данных, с которым они связаны. Например
Репозиторий для Domains
лица (пакет: org.springdemo.multiple.datasources.repository.domains
):
package org.springdemo.multiple.datasources.repository.domains;
import org.springdemo.multiple.datasources.domain.domains.Domains;
import org.springframework.data.jpa.repository.JpaRepository;
public interface DomainsRepository extends JpaRepository<Domains,Long> {
}
Репозиторий для Servers
лица (пакет: org.springdemo.multiple.datasources.repository.servers
)
package org.springdemo.multiple.datasources.repository.servers;
import org.springdemo.multiple.datasources.domain.servers.Servers;
import org.springframework.data.jpa.repository.JpaRepository;
public interface ServersRepository extends JpaRepository<Servers,Long> {
}
2. Для каждого JPA Data Soruce необходимо определить конфигурацию, в этом примере я покажу, как настроить два разных источника данных.
Domains
Конфигурация Jpa: связь между источником данных и хранилищем определяется в basePackages
Это является причиной, по которой необходимо сгруппировать репозитории в разные пакеты в зависимости от менеджера сущностей, который будет использоваться каждым репо.
@Configuration
@EnableJpaRepositories(
entityManagerFactoryRef = "domainsEntityManager",
transactionManagerRef = "domainsTransactionManager",
basePackages = {"org.springdemo.multiple.datasources.repository.domains"}
)
public class DomainsConfig {
Servers
Конфигурация источника данных: как видите, значение basePackages содержит имя пакета Servers
Репозиторий, а также значения entityManagerFactoryRef
а также transactionManagerRef
различаются для того, чтобы пружина отделяла каждый entityManager.
@Configuration
@EnableJpaRepositories(
entityManagerFactoryRef = "serversEntityManager",
transactionManagerRef = "serversTransactionManager",
basePackages = {"org.springdemo.multiple.datasources.repository.servers"}
)
public class ServersConfig {
3. Установите один источник данных в качестве основного
Чтобы избежать сообщения об ошибке: Parameter 0 of constructor in org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration required a single bean, but 2 were found:
просто установите один из источников данных как @Primary, в этом примере я выбираю Servers
Источник данных как основной:
@Bean("serversDataSourceProperties")
@Primary
@ConfigurationProperties("app.datasource.servers")
public DataSourceProperties serversDataSourceProperties(){
return new DataSourceProperties();
}
@Bean("serversDataSource")
@Primary
@ConfigurationProperties("app.datasource.servers")
public DataSource serversDataSource(@Qualifier("serversDataSourceProperties") DataSourceProperties serversDataSourceProperties) {
return serversDataSourceProperties().initializeDataSourceBuilder().build();
}
Если вам нужна дополнительная информация, смотрите полный пример для каждой конфигурации:
Servers
Конфигурация JPA
@Configuration
@EnableJpaRepositories(
entityManagerFactoryRef = "serversEntityManager",
transactionManagerRef = "serversTransactionManager",
basePackages = {"org.springdemo.multiple.datasources.repository.servers"}
)
public class ServersConfig {
@Bean(name = "serversEntityManager")
public LocalContainerEntityManagerFactoryBean getServersEntityManager(EntityManagerFactoryBuilder builder,
@Qualifier("serversDataSource") DataSource serversDataSource){
return builder
.dataSource(serversDataSource)
.packages("org.springdemo.multiple.datasources.domain.servers")
.persistenceUnit("servers")
.properties(additionalJpaProperties())
.build();
}
Map<String,?> additionalJpaProperties(){
Map<String,String> map = new HashMap<>();
map.put("hibernate.hbm2ddl.auto", "create");
map.put("hibernate.dialect", "org.hibernate.dialect.MySQLDialect");
map.put("hibernate.show_sql", "true");
return map;
}
@Bean("serversDataSourceProperties")
@Primary
@ConfigurationProperties("app.datasource.servers")
public DataSourceProperties serversDataSourceProperties(){
return new DataSourceProperties();
}
@Bean("serversDataSource")
@Primary
@ConfigurationProperties("app.datasource.servers")
public DataSource serversDataSource(@Qualifier("serversDataSourceProperties") DataSourceProperties serversDataSourceProperties) {
return serversDataSourceProperties().initializeDataSourceBuilder().build();
}
@Bean(name = "serversTransactionManager")
public JpaTransactionManager transactionManager(@Qualifier("serversEntityManager") EntityManagerFactory serversEntityManager){
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(serversEntityManager);
return transactionManager;
}
}
Domains
Конфигурация JPA
@Configuration
@EnableJpaRepositories(
entityManagerFactoryRef = "domainsEntityManager",
transactionManagerRef = "domainsTransactionManager",
basePackages = {"org.springdemo.multiple.datasources.repository.domains"}
)
public class DomainsConfig {
@Bean(name = "domainsEntityManager")
public LocalContainerEntityManagerFactoryBean getdomainsEntityManager(EntityManagerFactoryBuilder builder
,@Qualifier("domainsDataSource") DataSource domainsDataSource){
return builder
.dataSource(domainsDataSource)
.packages("org.springdemo.multiple.datasources.domain.domains")
.persistenceUnit("domains")
.properties(additionalJpaProperties())
.build();
}
Map<String,?> additionalJpaProperties(){
Map<String,String> map = new HashMap<>();
map.put("hibernate.hbm2ddl.auto", "create");
map.put("hibernate.dialect", "org.hibernate.dialect.H2Dialect");
map.put("hibernate.show_sql", "true");
return map;
}
@Bean("domainsDataSourceProperties")
@ConfigurationProperties("app.datasource.domains")
public DataSourceProperties domainsDataSourceProperties(){
return new DataSourceProperties();
}
@Bean("domainsDataSource")
public DataSource domainsDataSource(@Qualifier("domainsDataSourceProperties") DataSourceProperties domainsDataSourceProperties) {
return domainsDataSourceProperties.initializeDataSourceBuilder().build();
}
@Bean(name = "domainsTransactionManager")
public JpaTransactionManager transactionManager(@Qualifier("domainsEntityManager") EntityManagerFactory domainsEntityManager){
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(domainsEntityManager);
return transactionManager;
}
}
Чтобы отделить каждый источник данных, я поместил конфигурацию в application.properties
файл, как это:
app.datasource.domains.url=jdbc:h2:mem:~/test
app.datasource.domains.driver-class-name=org.h2.Driver
app.datasource.servers.driver-class-name=com.mysql.jdbc.Driver
app.datasource.servers.url=jdbc:mysql://localhost:3306/v?autoReconnect=true&useSSL=false
app.datasource.servers.username=myuser
app.datasource.servers.password=mypass
Если вам нужна дополнительная информация, см. Следующую документацию:
Весенняя документация: источники данных с двумя способами
Аналогичный пример того, как настроить две разные базы данных: пример github
Ответ, предоставленный @Daniel C., правильный. Небольшая поправка / наблюдение с моей стороны.
- @Primary не требуется, если вы не хотите отмечать какой-либо источник данных как источник по умолчанию, иначе это необходимо.
- Если вы определяете какой-либо объект EntityManagerFactoryBean с именем @Bean как entityManagerFactory, то лучше пометить его как @Primary, чтобы избежать конфликта.
- @ConfigurationProperties("app.datasource.servers") можно пометить на уровне класса, а не определять на уровне метода.
- Лучше вернуть HikariDataSource в качестве источника данных, если вы используете Spring Boot 2.x или более позднюю версию, поскольку она была изменена.
- Убедитесь, что вы определили точное свойство для jdbc-url, которое используется HikariDataSource для ссылки на URL-адрес соединения JDBC.
Я только что добавил в github библиотеку с поддержкой нескольких баз данных для mysql. Некоторые свойства приложения нужно добавить, и все готово.
Документацию и другие подробности можно найти по адресу: -
https://github.com/yatharthamishra0419/spring-boot-data-multimodule-mysql