动态多数据源连接池

动态多数据源连接池

用了这么多年的开源软件,第一次回报开源《spring-boot-easy-connection-pool》。有些朋友可能觉得这个不是很有必要开源,
但是可以简化别人实现逻辑的开发成本,以一个优雅的方式解决问题,为什么不能开源呢?

抛出问题问题

1. SpringBoot之前,老式这种配置系统是否使用了连接池?

1
2
3
4
5
<bean id="dataSource"  class="org.springframework.jdbc.datasource.DriverManagerDataSource"
p:driverClassName="com.mysql.jdbc.Driver"
p:url="jdbc:mysql://localhost:3306/login?useUnicode=true&characterEncoding=UTF-8"
p:username="root"
p:password="root" />

2. SpringBoot之后,新式的这种配置系统是否使用了连接池?

1
2
3
4
5
6
spring:
datasource:
url: jdbc:mysql://localhost:3306/stumanage?characterEncoding=utf-8
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver

3. 主从两个数据源要怎么配置?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
spring:
datasource:
primary: # 主数据源
# mysql 连接配置
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/test?useUnicode=true&allowMultiQueries=true&characterEncoding=utf8&useSSL=false
username: # 用户名
password: # 密码

secondary: # 次数据源
# mysql 连接配置
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/test1?useUnicode=true&allowMultiQueries=true&characterEncoding=utf8&useSSL=false
username: # 用户名
password: # 密码
3.1. 就这么配置就行了吗?当然不是,是不是还要有java代码中主从的Bean配置?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Configuration
public class DataSourceConfig {
@Bean(name = "primaryDataSource")
@Qualifier("primaryDataSource")
@Primary
@ConfigurationProperties(prefix = "spring.datasource.primary")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}

@Bean(name = "secondaryDataSource")
@Qualifier("secondaryDataSource")
@ConfigurationProperties(prefix = "spring.datasource.secondary")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
}
3.2. 就这么配置就行了吗?当然不是,是不是还要有事务管理的Bean配置?
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
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
entityManagerFactoryRef = "entityManagerFactoryPrimary",
transactionManagerRef = "transactionManagerPrimary",
basePackages = {"com.test.testdemo.repository.primary"}) // 指定该数据源操作的DAO接口包

public class PrimaryConfig {
@Autowired
@Qualifier("primaryDataSource")
private DataSource primaryDataSource;

@Autowired
private JpaProperties jpaProperties;

@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)
.properties(getVendorProperties(primaryDataSource))
.packages("com.test.testdemo.entity.primary") //设置实体类所在位置
.persistenceUnit("primaryPersistenceUnit")
.build();
}

private Map<String, String> getVendorProperties(DataSource dataSource) {
jpaProperties.setDatabase(Database.MYSQL);
Map<String,String> map = new HashMap<>();
map.put("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
map.put("hibernate.ddl-auto", "update");
map.put("hibernate.physical_naming_strategy", "org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl");
jpaProperties.setProperties(map);
return jpaProperties.getHibernateProperties(dataSource);
}

@Primary
@Bean(name = "transactionManagerPrimary")
public PlatformTransactionManager transactionManagerPrimary(EntityManagerFactoryBuilder builder) {
return new JpaTransactionManager(entityManagerFactoryPrimary(builder).getObject());
}
}
3.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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
entityManagerFactoryRef = "entityManagerFactorySecondary",
transactionManagerRef = "transactionManagerSecondary",
basePackages = {"com.test.testdemo.repository.secondary"})
public class SecondaryConfig {
@Autowired
@Qualifier("secondaryDataSource")
private DataSource secondaryDataSource;
@Autowired
private JpaProperties jpaProperties;

@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)
.properties(getVendorProperties(secondaryDataSource))
.packages("com.test.testdemo.entity.secondary") //设置实体类所在包的位置
.persistenceUnit("primaryPersistenceUnit")
.build();
}

/**
* 对数据源连接的表进行DDL(正向生成表、程序启动动态更新表)
*
* @return
*/
private Map<String, String> getVendorProperties(DataSource dataSource) {
jpaProperties.setDatabase(Database.MYSQL);
Map<String, String> map = new HashMap<>();
map.put("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");//mysql方言
map.put("hibernate.hbm2ddl.auto", "update");//反向生成
map.put("hibernate.physical_naming_strategy", "org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl");
jpaProperties.setProperties(map);
return jpaProperties.getHibernateProperties(dataSource);
}

@Bean(name = "transactionManagerSecondary")
PlatformTransactionManager transactionManagerSecondary(EntityManagerFactoryBuilder builder) {
return new JpaTransactionManager(entityManagerFactorySecondary(builder).getObject());
}
}
3.4. 第二个问题,他们使用了连接池了吗?

4. 第3步是一主一从的设置方法,如果从库有个十个八个要怎么配置呢?

5. 针对业务分片的数据源客户端可以使用针对key的定向存储,那么如何配置大数据组件Impala、ClickHouse这种每个库都有几十个节点的分布式库呢?

6. 市面上有好用的组件可以解决5中提到分布式库的问题吗?这些组件都有对连接建立了连接池吗?

7. 我的连接数据源有十几几十个还放在配置文件中管理、使用Bean配置吗?市面上有好用的插件可以解决吗?你要怎么用代码解决这个问题?

8. 我的项目数据源需要根据当前的业务key<这个key并没有存储在目标数据源中>,动态指定连接的数据源要怎么实现呢?市面上有什么好用的插件吗?

9. 分布式库如何提高他们的负载能力呢,有哪些负载策略呢?

以上问题spring-boot-easy-connection-pool都可以用一种优雅的方式解决。怎么使用?

A. 该项目资源已提交maven中央仓库,可以直接引入maven

1
2
3
4
5
<dependency>
<groupId>com.xieahui.springboot</groupId>
<artifactId>spring-boot-easy-connection-pool</artifactId>
<version>1.1.1-RELEASE</version>
</dependency>

B. 启动入口

1
@EnableDynamicDataSource

C. Bean中各种配置太繁琐,注解指定?

1
@TargetDataSource("db3")

D. 支持分组,负载均衡?

1
@TargetDataSource(groupName = "db5_group")

E. 想要根据业务key<业务key不在目标数据库中、不是动态分片>,动态指定?

1
2
3
DbEntity dbEntity = myDb3Dao.findById(id);
//设置连接池名称
DynamicDbSource.setGroupName(dbEntity.getGroupName());

以上,详细内容请参考:
[!spring-boot-easy-connection-pool]https://github.com/xieyucan/spring-boot-easy-connection-pool