原文地址 https://blog.csdn.net/pengjunlee/article/details/80135271

本文仅对如何在 Springboot 中使用 Spring Data JPA 和 JdbcTemplate 去操作多个 HikariCP 数据源进行简单示例和介绍,项目的完整目录层次如下图所示。

SpringBoot重点详解--操作多数据源(JPA JdbcTemplate) - 图1

添加依赖与配置

为了使用 Spring Data JPA 和 HikariCP 数据源,需要在工程 POM 文件中引入它们的 Maven 依赖。

  1. <parent>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-parent</artifactId>
  4. <version>1.4.1.RELEASE</version>
  5. </parent>
  6. <dependencies>
  7. <!-- 添加MySQL依赖 -->
  8. <dependency>
  9. <groupId>mysql</groupId>
  10. <artifactId>mysql-connector-java</artifactId>
  11. </dependency>
  12. <!-- 添加JDBC依赖 -->
  13. <dependency>
  14. <groupId>org.springframework.boot</groupId>
  15. <artifactId>spring-boot-starter-data-jpa</artifactId>
  16. <exclusions>
  17. <!-- 排除Tomcat-JDBC依赖 -->
  18. <exclusion>
  19. <groupId>org.apache.tomcat</groupId>
  20. <artifactId>tomcat-jdbc</artifactId>
  21. </exclusion>
  22. </exclusions>
  23. </dependency>
  24. <!-- 添加HikariCP依赖 -->
  25. <dependency>
  26. <groupId>com.zaxxer</groupId>
  27. <artifactId>HikariCP</artifactId>
  28. </dependency>
  29. </dependencies>

在 application.properties 核心配置文件中除了要定义 MYSQL 数据库连接信息外,还需要添加如下 JPA 相关配置。

  1. #########################################################
  2. ### Primary DataSource -- DataSource 1 configuration ###
  3. #########################################################
  4. primary.datasource.jdbc-url=jdbc:mysql://localhost:3306/dev1?useUnicode=true&characterEncoding=utf8
  5. primary.datasource.driverClassName=com.mysql.jdbc.Driver
  6. primary.datasource.username=root
  7. primary.datasource.password=123456
  8. #########################################################
  9. ### Secondary DataSource -- DataSource 2 configuration ##
  10. #########################################################
  11. secondary.datasource.url=jdbc:mysql://localhost:3306/dev2?useUnicode=true&characterEncoding=utf8
  12. secondary.datasource.driverClassName=com.mysql.jdbc.Driver
  13. secondary.datasource.username=root
  14. secondary.datasource.password=123456
  15. secondary.datasource.type=com.zaxxer.hikari.HikariDataSource
  16. #########################################################
  17. ### Java Persistence Api -- Spring jpa configuration ###
  18. #########################################################
  19. # Specify the DBMS
  20. spring.jpa.database = MYSQL
  21. # Show or not log for each sql query
  22. spring.jpa.show-sql = true
  23. # Hibernate ddl auto (create, create-drop, update)
  24. spring.jpa.hibernate.ddl-auto = update
  25. # Naming strategy
  26. #[org.hibernate.cfg.ImprovedNamingStrategy #org.hibernate.cfg.DefaultNamingStrategy]
  27. spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.ImprovedNamingStrategy
  28. # stripped before adding them to the entity manager)
  29. spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect

配置数据源与 JdbcTemplate

首先,在 DataSourceConfig 配置类中定义两个数据源,同时为每一个数据源创建一个 JdbcTemplate。

  1. @Configuration
  2. public class DataSourceConfig {
  3. /**
  4. * 数据源 1
  5. */
  6. @Primary
  7. @Bean(name = "primaryDataSource")
  8. @ConfigurationProperties(prefix = "primary.datasource")
  9. public DataSource primaryDataSource() {
  10. return DataSourceBuilder.create().build();
  11. }
  12. @Primary
  13. @Bean(name = "secondaryDataSourceProperties")
  14. @ConfigurationProperties(prefix = "secondary.datasource")
  15. public DataSourceProperties secondaryDataSourceProperties() {
  16. return new DataSourceProperties();
  17. }
  18. /**
  19. * 数据源 2
  20. */
  21. @Bean(name = "secondaryDataSource")
  22. public DataSource thirdDataSource(
  23. @Qualifier("secondaryDataSourceProperties") DataSourceProperties dataSourceProperties) {
  24. return dataSourceProperties.initializeDataSourceBuilder().build();
  25. }
  26. /**
  27. * 数据源 1 的 JdbcTemplate
  28. */
  29. @Bean(name = "primaryJdbcTemplate")
  30. public JdbcTemplate primaryJdbcTemplate(@Qualifier("primaryDataSource") DataSource dataSource) {
  31. return new JdbcTemplate(dataSource);
  32. }
  33. /**
  34. * 数据源 2 的 JdbcTemplate
  35. */
  36. @Bean(name = "secondaryJdbcTemplate")
  37. public JdbcTemplate secondaryJdbcTemplate(@Qualifier("secondaryDataSource") DataSource dataSource) {
  38. return new JdbcTemplate(dataSource);
  39. }
  40. }

