Spring春风拂面系列18---SpringBoot动态实现多数据源配置

Posted JavGop

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring春风拂面系列18---SpringBoot动态实现多数据源配置相关的知识,希望对你有一定的参考价值。

Spring春风拂面系列18---SpringBoot动态实现多数据源配置

@重庆 八一好吃街

一、创建一个多数据源配置类

创建一个Spring配置类,定义两个DataSource用来读取application.properties中的不同配置。如下例子中,主数据源配置为spring.datasource.primary开头的配置,第二数据源配置为spring.datasource.secondary开头的配置。

对JdbcTemplate的支持比较简单,只需要为其注入对应的datasource即可,如下例子,在创建JdbcTemplate的时候分别注入名为primaryDataSourcesecondaryDataSource的数据源来区分不同的JdbcTemplate。

@Configurationpublic 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=utf8spring.datasource.primary.username=rootspring.datasource.primary.password=rootspring.datasource.primary.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.secondary.url=jdbc:mysql://localhost:3366/test?tinyInt1isBit=false&useUnicode=true&characterEncoding=utf8spring.datasource.secondary.username=rootspring.datasource.secondary.password=rootspring.datasource.secondary.driver-class-name=com.mysql.jdbc.Driver

三、调用使用

/** * 多数据源的使用demo */@Servicepublic 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动态实现多数据源配置的主要内容,如果未能解决你的问题,请参考以下文章

读史三叹

耦合

Spring~ | 系一条丝巾 去追逐温柔的春风

语文教学新阶梯

《假如爱有天意》月光如春风拂面,你如种子深埋我心

《假如爱有天意》月光如春风拂面,你如种子深埋我心