1. 基本环境搭建
博客中有非常多的文章讲springboot 2.x + mybatis 搭建环境,这里就不展开了一步一步细讲了,只对关键的需要注意的点进行描述展示。
1. 1 依赖引入
通过Maven新建一个基本的springboot项目,在其依赖中加入一下关键包,具体版本号参考注释,要注意版本大更新引起的依赖变化。有一点要注意,下面的依赖组成只是形成一个能支持mybatis的小项目,要支持其他如web功能,在根据需要添加其他依赖。
<dependencies>
<!-- version: 2.1.0 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<!-- 5.1.46 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- 1.1.18 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
</dependency>
<!-- 2.0.4 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
1.2 构建 mybatis 的基本目录结构
源码包下基本包:domain、mapper;资源目录下的mybatis/sqlmap目录(放置xxxMapper.xml文件)。mybatis-config.xml是mybatis的配置文件,可选,因为基本的配置在application.yml中就可以配置。
然后在启动类中打开mybatis的扫描开关,扫描目录对于上面的mapper包结构
@SpringBootApplication
@MapperScan(basePackages = {"com.leon.mybatis.mapper"}) // 这里可以指定多个,看看源码,这个basepackages属性是个数组
public class MybatisApplication {
public static void main(String[] args) {
SpringApplication.run(MybatisApplication.class, args);
}
}
1.3 基本配置
经过上面两步的收拾,在配置一下数据库连接就可以测试类,基本配置如下:
mybatis:
configuration:
map-underscore-to-camel-case: true # 配置数据库和属性的驼峰映射
mapper-locations: classpath:mybatis/sqlmap/*/*.xml # xxxMapper.xml的目录,也可以配置多个目录,逗号分隔
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3307/mybatis
username: root
password: root
type: com.alibaba.druid.pool.DruidDataSource # 使用druid数据源,上面依赖引入类alibaba的DruidDataSource连接池
driver-class-name: com.mysql.jdbc.Driver
logging:
level:
com.leon.mybatis.mapper=TRACE # 开启对于mapper下的sql输出
1.4 最后
最后就是没有了,上面3步就搞定了,写个简单查询测试下就行,在详细的参考这个连接?Spring boot Mybatis 整合(完整版)
2. DruidDataSource连接池下的多数据源实现
有时面向 *** 固定 *** 多数据源的需求时,代码也得支持多数据源,所幸在springboot和mybatis组合下,这一点也不难,咱们还是快速实现一个小工程,中间穿插需要注意的要点,拒绝啰嗦。
2.1 依赖引入
还是快速搭建一个普通的springboot项目,然后依赖结构如下:
<!-- version: 2.1.0 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<!-- 1.1.18 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
</dependency>
<!-- 5.1.46 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- 2.0.4 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
2.2 构建包结构
包结构大致组成和上面一样,区别只是分了不同的业务方向(也就是不同的数据库映射),如图:
在上图中的domain包、mapper包和资源目录mybatis/sqlmap下都根据不同数据源分成了primary和secondary,这个要注意一下,下面配置多个数据源会用得到。另外在config包中,会放置每个数据源的初始化Config。
2.3 属性配置
# 双数据源配置
datasource:
primary:
url: jdbc:mysql://127.0.0.1:3307/primary
username: root
password: root
driverClassName: com.mysql.jdbc.Driver
secondary:
url: jdbc:mysql://127.0.0.1:3307/secondary
username: root
password: root
driverClassName: com.mysql.jdbc.Driver
datasource.primary和datasource.secondary是自定义的,关键是后面的子属性,这些都是构建数据库连接池所必须,其他性能属性,参考 附录1
2.4 分别声明初始化数据源
多个数据源下,在spring的上下文中会生成多个Datasource的对象,这本来是各单例的对象,现在存在多个,就要声明其中一个是主,那我们首先看这个主数据源的config代码
@Configuration
@MapperScan(basePackages = {"com.leon.multimybatis.mapper.primary"}, sqlSessionFactoryRef = "primarySqlSessionFactory")
public class PrimaryDatasourceConfiguration {
@Primary
@Bean(name="primaryDataSource")
@ConfigurationProperties(prefix = "datasource.primary")
public DataSource primaryDataSource() {
return new DruidDataSource();
}
@Primary
@Bean(name = "primarySqlSessionFactory")
public SqlSessionFactory primarySqlSessionFactory(@Qualifier("primaryDataSource")DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
// mybatis配置采用了xml文件形式,通过这种方式放入到mybatis上下文中
bean.setConfigLocation(new PathMatchingResourcePatternResolver()
.getResource("classpath:mybatis/mybatis-config.xml"));
// 指定要扫描的mapper.xml文件路径,注意不同数据的xml文件路径要区分开
Resource[] mapInfo = new PathMatchingResourcePatternResolver()
.getResources("classpath:mybatis/sqlmap/primary/*.xml");
bean.setMapperLocations(mapInfo);
return bean.getObject();
}
}
这个configuration中,需要注意的有一下几点:
- 在类上添加
@MapperScan
注解,在basePackages属性中配置这个数据源需要管理的Mapper,注意路径。同时在属性sqlSessionFactoryRef中配置下面方法中的SqlSessionFactory
实例的名称 - 类中的第一个方法,声明
DruidDataSource
数据源,并指定这个Bean的名称,要和其他数据区分开,同时添加Primary
注解,即声明成主Datasource。注意@ConfigurationProperties
注解,里面的prefix值就是资源文件中对于数据库连接信息的前缀 - 类中第二个方法,声明主
SqlSessionFactory
,在其参数中用@Qualifier
注解指定要注入哪个DataSource
(多数据源会有多个),其他看一下注释把。
在把第二个的数据源的声明代码贴上,基本一样,只是不再是主数据源,没有了@Primary
注释,其他就是对应路径的配置需要注意。
@Configuration
@MapperScan(basePackages = {"com.leon.multimybatis.mapper.secondary"}, sqlSessionFactoryRef = "secondarySqlSessionFactory")
public class SecondaryDatasourceConfiguration {
@Bean(name="secondaryDataSource")
@ConfigurationProperties(prefix="datasource.secondary")
public DataSource secondaryDataSource() {
return new DruidDataSource();
}
@Bean(name = "secondarySqlSessionFactory")
public SqlSessionFactory secondarySqlSessionFactory(@Qualifier("secondaryDataSource")DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setConfigLocation(new PathMatchingResourcePatternResolver().getResource("classpath:mybatis/mybatis-config.xml"));
Resource[] mapInfo = new PathMatchingResourcePatternResolver().getResources("classpath:mybatis/sqlmap/secondary/*.xml");
bean.setMapperLocations(mapInfo);
return bean.getObject();
}
}
2.5 测试一下
用单元测试搞一下,测试代码如下:
@RunWith(SpringRunner.class)
@SpringBootTest
public class MultiMybatisApplicationTests {
// UserInfoMapper 位于 com.leon.multimybatis.mapper.secondary 包下
@Autowired
private UserInfoMapper userInfoMapper;
// SpamEmailMapper 位于 com.leon.multimybatis.mapper.primary 包下
@Autowired
private SpamEmailMapper spamEmailMapper;
@Test
public void testMultiDB(){
List<UserInfo> userList = userInfoMapper.findAll();
List<SpamEmailReport> emailList = spamEmailMapper.findByCondition(null, -1, 0, 0, 10);
userList.forEach(user -> System.out.println(user.getSubscribedArray()));
System.out.println("------------------------");
emailList.forEach(email -> System.out.println(email.getSpamEmailAddress()));
}
}
UserInfoMapper
和SpamEmailMapper
分别位于两个数据源mapperScan的目录下,也就是分别用来不同的数据源,输出结果部分如下
com.alibaba.druid.pool.DruidDataSource : {dataSource-1} inited
com.alibaba.druid.pool.DruidDataSource : {dataSource-2} inited
['default']
['default']
['default']
['default']
['default']
['default']
['default']
['default']
['default']
------------------------
whsntml@xYD.cnjww.com
111@cxcx.com
222@coco.com
444@coco.com
bbb@zom.com
333@cxcx.com
111@coco.com
s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@71075444: startup date [Thu Jul 18 17:05:00 CST 2019]; root of context hierarchy
com.alibaba.druid.pool.DruidDataSource : {dataSource-1} closing ...
com.alibaba.druid.pool.DruidDataSource : {dataSource-1} closed
com.alibaba.druid.pool.DruidDataSource : {dataSource-2} closing ...
com.alibaba.druid.pool.DruidDataSource : {dataSource-2} closed
控制台输出可以看出,分别有两个数据启动了。
如果要在加几个数据源,就是在配置中添加对于的连接信息,在新增初始化类就可以。
3. 动态数据源切换
上面一个单数据源,一个双数据源,都是在数据固定的情况下,如果数据源不固定,或者读写分离,上面的写法就太啰嗦和冗余了,硬编码的缺点就很明显了,下面我们试试通过代码实现按需切换数据源。
动态数据源比较复杂,大家还是参考一下两个博主的内容吧:
Spring Boot + Mybatis多数据源和动态数据源配置
SpringBoot + Mybatis + Druid多数据源集成的心得
附录1
com.alibaba.druid.pool.DruidDataSource 基本配置参数如下:
配置 | 缺省值 | 说明 |
---|---|---|
name | 配置这个属性的意义在于,如果存在多个数据源,监控的时候可以通过名字来区分开来。 如果没有配置,将会生成一个名字,格式是:“DataSource-” + System.identityHashCode(this) | |
jdbcUrl | 连接数据库的url,不同数据库不一样。例如: mysql : jdbc:mysql://10.20.153.104:3306/druid2 oracle : jdbc:oracle:thin:@10.20.149.85:1521:ocnauto | |
username | 连接数据库的用户名 | |
password | 连接数据库的密码。如果你不希望密码直接写在配置文件中,可以使用ConfigFilter。详细看这里:https://github.com/alibaba/druid/wiki/%E4%BD%BF%E7%94%A8ConfigFilter | |
driverClassName | 根据url自动识别 这一项可配可不配,如果不配置druid会根据url自动识别dbType,然后选择相应的driverClassName(建议配置) | |
initialSize | 0 | 初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时 |
maxActive | 8 | 最大连接池数量 |
maxIdle | 8 | 已经不再使用,配置了也没效果 |
minIdle | 最小连接池数量 | |
maxWait | 获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。 | |
poolPreparedStatements | false | 是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。 |
maxOpenPreparedStatements | -1 | 要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100 |
validationQuery | 用来检测连接是否有效的sql,要求是一个查询语句。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会其作用。 | |
testOnBorrow | true | 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 |
testOnReturn | false | 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能 |
testWhileIdle | false | 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 |
timeBetweenEvictionRunsMillis | 有两个含义: 1) Destroy线程会检测连接的间隔时间;2) testWhileIdle的判断依据,详细看testWhileIdle属性的说明 | |
numTestsPerEvictionRun | 不再使用,一个DruidDataSource只支持一个EvictionRun | |
minEvictableIdleTimeMillis | ||
connectionInitSqls | 物理连接初始化的时候执行的sql | |
exceptionSorter | 根据dbType自动识别 | 当数据库抛出一些不可恢复的异常时,抛弃连接 |
filters | 属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有: 监控统计用的filter:stat日志用的filter:log4j防御sql注入的filter:wall | |
proxyFilters | 类型是List<com.alibaba.druid.filter.Filter>,如果同时配置了filters和proxyFilters,是组合关系,并非替换关系 |