SpringBoot + Hikari +JPA 多数据源

SpringBoot + Hikari +JPA 多数据源

SpringBoot2.0 Hikari 多数据源 —— JPA

SpringBoot 连接多数据源使用 JPA 查询应用记录.

pom.xml依赖配置

1
2
3
4
5
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-jpa -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

注:SpringBoot 2.0默认的数据连接池为Hikari,故不需要添加 Hikari 依赖

多数据源配置

  1. primaryDataSource 配置
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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
package com.learnning.config;

import javax.persistence.EntityManager;
import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import com.zaxxer.hikari.HikariDataSource;

/**
*
* @ClassName: HikariDataSourceConfig
* @Description: Hikari 多数据源配置(第一数据源配置)
* @author time
* @date 2018/10/29
* {@link {@link http://blog.didispace.com/springbootmultidatasource/}}
*/
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(entityManagerFactoryRef = "primaryEntityManagerFactory", //
transactionManagerRef = "primaryTransactionManager", //
basePackages = { "com.learnning.domain.p" }) // 设置Repository所在位置
public class PrimaryConfig {

@Primary
@Bean(name = "primaryDataSource")
@Qualifier(value = "primaryDataSource")
@ConfigurationProperties(prefix = "spring.datasource.primary")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().type(HikariDataSource.class).build();
}

@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()) //
.packages("com.learnning.domain.p")//
.persistenceUnit("primaryPersistenceUnit")//
.build();
}

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

}
  1. sencondaryDataSource 配置
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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package com.learnning.config;

import javax.persistence.EntityManager;
import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import com.zaxxer.hikari.HikariDataSource;

/**
*
* @ClassName: HikariDataSourceConfig
* @Description: Hikari 多数据源配置(第一数据源配置)
* @author time
* @date 2018/10/29
* {@link {@link http://blog.didispace.com/springbootmultidatasource/}}
*/
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(entityManagerFactoryRef = "secondaryEntityManagerFactory", //
transactionManagerRef = "secondaryTransactionManager", //
basePackages = { "com.learnning.domain.s" }) // 设置Repository所在位置
public class SecondaryConfig {

@Bean(name = "sencondaryDataSource")
@Qualifier(value = "sencondaryDataSource")
@ConfigurationProperties(prefix = "spring.datasource.secondary")
public DataSource sencondaryDataSource() {
return DataSourceBuilder.create().type(HikariDataSource.class).build();
}

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

@Bean(name = "secondaryEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean secondaryEntityManagerFactory(EntityManagerFactoryBuilder builder) {
return builder//
.dataSource(sencondaryDataSource()) //
.packages("com.learnning.domain.s")//
.persistenceUnit("secondaryPersistenceUnit")//
.build();
}

@Bean(name = "secondaryTransactionManager")
PlatformTransactionManager transactionManager(EntityManagerFactoryBuilder builder) {
return new JpaTransactionManager(secondaryEntityManagerFactory(builder).getObject());
}

}

JPA 使用多数据源查询

划重点:

  1. JPAentity与table因为有映射关系,所以实体类路径需要映射到对应的DataSource

  2. JPA查询接口最好也根据不同数据源区分开

primary数据源配置

  • primary数据源实体类
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
42
43
44
45
46
47
48
49
50
package com.learnning.domain.p;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "t_account")
public class User {

@Id
@GeneratedValue
private Long account_id;

private String user_name;

private Integer validity;

public Long getAccount_id() {
return account_id;
}

public void setAccount_id(Long account_id) {
this.account_id = account_id;
}


public String getUser_name() {
return user_name;
}

public void setUser_name(String user_name) {
this.user_name = user_name;
}

public Integer getValidity() {
return validity;
}

public void setValidity(Integer validity) {
this.validity = validity;
}

@Override
public String toString() {
return "{\"account_id\":\"" + account_id + "\",\"user_name\":\"" + user_name + "\",\"validity\":\"" + validity
+ "\"}";
}
}
  • primary 数据源Repository
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.learnning.domain.p.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;

