原文地址 https://blog.csdn.net/pengjunlee/article/details/80135271
本文仅对如何在 Springboot 中使用 Spring Data JPA 和 JdbcTemplate 去操作多个 HikariCP 数据源进行简单示例和介绍,项目的完整目录层次如下图所示。
添加依赖与配置
为了使用 Spring Data JPA 和 HikariCP 数据源,需要在工程 POM 文件中引入它们的 Maven 依赖。
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.1.RELEASE</version>
</parent>
<dependencies>
<!-- 添加MySQL依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- 添加JDBC依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<exclusions>
<!-- 排除Tomcat-JDBC依赖 -->
<exclusion>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jdbc</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 添加HikariCP依赖 -->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</dependency>
</dependencies>
在 application.properties 核心配置文件中除了要定义 MYSQL 数据库连接信息外,还需要添加如下 JPA 相关配置。
#########################################################
### Primary DataSource -- DataSource 1 configuration ###
#########################################################
primary.datasource.jdbc-url=jdbc:mysql://localhost:3306/dev1?useUnicode=true&characterEncoding=utf8
primary.datasource.driverClassName=com.mysql.jdbc.Driver
primary.datasource.username=root
primary.datasource.password=123456
#########################################################
### Secondary DataSource -- DataSource 2 configuration ##
#########################################################
secondary.datasource.url=jdbc:mysql://localhost:3306/dev2?useUnicode=true&characterEncoding=utf8
secondary.datasource.driverClassName=com.mysql.jdbc.Driver
secondary.datasource.username=root
secondary.datasource.password=123456
secondary.datasource.type=com.zaxxer.hikari.HikariDataSource
#########################################################
### Java Persistence Api -- Spring jpa configuration ###
#########################################################
# Specify the DBMS
spring.jpa.database = MYSQL
# Show or not log for each sql query
spring.jpa.show-sql = true
# Hibernate ddl auto (create, create-drop, update)
spring.jpa.hibernate.ddl-auto = update
# Naming strategy
#[org.hibernate.cfg.ImprovedNamingStrategy #org.hibernate.cfg.DefaultNamingStrategy]
spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.ImprovedNamingStrategy
# stripped before adding them to the entity manager)
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect
配置数据源与 JdbcTemplate
首先,在 DataSourceConfig 配置类中定义两个数据源,同时为每一个数据源创建一个 JdbcTemplate。
@Configuration
public class DataSourceConfig {
/**
* 数据源 1
*/
@Primary
@Bean(name = "primaryDataSource")
@ConfigurationProperties(prefix = "primary.datasource")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
@Primary
@Bean(name = "secondaryDataSourceProperties")
@ConfigurationProperties(prefix = "secondary.datasource")
public DataSourceProperties secondaryDataSourceProperties() {
return new DataSourceProperties();
}
/**
* 数据源 2
*/
@Bean(name = "secondaryDataSource")
public DataSource thirdDataSource(
@Qualifier("secondaryDataSourceProperties") DataSourceProperties dataSourceProperties) {
return dataSourceProperties.initializeDataSourceBuilder().build();
}
/**
* 数据源 1 的 JdbcTemplate
*/
@Bean(name = "primaryJdbcTemplate")
public JdbcTemplate primaryJdbcTemplate(@Qualifier("primaryDataSource") DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
/**
* 数据源 2 的 JdbcTemplate
*/
@Bean(name = "secondaryJdbcTemplate")
public JdbcTemplate secondaryJdbcTemplate(@Qualifier("secondaryDataSource") DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
}
使用 JdbcTemplate 操作数据源
在 Dao 层中通过注入不同的 JdbcTemplate 来操作对应的数据源。
/**
* 使用 JdbcTemplate 操作数据源 1
*/
@Repository
public class UserDao {
@Autowired
@Qualifier("primaryJdbcTemplate")
private JdbcTemplate jdbcTemplate;
@Transactional
public void addUser(Integer userAge, String userName) {
String sql = "insert into tbl_user (age,name) values ('" + userAge + "','" + userName + "');";
jdbcTemplate.execute(sql);
}
}
/**
* 使用 JdbcTemplate 操作数据源 2
*/
@Repository
public class DepartmentDao {
@Autowired
@Qualifier("secondaryJdbcTemplate")
private JdbcTemplate jdbcTemplate;
@Transactional
public void addDept(String userName) {
String sql = "insert into tbl_dept (name) values ('" + userName + "');";
jdbcTemplate.execute(sql);
}
}
配置 JPA
通过注解 @EnableJpaRepositories 来为不同包下的 Repository 分别创建不同 TransactionManager 和 EntityManagerFactory,用来操作不同的数据源。
/**
* 数据源 1 JPA配置
*/
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(entityManagerFactoryRef = "primaryEntityManagerFactory", transactionManagerRef = "primaryTransactionManager", basePackages = {
"com.pengjunlee.primary.repository" }) // 设置Repository所在位置
public class PrimaryConfig {
@Autowired
@Qualifier("primaryDataSource")
private DataSource primaryDataSource;
@Autowired
private JpaProperties jpaProperties;
@Primary
@Bean(name = "primaryEntityManager")
public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
return primaryEntityManagerFactory(builder).getObject().createEntityManager();
}
@Primary
@Bean(name = "primaryEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean primaryEntityManagerFactory(EntityManagerFactoryBuilder builder) {
return builder.dataSource(primaryDataSource).properties(getVendorProperties(primaryDataSource))
.packages("com.pengjunlee.primary.entity") // 设置实体类所在位置
.persistenceUnit("primaryPersistenceUnit").build();
}
private Map<String, String> getVendorProperties(DataSource dataSource) {
return jpaProperties.getHibernateProperties(dataSource);
}
@Primary
@Bean(name = "primaryTransactionManager")
public PlatformTransactionManager primaryTransactionManager(EntityManagerFactoryBuilder builder) {
return new JpaTransactionManager(primaryEntityManagerFactory(builder).getObject());
}
}
/**
* 数据源 2 JPA配置
*/
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(entityManagerFactoryRef = "secondaryEntityManagerFactory", transactionManagerRef = "secondaryTransactionManager", basePackages = {
"com.pengjunlee.secondary.repository" }) // 设置Repository所在位置
public class SecondaryConfig {
@Autowired
@Qualifier("secondaryDataSource")
private DataSource secondaryDataSource;
@Autowired
private JpaProperties jpaProperties;
@Bean(name = "secondaryEntityManager")
public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
return secondaryEntityManagerFactory(builder).getObject().createEntityManager();
}
@Bean(name = "secondaryEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean secondaryEntityManagerFactory(EntityManagerFactoryBuilder builder) {
return builder.dataSource(secondaryDataSource).properties(getVendorProperties(secondaryDataSource))
.packages("com.pengjunlee.secondary.entity") // 设置实体类所在位置
.persistenceUnit("secondaryPersistenceUnit").build();
}
private Map<String, String> getVendorProperties(DataSource dataSource) {
return jpaProperties.getHibernateProperties(dataSource);
}
@Bean(name = "secondaryTransactionManager")
public PlatformTransactionManager secondaryTransactionManager(EntityManagerFactoryBuilder builder) {
return new JpaTransactionManager(secondaryEntityManagerFactory(builder).getObject());
}
}
使用 JPA 操作数据源
由于是通过包名来区分哪些 Repository 用来操作哪个数据源,故而需要将不同数据源的 Entity 和 Repository 类放入不同的包中。
此处定义了 User(用户)和 Department(部门)两个实体类,分别对应数据源一和数据源二。
@Entity
@Table(name = "tbl_user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private Integer age;
// 此处省略get和set方法
}
@Entity
@Table(name = "tbl_dept")
public class Department {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
// 此处省略get和set方法
}
/**
* 使用 JPA 操作数据源 1
*/
@Repository
public interface UserRepository extends CrudRepository<User, Long> {
}
/**
* 使用 JPA 操作数据源 2
*/
@Repository
public interface DepartmentRepository extends CrudRepository<Department, Long> {
}
启动类中测试
在启动类中进行数据源测试,并分别向两个数据库中添加记录。
@SpringBootApplication
@EnableTransactionManagement // 只会回滚运行期异常
public class MyApplication {
public static void main(String[] args) throws SQLException {
ConfigurableApplicationContext context = SpringApplication.run(MyApplication.class, args);
Object ds1 = context.getBean("primaryDataSource");
System.out.println(ds1.getClass().getName());
Object ds2 = context.getBean("secondaryDataSource");
System.out.println(ds2.getClass().getName());
UserRepository userRepository = context.getBean(UserRepository.class);
User user1 = new User();
user1.setAge(21);
user1.setName("Tracy");
userRepository.save(user1);
DepartmentRepository deptRepository = context.getBean(DepartmentRepository.class);
Department dept1 = new Department();
dept1.setName("集团事业部");
deptRepository.save(dept1);
UserDao userDao = context.getBean(UserDao.class);
userDao.addUser(30, "pengjunlee");
DepartmentDao deptDao = context.getBean(DepartmentDao.class);
deptDao.addDept("总裁办公室");
}
}
启动程序,两个数据库中的用户表和部门表中数据都能正常添加成功。
在实际项目中我们一般都会将定义好的 Repository 自动装配到 Service 层进行调用,此时要格外注意事务。
@Service
@Transactional("primaryTransactionManager")
public class UserServiceImpl implements UserService {
@Autowired
private UserRepository userRepository;
public void deleteUserById(Long id) {
userRepository.delete(id);
}
}