在Spring Data JPA中配置多个数据源是一种常见的需求,尤其是在微服务架构或者需要同时操作多个数据库的场景下。配置多个数据源涉及到多个DataSource
实例的定义以及与之对应的EntityManagerFactory
的设置。下面我将详细介绍如何在Spring Boot应用中配置多个数据源,并使用这些数据源与Spring Data JPA结合进行数据访问。
1. 添加依赖
确保你的项目中有Spring Data JPA和Spring Boot Starter Web的依赖。如果你使用的是Maven,可以添加以下依赖到pom.xml文件中:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- MySQL驱动依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- PostgreSQL驱动依赖 -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
2. 配置数据源
你需要为每个数据源定义一个DataSource
实例。可以通过application.properties
或Java配置类来完成配置。
2.1 使用application.properties配置数据源
在application.properties
文件中配置两个数据源的信息。
# application.properties
# MySQL data source configuration
spring.datasource.db1.url=jdbc:mysql://localhost:3306/db1
spring.datasource.db1.username=db1user
spring.datasource.db1.password=db1pass
spring.datasource.db1.driver-class-name=com.mysql.cj.jdbc.Driver
# PostgreSQL data source configuration
spring.datasource.db2.url=jdbc:postgresql://localhost:5432/db2
spring.datasource.db2.username=db2user
spring.datasource.db2.password=db2pass
spring.datasource.db2.driver-class-name=org.postgresql.Driver
2.2 使用Java配置类配置数据源
通过Java配置类来定义DataSource
实例。
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
@Configuration
public class DataSourceConfig {
@Bean(name = "dataSource1")
@ConfigurationProperties(prefix = "spring.datasource.db1")
public DataSource dataSource1() {
return DataSourceBuilder.create().build();
}
@Bean(name = "dataSource2")
@ConfigurationProperties(prefix = "spring.datasource.db2")
public DataSource dataSource2() {
return DataSourceBuilder.create().build();
}
}
3. 配置EntityManagerFactory
对于每个数据源,你需要配置一个EntityManagerFactory
。这可以通过LocalContainerEntityManagerFactoryBean
来完成。
3.1 使用Java配置类配置EntityManagerFactory
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import javax.sql.DataSource;
import java.util.Properties;
@Configuration
public class JpaConfig {
@Bean(name = "entityManagerFactory1")
public LocalContainerEntityManagerFactoryBean entityManagerFactory1(@Qualifier("dataSource1") DataSource dataSource1) {
LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
emf.setDataSource(dataSource1);
emf.setPackagesToScan("com.example.demo.entity.db1");
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
emf.setJpaVendorAdapter(vendorAdapter);
emf.setJpaProperties(additionalProperties());
return emf;
}
@Bean(name = "entityManagerFactory2")
public LocalContainerEntityManagerFactoryBean entityManagerFactory2(@Qualifier("dataSource2") DataSource dataSource2) {
LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
emf.setDataSource(dataSource2);
emf.setPackagesToScan("com.example.demo.entity.db2");
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
emf.setJpaVendorAdapter(vendorAdapter);
emf.setJpaProperties(additionalProperties());
return emf;
}
Properties additionalProperties() {
Properties properties = new Properties();
properties.setProperty("hibernate.hbm2ddl.auto", "update");
properties.setProperty("hibernate.show_sql", "true");
properties.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
return properties;
}
}
4. 配置TransactionManager
对于每个EntityManagerFactory
,你需要配置一个TransactionManager
。
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.orm.jpa.JpaTransactionManager;
import javax.persistence.EntityManagerFactory;
@Configuration
public class TransactionManagerConfig {
@Bean(name = "transactionManager1")
public JpaTransactionManager transactionManager1(@Qualifier("entityManagerFactory1") EntityManagerFactory emf1) {
return new JpaTransactionManager(emf1);
}
@Bean(name = "transactionManager2")
public JpaTransactionManager transactionManager2(@Qualifier("entityManagerFactory2") EntityManagerFactory emf2) {
return new JpaTransactionManager(emf2);
}
}
5. 创建实体类
为每个数据源创建对应的实体类。
// For db1
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity(name = "Product1")
public class Product1 {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private double price;
// Getters and setters
}
// For db2
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity(name = "Product2")
public class Product2 {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private double price;
// Getters and setters
}
6. 创建Repository接口
为每个实体类创建对应的Repository接口。
// For db1
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface ProductRepository1 extends JpaRepository<Product1, Long> {
}
// For db2
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface ProductRepository2 extends JpaRepository<Product2, Long> {
}
7. 创建Service类
在Service层中注入对应的Repository,并使用它们进行数据访问。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
public class ProductService {
private final ProductRepository1 productRepository1;
private final ProductRepository2 productRepository2;
@Autowired
public ProductService(ProductRepository1 productRepository1, ProductRepository2 productRepository2) {
this.productRepository1 = productRepository1;
this.productRepository2 = productRepository2;
}
@Transactional(transactionManager = "transactionManager1")
public void createProduct1(String name, double price) {
Product1 product = new Product1();
product.setName(name);
product.setPrice(price);
productRepository1.save(product);
}
@Transactional(transactionManager = "transactionManager2")
public void createProduct2(String name, double price) {
Product2 product = new Product2();
product.setName(name);
product.setPrice(price);
productRepository2.save(product);
}
@Transactional(readOnly = true, transactionManager = "transactionManager1")
public List<Product1> getAllProducts1() {
return productRepository1.findAll();
}
@Transactional(readOnly = true, transactionManager = "transactionManager2")
public List<Product2> getAllProducts2() {
return productRepository2.findAll();
}
}
8. 创建Controller
创建Controller来处理HTTP请求,并使用Service层的方法进行数据访问。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/products")
public class ProductController {
private final ProductService productService;
@Autowired
public ProductController(ProductService productService) {
this.productService = productService;
}
@PostMapping("/db1")
public void createProduct1(@RequestParam String name, @RequestParam double price) {
productService.createProduct1(name, price);
}
@PostMapping("/db2")
public void createProduct2(@RequestParam String name, @RequestParam double price) {
productService.createProduct2(name, price);
}
@GetMapping("/db1")
public List<Product1> getAllProducts1() {
return productService.getAllProducts1();
}
@GetMapping("/db2")
public List<Product2> getAllProducts2() {
return productService.getAllProducts2();
}
}
9. 总结
通过上述步骤,我们成功地配置了两个数据源,并为每个数据源定义了对应的EntityManagerFactory
、TransactionManager
以及Repository接口。这种方式使得Spring Boot应用能够同时操作多个数据库,并保持数据的一致性和事务的正确性。