Автор оригинала: Eugen Paraschiv.
1. Обзор
В этом уроке мы реализуем простую конфигурацию Spring для системы JPA Spring Data с несколькими базами данных .
Дальнейшее чтение:
Spring Data JPA – Производные методы удаления
Программная настройка источника данных при весенней загрузке
2. Субъекты
Во – первых, давайте создадим две простые сущности, каждая из которых живет в отдельной базе данных.
Вот первая сущность ” User “:
package com.baeldung.multipledb.model.user; @Entity @Table(schema = "users") public class User { @Id @GeneratedValue(strategy = GenerationType.AUTO) private int id; private String name; @Column(unique = true, nullable = false) private String email; private int age; }
И вторая сущность – ” Продукт “:
package com.baeldung.multipledb.model.product; @Entity @Table(schema = "products") public class Product { @Id private int id; private String name; private double price; }
Как вы можете видеть, эти две сущности также помещены в независимые пакеты – это будет важно, когда мы перейдем к конфигурации.
3. Репозитории JPA
Далее – давайте взглянем на наши два репозитория JPA – UserRepository :
package com.baeldung.multipledb.dao.user; public interface UserRepository extends JpaRepository{ }
И ProductRepository :
package com.baeldung.multipledb.dao.product; public interface ProductRepository extends JpaRepository{ }
Еще раз обратите внимание, как мы создали эти два репозитория в разных пакетах.
4. Настройте JPA С Помощью Java
Далее – давайте перейдем к фактической конфигурации пружины. Мы начнем с настройки двух классов конфигурации – один для Пользователя , а другой для Продукта .
В каждом из этих классов конфигурации нам нужно будет определить следующие интерфейсы для User :
- Источник данных
- EntityManagerFactory ( userEntityManager )
- Менеджер транзакций ( userTransactionManager )
Давайте начнем с просмотра конфигурации пользователя:
@Configuration @PropertySource({ "classpath:persistence-multiple-db.properties" }) @EnableJpaRepositories( basePackages = "com.baeldung.multipledb.dao.user", entityManagerFactoryRef = "userEntityManager", transactionManagerRef = "userTransactionManager" ) public class PersistenceUserConfiguration { @Autowired private Environment env; @Bean @Primary public LocalContainerEntityManagerFactoryBean userEntityManager() { LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); em.setDataSource(userDataSource()); em.setPackagesToScan( new String[] { "com.baeldung.multipledb.model.user" }); HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); em.setJpaVendorAdapter(vendorAdapter); HashMapproperties = new HashMap<>(); properties.put("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto")); properties.put("hibernate.dialect", env.getProperty("hibernate.dialect")); em.setJpaPropertyMap(properties); return em; } @Primary @Bean public DataSource userDataSource() { DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName( env.getProperty("jdbc.driverClassName")); dataSource.setUrl(env.getProperty("user.jdbc.url")); dataSource.setUsername(env.getProperty("jdbc.user")); dataSource.setPassword(env.getProperty("jdbc.pass")); return dataSource; } @Primary @Bean public PlatformTransactionManager userTransactionManager() { JpaTransactionManager transactionManager = new JpaTransactionManager(); transactionManager.setEntityManagerFactory( userEntityManager().getObject()); return transactionManager; } }
Обратите внимание, как мы используем userTransactionManager в качестве нашего Primary TransactionManager – путем аннотирования определения компонента с помощью @Primary . Это полезно всякий раз, когда мы собираемся неявно или явно ввести диспетчер транзакций, не указывая, какой из них по имени.
Далее давайте обсудим PersistenceProductConfiguration – где мы определяем аналогичные компоненты:
@Configuration @PropertySource({ "classpath:persistence-multiple-db.properties" }) @EnableJpaRepositories( basePackages = "com.baeldung.multipledb.dao.product", entityManagerFactoryRef = "productEntityManager", transactionManagerRef = "productTransactionManager" ) public class PersistenceProductConfiguration { @Autowired private Environment env; @Bean public LocalContainerEntityManagerFactoryBean productEntityManager() { LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); em.setDataSource(productDataSource()); em.setPackagesToScan( new String[] { "com.baeldung.multipledb.model.product" }); HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); em.setJpaVendorAdapter(vendorAdapter); HashMapproperties = new HashMap<>(); properties.put("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto")); properties.put("hibernate.dialect", env.getProperty("hibernate.dialect")); em.setJpaPropertyMap(properties); return em; } @Bean public DataSource productDataSource() { DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName( env.getProperty("jdbc.driverClassName")); dataSource.setUrl(env.getProperty("product.jdbc.url")); dataSource.setUsername(env.getProperty("jdbc.user")); dataSource.setPassword(env.getProperty("jdbc.pass")); return dataSource; } @Bean public PlatformTransactionManager productTransactionManager() { JpaTransactionManager transactionManager = new JpaTransactionManager(); transactionManager.setEntityManagerFactory( productEntityManager().getObject()); return transactionManager; } }
5. Простой тест
Наконец, давайте проверим наши конфигурации.
Мы попробуем простой тест, создав экземпляр каждой сущности и убедившись, что она создана, как в следующем примере:
@RunWith(SpringRunner.class) @SpringBootTest @EnableTransactionManagement public class JpaMultipleDBIntegrationTest { @Autowired private UserRepository userRepository; @Autowired private ProductRepository productRepository; @Test @Transactional("userTransactionManager") public void whenCreatingUser_thenCreated() { User user = new User(); user.setName("John"); user.setEmail("[email protected]"); user.setAge(20); user = userRepository.save(user); assertNotNull(userRepository.findOne(user.getId())); } @Test @Transactional("userTransactionManager") public void whenCreatingUsersWithSameEmail_thenRollback() { User user1 = new User(); user1.setName("John"); user1.setEmail("[email protected]"); user1.setAge(20); user1 = userRepository.save(user1); assertNotNull(userRepository.findOne(user1.getId())); User user2 = new User(); user2.setName("Tom"); user2.setEmail("[email protected]"); user2.setAge(10); try { user2 = userRepository.save(user2); } catch (DataIntegrityViolationException e) { } assertNull(userRepository.findOne(user2.getId())); } @Test @Transactional("productTransactionManager") public void whenCreatingProduct_thenCreated() { Product product = new Product(); product.setName("Book"); product.setId(2); product.setPrice(20); product = productRepository.save(product); assertNotNull(productRepository.findOne(product.getId())); } }
6. Несколько баз данных в весенней загрузке
Пружинная загрузка может упростить конфигурацию, описанную выше.
По умолчанию Spring Boot создаст экземпляр по умолчанию DataSource со свойствами конфигурации с префиксом spring.datasource.* :
spring.datasource.jdbcUrl = [url] spring.datasource.username = [username] spring.datasource.password = [password]
Теперь мы хотим продолжать использовать тот же способ настройки второго источника данных , но с другим пространством имен свойств:
spring.second-datasource.jdbcUrl = [url] spring.second-datasource.username = [username] spring.second-datasource.password = [password]
Поскольку мы хотим, чтобы автоконфигурация Spring Boot собирала эти разные свойства (и создавала два разных источника данных ), мы определим два класса конфигурации, аналогичные тем, которые были описаны в предыдущих разделах:
@Configuration @PropertySource({"classpath:persistence-multiple-db-boot.properties"}) @EnableJpaRepositories( basePackages = "com.baeldung.multipledb.dao.user", entityManagerFactoryRef = "userEntityManager", transactionManagerRef = "userTransactionManager") public class PersistenceUserAutoConfiguration { @Primary @Bean @ConfigurationProperties(prefix="spring.datasource") public DataSource userDataSource() { return DataSourceBuilder.create().build(); } // userEntityManager bean // userTransactionManager bean }
@Configuration @PropertySource({"classpath:persistence-multiple-db-boot.properties"}) @EnableJpaRepositories( basePackages = "com.baeldung.multipledb.dao.product", entityManagerFactoryRef = "productEntityManager", transactionManagerRef = "productTransactionManager") public class PersistenceProductAutoConfiguration { @Bean @ConfigurationProperties(prefix="spring.second-datasource") public DataSource productDataSource() { return DataSourceBuilder.create().build(); } // productEntityManager bean // productTransactionManager bean }
Мы определили свойства источника данных внутри persistence-multiple-db-boot.properties в соответствии с соглашением об автоматической настройке загрузки.
Интересной частью является аннотирование метода создания компонента источника данных с помощью @ConfigurationProperties . Нам просто нужно указать соответствующий префикс конфигурации . Внутри этого метода мы используем DataSourceBuilder, и Spring Boot автоматически позаботится обо всем остальном.
Но как сконфигурированные свойства вводятся в Источник данных конфигурацию?
При вызове метода build() в DataSourceBuilder он вызовет свой частный bind() метод:
public T build() { Class extends DataSource> type = getType(); DataSource result = BeanUtils.instantiateClass(type); maybeGetDriverClassName(); bind(result); return (T) result; }
Этот частный метод выполняет большую часть магии автоконфигурации, привязывая разрешенную конфигурацию к фактическому источнику данных экземпляру:
private void bind(DataSource result) { ConfigurationPropertySource source = new MapConfigurationPropertySource(this.properties); ConfigurationPropertyNameAliases aliases = new ConfigurationPropertyNameAliases(); aliases.addAliases("url", "jdbc-url"); aliases.addAliases("username", "user"); Binder binder = new Binder(source.withAliases(aliases)); binder.bind(ConfigurationPropertyName.EMPTY, Bindable.ofInstance(result)); }
Хотя нам самим не нужно прикасаться к этому коду, все равно полезно знать, что происходит под капотом автоконфигурации Spring Boot.
Кроме того, конфигурация компонентов диспетчера транзакций и диспетчера сущностей совпадает со стандартным приложением Spring.
7. Заключение
Эта статья была практическим обзором того, как настроить проект Spring Data JPA для использования нескольких баз данных.
полную реализацию этой статьи можно найти в проекте GitHub – это проект на основе Maven, поэтому его должно быть легко импортировать и запускать как есть.