Spring JPA - Beberapa Database

1. Ikhtisar

Dalam tutorial ini, kami akan menerapkan konfigurasi Spring sederhana untuk sistem JPA Data Spring dengan beberapa database .

2. Entitas

Pertama - mari kita buat dua entitas sederhana - masing-masing hidup dalam database terpisah.

Berikut adalah entitas " 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 entitas 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 entitas juga ditempatkan dalam paket independen - ini akan menjadi penting saat kita memasuki konfigurasi.

3. Repositori JPA

Selanjutnya - 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 kami membuat dua repositori ini dalam paket yang berbeda.

4. Konfigurasi JPA Dengan Java

Selanjutnya - mari kita ke konfigurasi Spring yang sebenarnya. Kita akan mulai dengan menyiapkan dua kelas konfigurasi - satu untuk Pengguna dan yang lainnya untuk Produk .

Di setiap kelas konfigurasi ini, kita perlu menentukan antarmuka berikut untuk Pengguna :

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

Mari kita mulai 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 kami menggunakan userTransactionManager sebagai Primary TransactionManager - dengan memberi anotasi definisi kacang dengan @Primary . Itu berguna setiap kali kita akan secara implisit atau eksplisit memasukkan manajer transaksi tanpa menentukan nama yang mana.

Selanjutnya, mari kita bahas PersistenceProductConfiguration - di mana kita mendefinisikan kacang yang 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. Tes Sederhana

Terakhir - mari kita uji konfigurasi kita.

Kami akan mencoba tes sederhana dengan membuat instance dari setiap entitas dan memastikannya dibuat - seperti pada 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. Beberapa Database di Spring Boot

Spring Boot dapat mempermudah konfigurasi di atas.

Secara default, Spring Boot akan membuat instance DataSource default dengan properti 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 mengonfigurasi Sumber Data kedua , tetapi dengan namespace properti yang berbeda:

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

Karena kami ingin konfigurasi otomatis Spring Boot mengambil properti yang berbeda tersebut (dan membuat instance dua Sumber Data yang berbeda ), kami akan menentukan dua kelas konfigurasi yang serupa dengan yang ada di bagian 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 properti sumber data di dalam persistence-multiple-db-boot.properties sesuai dengan konvensi konfigurasi otomatis Boot.

Bagian yang menarik adalah menganotasi metode pembuatan kacang sumber data dengan @ConfigurationProperties . Kami hanya perlu menentukan awalan konfigurasi yang sesuai . Di dalam metode ini, kami menggunakan DataSourceBuilder, dan Spring Boot akan secara otomatis menangani sisanya.

Tetapi bagaimana properti yang dikonfigurasi dimasukkan ke dalam konfigurasi Sumber Data ?

Saat memanggil metode build () pada DataSourceBuilder , itu akan memanggil metode private bind () :

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

Metode privat ini melakukan banyak keajaiban konfigurasi otomatis, mengikat konfigurasi yang diselesaikan ke instance 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)); }

Meskipun kita tidak perlu menyentuh kode ini sendiri, tetap berguna untuk mengetahui apa yang terjadi di balik konfigurasi otomatis Spring Boot.

Selain itu, konfigurasi kacang Manajer Transaksi dan Manajer Entitas sama dengan aplikasi Spring standar.

7. Kesimpulan

Artikel ini adalah gambaran umum praktis tentang cara mengonfigurasi project JPA Spring Data Anda untuk menggunakan beberapa database.

The implementasi penuh dari artikel ini dapat ditemukan dalam proyek GitHub - ini adalah proyek berbasis Maven, sehingga harus mudah untuk impor dan berjalan seperti itu.