使用 JdbcTemplate 操作数据源

在 Dao 层中通过注入不同的 JdbcTemplate 来操作对应的数据源。

  1. /**
  2. * 使用 JdbcTemplate 操作数据源 1
  3. */
  4. @Repository
  5. public class UserDao {
  6. @Autowired
  7. @Qualifier("primaryJdbcTemplate")
  8. private JdbcTemplate jdbcTemplate;
  9. @Transactional
  10. public void addUser(Integer userAge, String userName) {
  11. String sql = "insert into tbl_user (age,name) values ('" + userAge + "','" + userName + "');";
  12. jdbcTemplate.execute(sql);
  13. }
  14. }
  1. /**
  2. * 使用 JdbcTemplate 操作数据源 2
  3. */
  4. @Repository
  5. public class DepartmentDao {
  6. @Autowired
  7. @Qualifier("secondaryJdbcTemplate")
  8. private JdbcTemplate jdbcTemplate;
  9. @Transactional
  10. public void addDept(String userName) {
  11. String sql = "insert into tbl_dept (name) values ('" + userName + "');";
  12. jdbcTemplate.execute(sql);
  13. }
  14. }

配置 JPA

通过注解 @EnableJpaRepositories 来为不同包下的 Repository 分别创建不同 TransactionManager 和 EntityManagerFactory,用来操作不同的数据源。

  1. /**
  2. * 数据源 1 JPA配置
  3. */
  4. @Configuration
  5. @EnableTransactionManagement
  6. @EnableJpaRepositories(entityManagerFactoryRef = "primaryEntityManagerFactory", transactionManagerRef = "primaryTransactionManager", basePackages = {
  7. "com.pengjunlee.primary.repository" }) // 设置Repository所在位置
  8. public class PrimaryConfig {
  9. @Autowired
  10. @Qualifier("primaryDataSource")
  11. private DataSource primaryDataSource;
  12. @Autowired
  13. private JpaProperties jpaProperties;
  14. @Primary
  15. @Bean(name = "primaryEntityManager")
  16. public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
  17. return primaryEntityManagerFactory(builder).getObject().createEntityManager();
  18. }
  19. @Primary
  20. @Bean(name = "primaryEntityManagerFactory")
  21. public LocalContainerEntityManagerFactoryBean primaryEntityManagerFactory(EntityManagerFactoryBuilder builder) {
  22. return builder.dataSource(primaryDataSource).properties(getVendorProperties(primaryDataSource))
  23. .packages("com.pengjunlee.primary.entity") // 设置实体类所在位置
  24. .persistenceUnit("primaryPersistenceUnit").build();
  25. }
  26. private Map<String, String> getVendorProperties(DataSource dataSource) {
  27. return jpaProperties.getHibernateProperties(dataSource);
  28. }
  29. @Primary
  30. @Bean(name = "primaryTransactionManager")
  31. public PlatformTransactionManager primaryTransactionManager(EntityManagerFactoryBuilder builder) {
  32. return new JpaTransactionManager(primaryEntityManagerFactory(builder).getObject());
  33. }
  34. }
  1. /**
  2. * 数据源 2 JPA配置
  3. */
  4. @Configuration
  5. @EnableTransactionManagement
  6. @EnableJpaRepositories(entityManagerFactoryRef = "secondaryEntityManagerFactory", transactionManagerRef = "secondaryTransactionManager", basePackages = {
  7. "com.pengjunlee.secondary.repository" }) // 设置Repository所在位置
  8. public class SecondaryConfig {
  9. @Autowired
  10. @Qualifier("secondaryDataSource")
  11. private DataSource secondaryDataSource;
  12. @Autowired
  13. private JpaProperties jpaProperties;
  14. @Bean(name = "secondaryEntityManager")
  15. public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
  16. return secondaryEntityManagerFactory(builder).getObject().createEntityManager();
  17. }
  18. @Bean(name = "secondaryEntityManagerFactory")
  19. public LocalContainerEntityManagerFactoryBean secondaryEntityManagerFactory(EntityManagerFactoryBuilder builder) {
  20. return builder.dataSource(secondaryDataSource).properties(getVendorProperties(secondaryDataSource))
  21. .packages("com.pengjunlee.secondary.entity") // 设置实体类所在位置
  22. .persistenceUnit("secondaryPersistenceUnit").build();
  23. }
  24. private Map<String, String> getVendorProperties(DataSource dataSource) {
  25. return jpaProperties.getHibernateProperties(dataSource);
  26. }
  27. @Bean(name = "secondaryTransactionManager")
  28. public PlatformTransactionManager secondaryTransactionManager(EntityManagerFactoryBuilder builder) {
  29. return new JpaTransactionManager(secondaryEntityManagerFactory(builder).getObject());
  30. }
  31. }

