1. Einführung
In diesem kurzen Tutorial werden wir lernen, wie man mehrere Datenquellen in einer Spring Boot-Anwendung konfiguriert.
Stellen wir uns vor, wir arbeiten an einer E-Commerce-App und benötigen zwei separate Datenbanken, um Kunden und Bestellungen zu speichern. Wir werden diese als separate Datenquellen konfigurieren und sehen, wie wir sie in unserer Anwendung nutzen können.
2. Konfigurieren von Eigenschaften
Lassen Sie uns damit beginnen, einige Eigenschaften in application.properties
für unsere Customer- und Order-Datenquellen zu definieren:
#Customer db
spring.datasource.customer.url = [url]
spring.datasource.customer.username = [username]
spring.datasource.customer.password = [password]
spring.datasource.customer.driverClassName = org.postgresql.Driver
#Order db
spring.datasource.order.url = [url]
spring.datasource.order.username = [username]
spring.datasource.order.password = [password]
spring.datasource.order.driverClassName = org.sqlite.JDBC
Oben definieren wir die Datenquellen für die beiden Datenbanken und deren jeweilige Eigenschaften. Darüber hinaus können wir verschiedene Arten von Datenbanken konfigurieren.
Nun können wir diese Eigenschaften in unserer Anwendung verwenden.
3. Konfigurieren von Datenquellen
Als nächstes erstellen wir eine Konfigurationsklasse, in der wir zwei DataSource
Beans für jede Datenbank definieren:
@Configuration
public class CustomerOrderDataSourcesConfiguration {
@Primary
@Bean(name = "customerDataSource")
@ConfigurationProperties(prefix = "spring.datasource.customer")
public DataSource customerDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "orderDataSource")
@ConfigurationProperties(prefix = "spring.datasource.order")
public DataSource orderDataSource() {
return DataSourceBuilder.create().build();
}
}
Wir benennen unsere Beans explizit, damit wir mithilfe der @Qualifier
Annotation entscheiden können, auf welche Datenquelle wir uns beziehen möchten. Zusätzlich haben wir die customerDataSource
als @Primary
markiert und werden sie als Hauptdatenbank für unsere Anwendung betrachten. Falls wir also die @Qualifier
Annotation nicht verwenden, wird Spring die Kundendatenquelle dort einfügen, wo sie benötigt wird.
Lassen Sie uns sehen, wie wir diese Beans verwenden können.
4. Verwendung der Datenquellen
Wir können die beiden Datenquellen bei Bedarf in unsere Anwendung injizieren. Zum Beispiel können wir die @Autowired
Annotation verwenden, um die Datenquelle in eine Serviceklasse zu injizieren:
@Autowired
@Qualifier("orderDataSource")
private DataSource orderDataSource;
Allerdings möchten wir meistens die Datenquelle mit einem Spring Data JPA-Repository verbinden und dieses verwenden, anstatt direkt mit der Datenquelle zu interagieren.
Schauen wir uns an, wie wir das als Nächstes konfigurieren können.
5. Konfigurieren von @Repository
mit einer spezifischen Datenquelle
Zuerst müssen wir eine Konfigurationsklasse erstellen, in der wir die Annotation @EnableJpaRepositories
verwenden, um die Repositories zu scannen und die Entity Manager Factory für jede Datenquelle zu definieren:
@Configuration
@EnableJpaRepositories(
basePackages = "com.namastecode.repositories.customer",
entityManagerFactoryRef = "customerEntityManagerFactory",
transactionManagerRef = "customerTransactionManager"
)
public class CustomerDataSourceConfiguration{
// More code
}
Nun müssen wir den entityManagerFactory
Bean innerhalb dieser Klasse erstellen:
@Primary
@Bean(name = "customerEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean customerEntityManagerFactory(
EntityManagerFactoryBuilder builder,
@Qualifier("customerDataSource") DataSource dataSource) {
return builder
.dataSource(dataSource)
.packages("com.namastecode.domain.customer") // entities package
.persistenceUnit("customers")
.build();
}
Als nächstes werden wir den transactionManager
-Bean erstellen, sodass jede Datenquelle ihren eigenen Transaktionsmanager hat und sie sich nicht gegenseitig stören:
@Primary
@Bean(name = "customerTransactionManager")
public PlatformTransactionManager customerTransactionManager(
@Qualifier("customerEntityManagerFactory") EntityManagerFactory customerEntityManagerFactory) {
return new JpaTransactionManager(customerEntityManagerFactory);
}
Oben definieren wir beide Beans als @Primary
, da die Kundendatenquelle die Standardquelle ist. Für die Bestelldatenquelle müssen wir das nicht tun.
Wir haben alles bereit, um unsere Entitäten im Paket com.namastecode.domain.customer
zu erstellen:
@Entity
@Table(name = "customers")
public class Customer {
// More code
}
Ähnlich können wir die Repositories unter dem Paket com.namastecode.repositories.customer
erstellen:
@Repository
public interface CustomerRepository extends JpaRepository<Customer, Long> {
// More code
}
Nun haben wir zwei separate Datenquellen mit ihren Ressourcen. Es könnte jedoch eine Situation auftreten, in der wir die Datenquellen so konfigurieren möchten, dass wir eine einzige Transaktion über beide Datenquellen hinweg verwenden. Lassen Sie uns als Nächstes sehen, wie wir das tun können.
6. Konfiguration eines Chained Transaction Manager
Wenn wir Transaktionen über beide Datenquellen hinweg durchführen möchten, können wir einen ChainedTransactionManager
-Bean einrichten, den wir später in unseren Operationen verwenden können:
@Bean(name = "chainedTransactionManager")
public ChainedTransactionManager chainedTransactionManager(
@Qualifier("customerTransactionManager") PlatformTransactionManager customerTransactionManager,
@Qualifier("orderTransactionManager") PlatformTransactionManager orderTransactionManager) {
return new ChainedTransactionManager(customerTransactionManager, orderTransactionManager);
}
Angenommen, wir haben einen CustomerOrderService
, in dem wir sowohl CustomerRepository
als auch OrderRepository
verwenden. Um unseren verketteten Transaktionsmanager in dieser Service-Klasse zu nutzen, können wir den Transaktionsmanager explizit definieren:
@Service
public class CustomerOrderService {
@Autowired
private CustomerRepository customerRepository;
@Autowired
private OrderRepository orderRepository;
@Transactional(transactionManager = "chainedTransactionManager")
public void updateCustomerAndOrder(Customer customer, Order order) {
customerRepository.save(customer);
orderRepository.save(order);
}
}
Oben, wenn eine der Speichervorgänge fehlschlägt, wird die Transaktion für beide Datenquellen zurückgesetzt.
7. Fazit
In diesem Tutorial haben wir gelernt, wie wir mehrere Datenquellen in einer Spring Boot-Anwendung konfigurieren können.
Zuerst haben wir die Eigenschaften jeder Datenquelle definiert, die Datenquellen-Beans erstellt, das @Repository
mit einer spezifischen Datenquelle konfiguriert und schließlich gesehen, wie man einen gemeinsamen Transaktionsmanager für beide Datenquellen konfiguriert.