Spring JPA –複数のデータベース
1. 概要
このチュートリアルでは、Spring Data JPA system with multiple databasesの単純なSpring構成を実装します。
参考文献:
Spring BootでのプログラムによるDataSourceの構成
Spring Boot DataSourceをプログラムで構成する方法を学び、それによってSpringBootの自動DataSource構成アルゴリズムを回避します。
2. エンティティ
まず、2つの単純なエンティティを作成しましょう。それぞれが別々のデータベースに存在します。
これが最初のエンティティ「User」です。
package com.example.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;
}
そして2番目のエンティティ–“Product“:
package com.example.multipledb.model.product;
@Entity
@Table(schema = "products")
public class Product {
@Id
private int id;
private String name;
private double price;
}
ご覧のとおり、the two entities are also placed in independent packages –これは構成に移行するときに重要になります。
参考文献:
Spring BootでのプログラムによるDataSourceの構成
Spring Boot DataSourceをプログラムで構成する方法を学び、それによってSpringBootの自動DataSource構成アルゴリズムを回避します。
3. JPAリポジトリ
次に、2つのJPAリポジトリを見てみましょう–UserRepository:
package com.example.multipledb.dao.user;
public interface UserRepository
extends JpaRepository { }
AndProductRepository:
package com.example.multipledb.dao.product;
public interface ProductRepository
extends JpaRepository { }
繰り返しますが、これら2つのリポジトリを異なるパッケージに作成した方法に注意してください。
4. JavaでJPAを構成する
次へ–実際のSpring構成に取り掛かりましょう。 We’ll start by setting up 2 configuration classes – one for the User and the other for the Product。
この構成クラスのそれぞれで、Userに対して次のインターフェースを定義する必要があります。
-
情報源
-
EntityManagerFactory(userEntityManager)
-
TransactionManager(userTransactionManager)
まず、ユーザー構成を確認します。
@Configuration
@PropertySource({ "classpath:persistence-multiple-db.properties" })
@EnableJpaRepositories(
basePackages = "com.example.multipledb.dao.user",
entityManagerFactoryRef = "userEntityManager",
transactionManagerRef = "userTransactionManager"
)
public class UserConfig {
@Autowired
private Environment env;
@Bean
@Primary
public LocalContainerEntityManagerFactoryBean userEntityManager() {
LocalContainerEntityManagerFactoryBean em
= new LocalContainerEntityManagerFactoryBean();
em.setDataSource(userDataSource());
em.setPackagesToScan(
new String[] { "com.example.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;
}
}
Bean定義に@Primaryアノテーションを付けることにより、userTransactionManagerをPrimary TransactionManagerとして使用していることに注目してください。 これは、名前でトランザクションマネージャーを指定せずに、暗黙的または明示的にトランザクションマネージャーを挿入する場合に役立ちます。
次に、ProductConfigについて説明します–ここで同様のBeanを定義します。
@Configuration
@PropertySource({ "classpath:persistence-multiple-db.properties" })
@EnableJpaRepositories(
basePackages = "com.example.multipledb.dao.product",
entityManagerFactoryRef = "productEntityManager",
transactionManagerRef = "productTransactionManager"
)
public class ProductConfig {
@Autowired
private Environment env;
@Bean
public LocalContainerEntityManagerFactoryBean productEntityManager() {
LocalContainerEntityManagerFactoryBean em
= new LocalContainerEntityManagerFactoryBean();
em.setDataSource(productDataSource());
em.setPackagesToScan(
new String[] { "com.example.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. 簡単なテスト
最後に、構成をテストしましょう。
次の例のように、各エンティティのインスタンスを作成して簡単にテストし、作成されていることを確認します。
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { UserConfig.class, ProductConfig.class })
@TransactionConfiguration
public class JPAMultipleDBTest {
@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の複数のデータベース
Spring Bootは上記の構成を簡素化できます。
デフォルトでは、Spring Boot will instantiate its default DataSource with the configuration properties prefixed by spring.datasource.*:
spring.datasource.jdbcUrl = [url]
spring.datasource.username = [username]
spring.datasource.password = [password]
configure the second DataSource, but with a different property namespace:と同じ方法を使い続けたいと思います
spring.second-datasource.jdbcUrl = [url]
spring.second-datasource.username = [username]
spring.second-datasource.password = [password]
Spring Boot自動構成でこれらの異なるプロパティを取得する(そして実際には2つの異なるDataSourcesをインスタンス化する)必要があるため、前のセクションと同様の2つの構成クラスを定義します。
@Configuration
@PropertySource({"classpath:persistence-multiple-db-boot.properties"})
@EnableJpaRepositories(
basePackages = "com.example.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.example.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内にデータソースプロパティを定義しました。
興味深い部分はannotating the data source bean creation method with*@ConfigurationProperties*.です。対応する構成プレフィックス.を指定する必要があります。このメソッド内では、DataSourceBuilder,を使用しており、SpringBootが自動的に処理します。残り。
しかし、構成されたプロパティは実際にどのようにしてDataSource構成に注入されるのでしょうか。
DataSourceBuilderでbuild()メソッドを呼び出すと、プライベートbind()メソッドが呼び出されます。
public T build() {
Class type = getType();
DataSource result = BeanUtils.instantiateClass(type);
maybeGetDriverClassName();
bind(result);
return (T) result;
}
このプライベートメソッドは、自動構成の魔法の多くを実行し、解決された構成を実際のDataSourceインスタンスにバインドします。
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));
}
このコードに自分で触れる必要はありませんが、SpringBoot自動構成の内部で何が起こっているのかを知ることは依然として有用です。
これに加えて、Transaction ManagerおよびEntity Manager Beanの構成は、標準のSpringアプリケーションと同じです。
7. 結論
この記事は、複数のデータベースを使用するようにSpring Data JPAプロジェクトを構成する方法の実用的な概要でした。
この記事のfull implementationは、the GitHub projectにあります。これはMavenベースのプロジェクトであるため、そのままインポートして実行するのは簡単です。