Spring JPA - Pelbagai Pangkalan Data

1. Gambaran keseluruhan

Dalam tutorial ini, kami akan melaksanakan konfigurasi Spring sederhana untuk sistem Spring Data JPA dengan pelbagai pangkalan data .

2. Entiti

Pertama - mari buat dua entiti mudah - masing-masing tinggal di pangkalan data yang berasingan.

Inilah entiti “ Pengguna ” pertama :

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; }

Dan entiti kedua - " Produk ":

package com.baeldung.multipledb.model.product; @Entity @Table(schema = "products") public class Product { @Id private int id; private String name; private double price; }

Seperti yang anda lihat, kedua-dua entiti itu juga diletakkan dalam pakej bebas - ini akan menjadi penting semasa kita beralih ke konfigurasi.

3. Repositori JPA

Seterusnya - mari kita lihat dua repositori JPA kami - UserRepository :

package com.baeldung.multipledb.dao.user; public interface UserRepository extends JpaRepository { }

Dan ProductRepository :

package com.baeldung.multipledb.dao.product; public interface ProductRepository extends JpaRepository { }

Perhatikan sekali lagi, bagaimana kita membuat dua repositori ini dalam pakej yang berbeza.

4. Konfigurasikan JPA Dengan Java

Seterusnya - mari kita pergi ke konfigurasi Spring yang sebenarnya. Kami akan memulakan dengan menetapkan dua kelas konfigurasi - satu untuk Pengguna dan satu lagi untuk Produk .

Dalam setiap kelas konfigurasi ini, kita perlu menentukan antara muka berikut untuk Pengguna :

  • Sumber data
  • EntityManagerFactory ( userEntityManager )
  • TransactionManager ( userTransactionManager )

Mari mulakan dengan melihat konfigurasi Pengguna:

@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); HashMap properties = 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; } }

Perhatikan bagaimana kita menggunakan userTransactionManager sebagai Primer TransactionManager - dengan memberi penjelasan mengenai definisi kacang dengan @Primary . Itu berguna setiap kali kita menyuntik pengurus transaksi secara implisit atau eksplisit tanpa menentukan yang mana satu dengan namanya.

Seterusnya, mari kita bincangkan PersistenceProductConfiguration - di mana kita menentukan kacang serupa:

@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); HashMap properties = 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. Ujian Ringkas

Akhirnya - mari kita uji konfigurasi kami.

Kami akan mencuba ujian mudah dengan membuat contoh setiap entiti dan memastikannya dibuat - seperti dalam contoh berikut:

@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. Pelbagai Pangkalan Data di Spring Boot

Spring Boot dapat mempermudah konfigurasi di atas.

Secara lalai, Spring Boot akan menjadikan DataSource lalai dengan sifat konfigurasi yang diawali oleh spring.datasource. * :

spring.datasource.jdbcUrl = [url] spring.datasource.username = [username] spring.datasource.password = [password]

Kami sekarang ingin terus menggunakan cara yang sama untuk mengkonfigurasi DataSource kedua , tetapi dengan ruang nama harta benda yang berbeza:

spring.second-datasource.jdbcUrl = [url] spring.second-datasource.username = [username] spring.second-datasource.password = [password]

Kerana kami mahu konfigurasi automatik Spring Boot mengambil sifat-sifat yang berbeza (dan menunjukkan dua Sumber Data yang berbeza ), kami akan menentukan dua kelas konfigurasi yang serupa dengan yang terdapat di bahagian sebelumnya:

@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 } 

Kami telah menentukan sifat sumber data di dalam persistensi-multiple-db-boot.properties mengikut konvensyen Boot automatik

Bahagian yang menarik adalah anotasi kaedah pembuatan kacang sumber data dengan @ConfigurationProperties . Kita hanya perlu menentukan awalan konfigurasi yang sesuai . Di dalam kaedah ini, kami menggunakan DataSourceBuilder, dan Spring Boot secara automatik akan menguruskan selebihnya.

Tetapi bagaimana sifat yang dikonfigurasi disuntik ke dalam konfigurasi DataSource ?

Apabila memanggil kaedah build () pada DataSourceBuilder , ia akan memanggil kaedah binding peribadi () :

public T build() { Class type = getType(); DataSource result = BeanUtils.instantiateClass(type); maybeGetDriverClassName(); bind(result); return (T) result; }

Kaedah peribadi ini melakukan banyak keajaiban autokonfigurasi, mengikat konfigurasi yang diselesaikan dengan contoh DataSource yang sebenarnya :

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)); }

Walaupun kita tidak perlu menyentuh mana-mana kod ini, tetap berguna untuk mengetahui apa yang berlaku di bawah konfigurasi automatik Spring Boot.

Selain itu, konfigurasi kacang Transaction Manager dan Entity Manager adalah sama dengan aplikasi Spring standard.

7. Kesimpulannya

Artikel ini adalah gambaran umum praktikal tentang cara mengkonfigurasi projek JPA Data Spring anda untuk menggunakan pelbagai pangkalan data.

The pelaksanaan penuh artikel ini boleh didapati dalam projek GitHub - ini adalah projek berasaskan Maven, jadi ia harus mudah untuk import dan berjalan kerana ia adalah.