Spring春风拂面系列18---SpringBoot动态实现多数据源配置
Posted JavGop
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring春风拂面系列18---SpringBoot动态实现多数据源配置相关的知识,希望对你有一定的参考价值。
@重庆 八一好吃街
一、创建一个多数据源配置类
创建一个Spring配置类,定义两个DataSource用来读取application.properties
中的不同配置。如下例子中,主数据源配置为spring.datasource.primary
开头的配置,第二数据源配置为spring.datasource.secondary
开头的配置。
对JdbcTemplate的支持比较简单,只需要为其注入对应的datasource即可,如下例子,在创建JdbcTemplate的时候分别注入名为primaryDataSource
和secondaryDataSource
的数据源来区分不同的JdbcTemplate。
@Configuration
public class DataSourceConfig {
@Bean(name = "primaryDataSource")
@Qualifier("primaryDataSource")
@ConfigurationProperties(prefix="spring.datasource.primary")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "secondaryDataSource")
@Qualifier("secondaryDataSource")
@Primary
@ConfigurationProperties(prefix="spring.datasource.secondary")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "primaryJdbcTemplate")
public JdbcTemplate primaryJdbcTemplate(
@Qualifier("primaryDataSource") DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
@Bean(name = "secondaryJdbcTemplate")
public JdbcTemplate secondaryJdbcTemplate(
@Qualifier("secondaryDataSource") DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
}
二、application.properties中DataSource配置
#多数据源支持
spring.datasource.primary.url=jdbc:mysql://localhost:3306/test?tinyInt1isBit=false&useUnicode=true&characterEncoding=utf8
spring.datasource.primary.username=root
spring.datasource.primary.password=root
spring.datasource.primary.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.secondary.url=jdbc:mysql://localhost:3366/test?tinyInt1isBit=false&useUnicode=true&characterEncoding=utf8
spring.datasource.secondary.username=root
spring.datasource.secondary.password=root
spring.datasource.secondary.driver-class-name=com.mysql.jdbc.Driver
三、调用使用
/**
* 多数据源的使用demo
*/
@Service
public class KOLDataService {
private org.slf4j.Logger logger = LoggerFactory.getLogger(KOLDataService.class);
/***********引入多个JdbcTemplate************/
@Autowired
@Qualifier("primaryJdbcTemplate")
protected JdbcTemplate primaryJdbcTemplate;
@Autowired
@Qualifier("secondaryJdbcTemplate")
protected JdbcTemplate secondaryJdbcTemplate;
public JSONObject query(String user_ids){
JSONObject data = new JSONObject();
//primary库查询用户手机号
String query = "select user_id,mobile_id from user where user_id in ("+user_ids+") ";
List<JSONObject> listData = primaryJdbcTemplate.query(query, new RowMapper<JSONObject>() {
@Override
public JSONObject mapRow(ResultSet rs, int rowNum) throws SQLException {
JSONObject one = new JSONObject();
one.put("user_id",rs.getInt("user_id"));
one.put("mobile_id",rs.getString("mobile_id"));
return one;
}
});
for(int i = 0;i<listData.size;i++){
JSONObject one = listData.get(i);
//secondary库根据用户名、手机号得到它的历史浏览信息
query = "select title,content,create_time from message_info "+
"where user_id="+one.getInt("user_id")+" and mobile_id = \""+one.getString("mobile_id")+"\"";
List<JSONObject> messageData = secondaryJdbcTemplate.query(query, new RowMapper<JSONObject>() {
@Override
public JSONObject mapRow(ResultSet rs, int rowNum) throws SQLException {
JSONObject one = new JSONObject();
one.put("user_id",rs.getInt("user_id"));
one.put("mobile_id",rs.getString("mobile_id"));
return one;
}
});
//讲历史浏览信息插入到指定用户下
one.put("info_list",messageData);
listData.set(i,one);
}
//封装数据
data.put("count",listData.size);
data.put("list",listData);
return data;
}
}
四、可能存在的问题
如果application.properties中datasource使用的是上述中的application.properties中DataSource配置的话,项目运行8个小时(为啥就8小时呢?继续往下看就明白了)之后将会出现如下错误信息:
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: No operations allowed after connection closed.
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
at com.mysql.jdbc.Util.handleNewInstance(Util.java:404)
at com.mysql.jdbc.Util.getInstance(Util.java:387)
之所以会出现这个异常,是因为MySQL5.0以后针对超长时间DB连接做了一个处理,那就是如果一个DB连接在无任何操作情况下过了8个小时后(Mysql 服务器默认的“wait_timeout”是8小时),Mysql会自动把这个连接关闭。这就是问题的所在,在连接池中的connections如果空闲超过8小时,mysql将其断开,而连接池自己并不知道该connection已经失效,如果这时有 Client请求connection,连接池将该失效的Connection提供给Client,将会造成上面的异常。
所以配置datasource时需要配置相应的连接池参数,定是去检查连接的有效性,定时清理无效的连接。
解决办法:
在application.properties的primary数据源的配置下添加如下连接池配置:
#以下为连接池的相关参数配置
spring.datasource.primary.max-idle=10
spring.datasource.primary.max-wait=10000
spring.datasource.primary.min-idle=5
spring.datasource.primary.initial-size=5
spring.datasource.primary.validation-query=SELECT 1
spring.datasource.primary.test-on-borrow=false
spring.datasource.primary.test-while-idle=true
spring.datasource.primary.time-between-eviction-runs-millis=18800
在application.properties的primary数据源的配置下添加如下连接池配置:
#以下为连接池的相关参数配置
spring.datasource.secondary.max-idle=10
spring.datasource.secondary.max-wait=10000
spring.datasource.secondary.min-idle=5
spring.datasource.secondary.initial-size=5
spring.datasource.secondary.validation-query=SELECT 1
spring.datasource.secondary.test-on-borrow=false
spring.datasource.secondary.test-while-idle=true
spring.datasource.secondary.time-between-eviction-runs-millis=18800
这样上述异常就得到解决,其实出现该问题的原因,还是自己本人当时忽略了连接池的相关配置,以及对一些常见连接池的配置尚有欠缺,这里再补充一个小知识点,spring boot默认会优先使用的连接池是tomcat连接池,前提是在tomcat连接池可用的情况下
Reference:
https://www.jianshu.com/p/34730e595a8c
https://www.jianshu.com/p/1626d41572f2
以上是关于Spring春风拂面系列18---SpringBoot动态实现多数据源配置的主要内容,如果未能解决你的问题,请参考以下文章