个人博客


1、添加多数据源的配置

1.1、yaml配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
server:
port: 40300

spring:
application:
name: jpa-multi-datasource
datasource:
primary:
jdbc-url: jdbc:mysql://148.70.153.63:3306/ttms?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false
username: root
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
initialSize: 5
maxActive: 50
minIdle: 0
maxWait: 60000
useUnfairLock: true # 禁用公平锁
secondary:
jdbc-url: jdbc:mysql://148.70.153.63:3306/ttms?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false
username: root
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
initialSize: 3
maxActive: 10
minIdle: 0
maxWait: 60000
useUnfairLock: true # 禁用公平锁
jpa:
show-sql: true
hibernate:
ddl-auto: update
database: MYSQL

注意:

  1. 这里为了方便,2个数据源的配置是用同一个数据库。
  2. 如果使用默认的数据源,在SpringBoot2.x以后需要使用jdbc-url而非url,否则会报 java.lang.IllegalArgumentException: jdbcUrl is required with driverClassName.

1.2、数据源配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Configuration
public class DataSourceConfig {
@Primary
@Bean
@ConfigurationProperties(prefix = "spring.datasource.primary")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}

@Bean
@ConfigurationProperties(prefix = "spring.datasource.secondary")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
}

数据源使用的是SpringBoot2.x版本默认的HikariCP连接池。@Primary注解指定了主数据源。

1.3、JPA配置

1.3.1、Primary数据源的JPA配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
entityManagerFactoryRef="entityManagerFactoryPrimary",
transactionManagerRef="transactionManagerPrimary",
basePackages= { "net.zhaoxiaobin.jpa.dao.primary" }) //设置Repository所在位置
public class PrimaryConfig {
@Autowired
private DataSource primaryDataSource;

@Autowired
private JpaProperties jpaProperties;
@Autowired
private HibernateProperties hibernateProperties;

private Map<String, Object> getVendorProperties() {
return hibernateProperties.determineHibernateProperties(jpaProperties.getProperties(), new HibernateSettings());
}

@Primary
@Bean(name = "entityManagerPrimary")
public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
return entityManagerFactoryPrimary(builder).getObject().createEntityManager();
}

@Primary
@Bean(name = "entityManagerFactoryPrimary")
public LocalContainerEntityManagerFactoryBean entityManagerFactoryPrimary (EntityManagerFactoryBuilder builder) {
return builder.dataSource(primaryDataSource)
.packages("net.zhaoxiaobin.jpa.domain.primary") //设置实体类所在位置
.persistenceUnit("primaryPersistenceUnit")
.properties(getVendorProperties())
.build();
}

@Primary
@Bean(name = "transactionManagerPrimary")
public PlatformTransactionManager transactionManagerPrimary(EntityManagerFactoryBuilder builder) {
return new JpaTransactionManager(entityManagerFactoryPrimary(builder).getObject());
}
}

Repository配置:

1
2
3
public interface PrimaryRepository extends JpaRepository<Actor,Long> {

}

实体类配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Entity
@Table(name = "actor")
@Data
public class Actor {
/**
* 主键生成采用数据库自增方式,比如MySQL的AUTO_INCREMENT
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(name = "actor_name", nullable = false, length = 128, unique = true)
private String actorName;

@Column(name = "actor_age", nullable = false)
private int actorAge;

@Column(name = "actor_email", length = 64, unique = true)
private String actorEmail;

@Column(name = "create_time", nullable = false, length = 32)
private String createTime = DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss SSS");
}
1.3.2、Secondary数据源的JPA配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
entityManagerFactoryRef="entityManagerFactorySecondary",
transactionManagerRef="transactionManagerSecondary",
basePackages= { "net.zhaoxiaobin.jpa.dao.secondary" }) //设置Repository所在位置
public class SecondaryConfig {
@Autowired
private DataSource secondaryDataSource;

@Autowired
private JpaProperties jpaProperties;
@Autowired
private HibernateProperties hibernateProperties;

private Map<String, Object> getVendorProperties() {
return hibernateProperties.determineHibernateProperties(jpaProperties.getProperties(), new HibernateSettings());
}

@Bean(name = "entityManagerSecondary")
public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
return entityManagerFactorySecondary(builder).getObject().createEntityManager();
}

@Bean(name = "entityManagerFactorySecondary")
public LocalContainerEntityManagerFactoryBean entityManagerFactorySecondary (EntityManagerFactoryBuilder builder) {
return builder.dataSource(secondaryDataSource)
.packages("net.zhaoxiaobin.jpa.domain.secondary") //设置实体类所在位置
.persistenceUnit("secondaryPersistenceUnit")
.properties(getVendorProperties())
.build();
}

@Bean(name = "transactionManagerSecondary")
public PlatformTransactionManager transactionManagerSecondary(EntityManagerFactoryBuilder builder) {
return new JpaTransactionManager(entityManagerFactorySecondary(builder).getObject());
}
}

Repository配置:

1
2
3
public interface SecondaryRepository extends JpaRepository<User,Long> {

}

实体类配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Entity
@Table(name = "user")
@Data
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(name = "name",length = 64)
private String name;

@Column(name = "age")
private int age;
}

说明与注意

  • 在使用JPA的时候,需要为不同的数据源创建不同的package来存放对应的Entity和Repository,以便于配置类的分区扫描。
  • 类名上的注解@EnableJpaRepositories中指定Repository的所在位置。
  • LocalContainerEntityManagerFactoryBean创建的时候,指定Entity所在的位置。

2、测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class MultiDataSourceTest {
@Autowired
private PrimaryRepository primaryRepository;

@Autowired
private SecondaryRepository secondaryRepository;

@Test
public void testPrimary() {
List<Actor> actorList = new ArrayList<>();
for (int i = 0; i < 5; i++) {
Actor actor = new Actor();
actor.setActorName("actor" + i);
actor.setActorEmail("email" + i);
actor.setActorAge(i + 20);
actorList.add(actor);
}
primaryRepository.saveAll(actorList);

// 验证
// Assert.assertEquals(5, primaryRepository.findAll().size());
}

@Test
public void testSecondary() {
List<User> userList = new ArrayList<>();
for (int i = 0; i < 5; i++) {
User user = new User();
user.setName("userName" + i);
user.setAge(i);
userList.add(user);
}
secondaryRepository.saveAll(userList);

// 验证
// Assert.assertEquals(5, secondaryRepository.findAll().size());
}
}

3、工程结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
├── jpa-multi-datasource.iml
├── pom.xml
└── src
├── main
│   ├── java
│   │   └── net
│   │   └── zhaoxiaobin
│   │   └── jpa
│   │   ├── JpaMultiDatasourceApplication.java
│   │   ├── config
│   │   │   ├── DataSourceConfig.java
│   │   │   ├── PrimaryConfig.java
│   │   │   └── SecondaryConfig.java
│   │   ├── dao
│   │   │   ├── primary
│   │   │   │   └── PrimaryRepository.java
│   │   │   └── secondary
│   │   │   └── SecondaryRepository.java
│   │   └── domain
│   │   ├── primary
│   │   │   └── Actor.java
│   │   └── secondary
│   │   └── User.java
│   └── resources
│   ├── application.yml
│   └── hibernate.properties
└── test
└── java
└── net
└── zhaoxiaobin
└── jpa
└── MultiDataSourceTest.java

参考链接

代码地址