import com.learnning.domain.p.User;

public interface UserRepository extends JpaRepository<User, Long> {

/**
*
* @Title: findByAccount_id
* @Description: 查询用户 User 信息
* nativeQuery:指定为原生SQL查询
* @param account_id
* @return
* User
*/
/// 模糊匹配
@Query(value = "select account_id,user_name,validity from t_account where account_id = ?", nativeQuery = true)
User findByAccount_id(Long account_id);

}

secondary 数据源配置

  • secondary 实体类
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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package com.learnning.domain.s;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "t_user")
public class PopUser {

@Id
@GeneratedValue
private Long pop_id;

private String pop_name;

private String ip_start;

private String ip_end;

private String netmask;

private String description;

private String start_num;

private String end_num;

private String platform;

private Integer ipgap;

private Integer pop_type;

public Long getPop_id() {
return pop_id;
}

public void setPop_id(Long pop_id) {
this.pop_id = pop_id;
}

public String getPop_name() {
return pop_name;
}

public void setPop_name(String pop_name) {
this.pop_name = pop_name;
}

public String getIp_start() {
return ip_start;
}

public void setIp_start(String ip_start) {
this.ip_start = ip_start;
}

public String getIp_end() {
return ip_end;
}

public void setIp_end(String ip_end) {
this.ip_end = ip_end;
}

public String getNetmask() {
return netmask;
}

public void setNetmask(String netmask) {
this.netmask = netmask;
}

public String getDescription() {
return description;
}

public void setDescription(String description) {
this.description = description;
}

public String getStart_num() {
return start_num;
}

public void setStart_num(String start_num) {
this.start_num = start_num;
}

public String getEnd_num() {
return end_num;
}

public void setEnd_num(String end_num) {
this.end_num = end_num;
}

public String getPlatform() {
return platform;
}

public void setPlatform(String platform) {
this.platform = platform;
}

public Integer getIpgap() {
return ipgap;
}

public void setIpgap(Integer ipgap) {
this.ipgap = ipgap;
}

public Integer getPop_type() {
return pop_type;
}

public void setPop_type(Integer pop_type) {
this.pop_type = pop_type;
}

@Override
public String toString() {
return "{\"pop_id\":\"" + pop_id + "\",\"pop_name\":\"" + pop_name + "\",\"ip_start\":\"" + ip_start
+ "\",\"ip_end\":\"" + ip_end + "\",\"netmask\":\"" + netmask + "\",\"description\":\"" + description
+ "\",\"start_num\":\"" + start_num + "\",\"end_num\":\"" + end_num + "\",\"platform\":\"" + platform
+ "\",\"ipgap\":\"" + ipgap + "\",\"pop_type\":\"" + pop_type + "\"}";
}

}
  • secondary 数据源 Repository
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.learnning.domain.s.repository;

import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;

import com.learnning.domain.s.PopUser;

public interface PopUserRepository extends JpaRepository<PopUser, Long> {

@Query(value = "select pop_name,pop_id,ip_start,ip_end,netmask,description,start_num,end_num,platform,ipgap,pop_type from t_user where rownum<= 10", nativeQuery = true)
List<PopUser> queryPopUserPage1();

}

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Autowired
private UserRepository userRepository;

@Autowired
private PopUserRepository popUserRepository;

@Test
public void testPrimarySession() {
User users = userRepository.findByAccount_id(1L);
System.out.println(users.toString());

List<PopUser> page = popUserRepository.queryPopUserPage1();
System.out.println(page.toString());

}

注: SpringBoot官方建议使用构造函数的方式注入依赖,上述测试代码为在测试类中使用.

使用构造函数注入如下:

1
2
3
4
5
6
7
8
9
private UserRepository userRepository;

private PopUserRepository popUserRepository;

@Autowired
public MyErrorController(UserRepository userRepository, PopUserRepository popUserRepository) {
this.userRepository = userRepository;
this.popUserRepository = popUserRepository;
}