Spring Boot 使用HikariCP和MyBatis配置多数据源支持

Spring Boot 2 开始,默认的数据库连接池是HikariCP,一般没有特殊定制化的需求,HikariCP都是推荐的数据库连接池组件。MyBatis也是现在使用较多的ORM框架,一般都是选择使用它。

多数据源的配置需求很容易遇到,这方面的文档官方并没有提供直接的example支持,大部分情况下,确实单数据源就已经满足需求。

1、启动排除自动配置类

也可以不操作,Spring Boot 默认的Bean处理,会根据实际的Bean来依赖实例化特定的类,如果没有足够的条件,框架也不是无穷无尽的去实例化它认为需要的实例。

import org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration;
@SpringBootApplication(
exclude = {
DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class,
JdbcTemplateAutoConfiguration.class,
MybatisAutoConfiguration.class
}
)
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

2、配置文件

# 数据源配置
datasource:
a:
jdbc-url: jdbc:mysql://ip:port/dbName?characterEncoding=utf8&useSSL=true


username: username
password: .....
# 以下为 HikariCP 连接池信息
pool-name: HikariPool-a
minimum-idle: 4
maximum-pool-size: 10
b:
jdbc-url: jdbc:mysql://ip:port/dbName?characterEncoding=utf8&useSSL=true
username: userName
password: ......
pool-name: HikariPool-b
minimum-idle: 2
maximum-pool-size: 5

3、数据源配置类

数据源配置类需要,每个数据源要有单独的配置类,虽然内容基本上是一样的,但是扫描包的路径是需要明确分开的。

这里面有个坑,说实话我不知道是网上的文章太久了,还是大家的版本不一致,导致一些解决方案总是失效的。比如下面这个配置:

@Bean("aSqlSessionFactory")
public SqlSessionFactory sqlSessionFactory(@Qualifier("aDataSource") DataSource dataSource) throws Exception {
// 重要: 注意设置的顺序, SqlSessionFactoryBean 设置属性后再获取 getObject, 否则不生效
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
// 如果有 *Mapper.xml 接口实现需要配置, 请在该处配置
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mappers/a/*Mapper.xml"));
// 支持自动转化驼峰
bean.getObject().getConfiguration().setMapUnderscoreToCamelCase(true);
return bean.getObject();
}


你注意到 bean.getObject()这行了吗,如果这之后设置扫描xml接口实现文件,那将是不生效的,但我在网上搜索到的答案中,都是在这一行之后扫描。我在这里调试了很久,就差没看MyBatis的源代码了。后来只能打印配置的内容,才发现这个顺序问题。我不知道写这个的人自己有没有调试过,还是知道有这么个配置项,具体生效不生效,需要使用者自行去确定。

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
/**
* a 数据源
*/
@Configuration
@MapperScan(
basePackages = "com.....",
sqlSessionTemplateRef = "ASqlSessionTemplate"
)
public class ADatasourceConfig {
/**
* a 数据源
*
* @return 默认指定的连接池为框架默认的 {@link com.zaxxer.hikari.HikariDataSource}
*/
@Bean("aDataSource")


@ConfigurationProperties(prefix = "datasource.miio")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
@Bean("aSqlSessionFactory")
public SqlSessionFactory sqlSessionFactory(@Qualifier("aDataSource") DataSource dataSource) throws Exception {
// 重要: 注意设置的顺序, SqlSessionFactoryBean 设置属性后再获取 getObject, 否则不生效
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
// 如果有 *Mapper.xml 接口实现需要配置, 请在该处配置
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mappers/a/*Mapper.xml"));
// 支持自动转化驼峰
bean.getObject().getConfiguration().setMapUnderscoreToCamelCase(true);
return bean.getObject();
}
/**
* 使用Spring声明式事务时需要明确指定事务管理器
*
* @param dataSource 对应的数据源
* @return 需要使用者明确引用的事务管理器
*/
@Bean("aDataSourceTransactionManager")
public PlatformTransactionManager platformTransactionManager(@Qualifier("aDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean("aSqlSessionTemplate")
public SqlSessionTemplate sqlSessionTemplate(@Qualifier("aSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
/**
* jdbcTemplate 数据库操作模板, 可单独使用操作数据库
*
* @param dataSource a 数据源
* @return a 数据操作模板
*/
@Bean("aJdbcTemplate")
public JdbcTemplate jdbcTemplate(@Qualifier("aDataSource") DataSource dataSource) {
return new JdbcTemplate(dataSource);
}

}

4、Java注解和xml实现

如果使用Java注解去编写SQL,那其实无需提供任何的xml接口实现文件。对于大部分的SQL来说,确实一个@Select("")注解比什么都方便,也不需要过多的配置。但是@Update("")就没那么容易了,比如我的Where条件是不固定的,有的值可能并不存在,或者Set的内容也是动态的。我并不想为每种情形编写一个@Update方法,注解实现就显得有点啰嗦和不那么容易了。

这时候xml配置的优势就体现出来,xml提供的特定格式巧妙的解决了很多问题,可以避开复杂的SQL语法,比如CASE WHEN条件等等,就像下面这个,我的where有两个条件,而且后一个可能为空的时候,就特别的直接和简单:


br> PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper>

rid,
refund_id as "refundId",
oid,
uid,
pid,
status,
`count`


<select>
SELECT
<include>
FROM shop_refound_order
WHERE oid = #{orderId}

AND pid IN
<foreach>
#{item}
/<foreach>

/<select>
/<mapper>

你看订单条件肯定存在,但是后一个IN条件就可能为空,这时候用xml来编写就变得极其容易了。

实际项目中,一般统一为一种方式,也可以两种并用,为了减轻复杂性,一般是一个项目保持一种风格,只要是解决了项目的问题,其实选择哪种意义不大。