使用 JPA 操作数据源

由于是通过包名来区分哪些 Repository 用来操作哪个数据源,故而需要将不同数据源的 Entity 和 Repository 类放入不同的包中。

此处定义了 User(用户)和 Department(部门)两个实体类,分别对应数据源一和数据源二。

  1. @Entity
  2. @Table(name = "tbl_user")
  3. public class User {
  4. @Id
  5. @GeneratedValue(strategy = GenerationType.IDENTITY)
  6. private Long id;
  7. private String name;
  8. private Integer age;
  9. // 此处省略get和set方法
  10. }
  1. @Entity
  2. @Table(name = "tbl_dept")
  3. public class Department {
  4. @Id
  5. @GeneratedValue(strategy = GenerationType.IDENTITY)
  6. private Long id;
  7. private String name;
  8. // 此处省略get和set方法
  9. }
  1. /**
  2. * 使用 JPA 操作数据源 1
  3. */
  4. @Repository
  5. public interface UserRepository extends CrudRepository<User, Long> {
  6. }
  1. /**
  2. * 使用 JPA 操作数据源 2
  3. */
  4. @Repository
  5. public interface DepartmentRepository extends CrudRepository<Department, Long> {
  6. }

启动类中测试

在启动类中进行数据源测试,并分别向两个数据库中添加记录。

  1. @SpringBootApplication
  2. @EnableTransactionManagement // 只会回滚运行期异常
  3. public class MyApplication {
  4. public static void main(String[] args) throws SQLException {
  5. ConfigurableApplicationContext context = SpringApplication.run(MyApplication.class, args);
  6. Object ds1 = context.getBean("primaryDataSource");
  7. System.out.println(ds1.getClass().getName());
  8. Object ds2 = context.getBean("secondaryDataSource");
  9. System.out.println(ds2.getClass().getName());
  10. UserRepository userRepository = context.getBean(UserRepository.class);
  11. User user1 = new User();
  12. user1.setAge(21);
  13. user1.setName("Tracy");
  14. userRepository.save(user1);
  15. DepartmentRepository deptRepository = context.getBean(DepartmentRepository.class);
  16. Department dept1 = new Department();
  17. dept1.setName("集团事业部");
  18. deptRepository.save(dept1);
  19. UserDao userDao = context.getBean(UserDao.class);
  20. userDao.addUser(30, "pengjunlee");
  21. DepartmentDao deptDao = context.getBean(DepartmentDao.class);
  22. deptDao.addDept("总裁办公室");
  23. }
  24. }

启动程序,两个数据库中的用户表和部门表中数据都能正常添加成功。

在实际项目中我们一般都会将定义好的 Repository 自动装配到 Service 层进行调用,此时要格外注意事务。

  1. @Service
  2. @Transactional("primaryTransactionManager")
  3. public class UserServiceImpl implements UserService {
  4. @Autowired
  5. private UserRepository userRepository;
  6. public void deleteUserById(Long id) {
  7. userRepository.delete(id);
  8. }
  9. }