Несколько jpa: репозиториев в xml config, как настроить @EnableJPARepositories с помощью Spring java config?
Я исследовал и нашел объяснение и пример кода относительно того, как использовать jpa данных Spring с несколькими источниками данных, что относится к настройке нескольких репозиториев jpa: в конфигурации xml следующим образом:
<jpa:repositories base-package="org.springframework.data.jpa.repository.sample"
entity-manager-factory-ref="entityManagerFactory">
<repository:exclude-filter type="assignable" expression="org.springframework.data.jpa.repository.sample.AuditableUserRepository" />
</jpa:repositories>
<jpa:repositories base-package="org.springframework.data.jpa.repository.sample"
entity-manager-factory-ref="entityManagerFactory-2"
transaction-manager-ref="transactionManager-2">
<repository:include-filter type="assignable" expression="org.springframework.data.jpa.repository.sample.AuditableUserRepository" />
</jpa:repositories>
Как бы вы объявили обе вышеуказанные конфигурации репозиториев jpa: используя конфигурацию java и аннотацию @EnableJpaRepositories?
Кажется, что аннотация поддерживает только один набор атрибутов (т.е. только для одного jpa: репозитория), и аннотацию невозможно объявить несколько раз.
2 ответа
Я создал "минимальный" проект с несколькими источниками данных, чтобы помочь мне понять, как это сделать. Там есть 7 классов Java и другие конфигурации, так что в этом ответе я опубликую только ключевые выдержки. Вы можете получить полный проект от GitHub: https://github.com/gratiartis/multids-demo
Демонстрация устанавливает две сущности JPA:
@Entity public class Foo { /* Constructors, fields and accessors/mutators */ }
@Entity public class Bar { /* Constructors, fields and accessors/mutators */ }
В связи с этим мы создадим два хранилища. Благодаря огромному количеству Spring Data, мы можем получить некоторые довольно полнофункциональные репозитории, просто определив интерфейсы, расширяющие JpaRepository:
public interface FooRepository extends JpaRepository<Foo, Long> {}
public interface BarRepository extends JpaRepository<Bar, Long> {}
Теперь нам нужно убедиться, что каждая из этих карт отображается в таблице в своей собственной базе данных.
Для этого нам понадобятся два отдельных менеджера сущностей, каждый из которых имеет свой источник данных. Тем не менее, в конфигурации Java Spring @Configuration
класс, мы можем иметь только один @EnableJpaRepositories
аннотация и каждая такая аннотация может ссылаться только на один EntityManagerFactory. Чтобы добиться этого, мы создаем два отдельных @Configuration
классы: FooConfig и BarConfig.
Каждый из этих классов @Configuration будет определять источник данных на основе встроенной базы данных HSQL:
@Bean(name = "fooDataSource")
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setName("foodb").setType(EmbeddedDatabaseType.HSQL).build();
}
@Bean(name = "barDataSource")
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setName("bardb").setType(EmbeddedDatabaseType.HSQL).build();
}
@Bean(name = "barEntityManagerFactory")
public EntityManagerFactory entityManagerFactory() {
LocalContainerEntityManagerFactoryBean lef =
new LocalContainerEntityManagerFactoryBean();
lef.setDataSource(dataSource());
lef.setJpaVendorAdapter(jpaVendorAdapter);
lef.setPackagesToScan("com.sctrcd.multidsdemo.domain.bar");
lef.setPersistenceUnitName("barPersistenceUnit");
lef.afterPropertiesSet();
return lef.getObject();
}
@Bean(name = "fooEntityManagerFactory")
public EntityManagerFactory entityManagerFactory() {
LocalContainerEntityManagerFactoryBean lef =
new LocalContainerEntityManagerFactoryBean();
lef.setDataSource(dataSource());
lef.setJpaVendorAdapter(jpaVendorAdapter);
lef.setPackagesToScan("com.sctrcd.multidsdemo.domain.foo");
lef.setPersistenceUnitName("fooPersistenceUnit");
lef.afterPropertiesSet();
return lef.getObject();
}
Каждая конфигурация должна определять EntityManagerFactory, как указано выше, которая ссылается на свой собственный метод dataSource() @Bean. Он также определяет путь к бинам @Entity, которыми он управляет. Вы должны убедиться, что бины @Entity для разных источников данных находятся в разных пакетах.
На этом этапе стоит отметить, что если каждая из этих конфигураций использует именования по умолчанию для ключевых компонентов персистентности (т. Е. EntityManagerFactory), то Spring увидит, что есть два компонента с интерфейсом EntityManager, оба из которых имеют одинаковые имена. Так что один будет выбран. Это приводит к таким ошибкам, как:
Not an managed type: class com.sctrcd.multidsdemo.domain.bar.Bar
Это можно увидеть в разделе демо-проекта здесь: https://github.com/gratiartis/multids-demo/tree/1-unnamed-entitymanager-beans
Это связано с тем, что в этом примере Spring связал bean-компоненты, относящиеся к базе данных "foodb", а Bar не является сущностью в этой базе данных. К сожалению, BarRepository был связан с менеджером сущностей Foo.
Мы решаем эту проблему, называя все наши bean-компоненты в каждом классе конфигурации. т.е.
@Bean(name = "fooDataSource") public DataSource dataSource() { .. }
@Bean(name = "fooEntityManager") public EntityManager entityManager() { .. }
На этом этапе, если вы должны запустить тесты в проекте, вы можете увидеть такие предупреждения, как:
No bean named 'entityManagerFactory' is defined.
Это потому что... drumroll... у нас нет EntityManagerFactory с именем по умолчанию "entityManagerFactory". У нас есть один с именем "fooEntityManagerFactory", а другой с именем "barEntityManagerFactory". Spring ищет что-то с именем по умолчанию, поэтому мы должны указать ему, чтобы все было по-другому.
Оказывается, это невероятно просто сделать. Нам просто нужно поместить правильные ссылки в аннотацию @EnableJpaRepositories для каждого класса @Configuration.
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
entityManagerFactoryRef = "fooEntityManagerFactory",
transactionManagerRef = "fooTransactionManager",
basePackages = {"com.sctrcd.multidsdemo.integration.repositories.foo"})
public class FooConfig {
// ...
}
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
entityManagerFactoryRef = "barEntityManagerFactory",
transactionManagerRef = "barTransactionManager",
basePackages = { "com.sctrcd.multidsdemo.integration.repositories.bar" })
public class BarConfig {
// ...
}
Как видите, каждая из этих аннотаций @EnableJpaRepositories определяет конкретные имена EntityManagerFactory и PlatformTransactionManager. Они также указывают, какие репозитории должны быть подключены с этими бобами. В этом примере я поместил репозитории в специфичные для базы данных пакеты. Также возможно определить каждый отдельный репозиторий по имени, добавив includeFilters к аннотации, но, отделяя репозитории по базе данных, я считаю, что все должно получиться более читабельным.
На этом этапе у вас должно быть работающее приложение, использующее репозитории Spring Data для управления объектами в двух отдельных базах данных. Не стесняйтесь захватить проект по ссылке выше и запустить тесты, чтобы увидеть, как это происходит. Надеюсь, этот ответ будет полезен большему количеству людей, так как я потратил приличное количество времени, работая над тем, чтобы сделать это как можно более чисто, используя как можно меньше кода. Любые идеи по улучшению ответа или демо-проекта приветствуются.
Вы можете попробовать поставить его на два @Configuration
классы (один @EnableJpaRepositories
в @Configuration
).