本篇文章主要讲述的是SpringBoot整合Mybatis、Druid和PageHelper 并实现多数据源和分页。其中SpringBoot整合Mybatis这块,之前有一篇文章已经说明过了,这里就不在详细说明。重点是讲述在多数据源下的如何配置使用Druid和PageHelper 。
在使用Druid之前,先来简单的了解下Druid。
Druid是一个数据库连接池。Druid可以说是目前最好的数据库连接池!因其优秀的功能、性能和扩展性方面,深受开发人员的青睐。
Druid已经在阿里巴巴部署了超过600个应用,经过一年多生产环境大规模部署的严苛考验。Druid是阿里巴巴开发的号称为监控而生的数据库连接池!
同时Druid不仅仅是一个数据库连接池,Druid 核心主要包括三部分:
Druid的主要功能如下:
废话不多说,直接上代码:
4.0.0
com.renrengou
renrengou-mange
0.0.1-SNAPSHOT
jar
renrengou-mange
Demo project for Spring Boot
com.renren
renrengou-v1
0.0.1-SNAPSHOT
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-test
com.renrengou
renrengou-pojo
0.0.1-SNAPSHOT
org.mybatis.spring.boot
mybatis-spring-boot-starter
1.3.2
mysql
mysql-connector-java
runtime
com.github.pagehelper
pagehelper-spring-boot-starter
1.2.5
com.alibaba
druid-spring-boot-starter
1.1.9
org.springframework.boot
spring-boot-maven-plugin
spring:
datasource:
#使用druid连接池
type: com.alibaba.druid.pool.DruidDataSource
# 自定义的主数据源配置信息
primary:
datasource:
#druid相关配置
druid:
#监控统计拦截的filters
filters: stat
driverClassName: com.mysql.jdbc.Driver
#配置基本属性
url: jdbc:mysql://****:3306/renren-shop?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
username: root
password: ***
#配置初始化大小/最小/最大
initialSize: 1
minIdle: 1
maxActive: 20
#获取连接等待超时时间
maxWait: 60000
#间隔多久进行一次检测,检测需要关闭的空闲连接
timeBetweenEvictionRunsMillis: 60000
#一个连接在池中最小生存的时间
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 'x'
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
#打开PSCache,并指定每个连接上PSCache的大小。oracle设为true,mysql设为false。分库分表较多推荐设置为false
poolPreparedStatements: false
maxPoolPreparedStatementPerConnectionSize: 20
# 自定义的从数据源配置信息
back:
datasource:
#druid相关配置
druid:
#监控统计拦截的filters
filters: stat
driverClassName: com.mysql.jdbc.Driver
#配置基本属性
url: jdbc:mysql://****:3306/renren-mange?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
username: root
password: ***
#配置初始化大小/最小/最大
initialSize: 1
minIdle: 1
maxActive: 20
#获取连接等待超时时间
maxWait: 60000
#间隔多久进行一次检测,检测需要关闭的空闲连接
timeBetweenEvictionRunsMillis: 60000
#一个连接在池中最小生存的时间
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 'x'
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
#打开PSCache,并指定每个连接上PSCache的大小。oracle设为true,mysql设为false。分库分表较多推荐设置为false
poolPreparedStatements: false
maxPoolPreparedStatementPerConnectionSize: 20
package com.renrengou.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import javax.sql.DataSource;
import java.sql.SQLException;
/**
* @Author: 彭伟
* @Date: 2018/9/20 9:49
* @Description: 主数据源配置类
*/
@Configuration
// 前缀为primary.datasource.druid的配置信息
@ConfigurationProperties(prefix = "primary.datasource.druid")
@MapperScan(basePackages = MasterDataSourceConfig.PACKAGE, sqlSessionFactoryRef = "primarySqlSessionFactory")
public class MasterDataSourceConfig {
/**
* dao层的包路径
*/
static final String PACKAGE = "com.renrengou.dao";
/**
* mapper文件的相对路径
*/
private static final String MAPPER_LOCATION = "classpath:mapper/*.xml";
private String filters;
private String url;
private String username;
private String password;
private String driverClassName;
private int initialSize;
private int minIdle;
private int maxActive;
private long maxWait;
private long timeBetweenEvictionRunsMillis;
private long minEvictableIdleTimeMillis;
private String validationQuery;
private boolean testWhileIdle;
private boolean testOnBorrow;
private boolean testOnReturn;
private boolean poolPreparedStatements;
private int maxPoolPreparedStatementPerConnectionSize;
// 主数据源使用@Primary注解进行标识
@Primary
@Bean(name = "primaryDataSource")
public DataSource primaryDataSource() throws SQLException {
DruidDataSource druid = new DruidDataSource();
// 监控统计拦截的filters
druid.setFilters(filters);
// 配置基本属性
druid.setDriverClassName(driverClassName);
druid.setUsername(username);
druid.setPassword(password);
druid.setUrl(url);
//初始化时建立物理连接的个数
druid.setInitialSize(initialSize);
//最大连接池数量
druid.setMaxActive(maxActive);
//最小连接池数量
druid.setMinIdle(minIdle);
//获取连接时最大等待时间,单位毫秒。
druid.setMaxWait(maxWait);
//间隔多久进行一次检测,检测需要关闭的空闲连接
druid.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
//一个连接在池中最小生存的时间
druid.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
//用来检测连接是否有效的sql
druid.setValidationQuery(validationQuery);
//建议配置为true,不影响性能,并且保证安全性。
druid.setTestWhileIdle(testWhileIdle);
//申请连接时执行validationQuery检测连接是否有效
druid.setTestOnBorrow(testOnBorrow);
druid.setTestOnReturn(testOnReturn);
//是否缓存preparedStatement,也就是PSCache,oracle设为true,mysql设为false。分库分表较多推荐设置为false
druid.setPoolPreparedStatements(poolPreparedStatements);
// 打开PSCache时,指定每个连接上PSCache的大小
druid.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);
return druid;
}
// 创建该数据源的事务管理
@Primary
@Bean(name = "primaryTransactionManager")
public DataSourceTransactionManager primaryTransactionManager() throws SQLException {
return new DataSourceTransactionManager(primaryDataSource());
}
// 创建Mybatis的连接会话工厂实例
@Primary
@Bean(name = "primarySqlSessionFactory")
public SqlSessionFactory primarySqlSessionFactory(@Qualifier("primaryDataSource") DataSource primaryDataSource) throws Exception {
final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(primaryDataSource); // 设置数据源bean
sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
.getResources(MasterDataSourceConfig.MAPPER_LOCATION)); // 设置mapper文件路径
return sessionFactory.getObject();
}
//----------------------------------get AND set------------
public String getFilters() {
return filters;
}
public void setFilters(String filters) {
this.filters = filters;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getDriverClassName() {
return driverClassName;
}
public void setDriverClassName(String driverClassName) {
this.driverClassName = driverClassName;
}
public int getInitialSize() {
return initialSize;
}
public void setInitialSize(int initialSize) {
this.initialSize = initialSize;
}
public int getMinIdle() {
return minIdle;
}
public void setMinIdle(int minIdle) {
this.minIdle = minIdle;
}
public int getMaxActive() {
return maxActive;
}
public void setMaxActive(int maxActive) {
this.maxActive = maxActive;
}
public long getMaxWait() {
return maxWait;
}
public void setMaxWait(long maxWait) {
this.maxWait = maxWait;
}
public long getTimeBetweenEvictionRunsMillis() {
return timeBetweenEvictionRunsMillis;
}
public void setTimeBetweenEvictionRunsMillis(long timeBetweenEvictionRunsMillis) {
this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
}
public long getMinEvictableIdleTimeMillis() {
return minEvictableIdleTimeMillis;
}
public void setMinEvictableIdleTimeMillis(long minEvictableIdleTimeMillis) {
this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
}
public String getValidationQuery() {
return validationQuery;
}
public void setValidationQuery(String validationQuery) {
this.validationQuery = validationQuery;
}
public boolean isTestWhileIdle() {
return testWhileIdle;
}
public void setTestWhileIdle(boolean testWhileIdle) {
this.testWhileIdle = testWhileIdle;
}
public boolean isTestOnBorrow() {
return testOnBorrow;
}
public void setTestOnBorrow(boolean testOnBorrow) {
this.testOnBorrow = testOnBorrow;
}
public boolean isTestOnReturn() {
return testOnReturn;
}
public void setTestOnReturn(boolean testOnReturn) {
this.testOnReturn = testOnReturn;
}
public boolean isPoolPreparedStatements() {
return poolPreparedStatements;
}
public void setPoolPreparedStatements(boolean poolPreparedStatements) {
this.poolPreparedStatements = poolPreparedStatements;
}
public int getMaxPoolPreparedStatementPerConnectionSize() {
return maxPoolPreparedStatementPerConnectionSize;
}
public void setMaxPoolPreparedStatementPerConnectionSize(int maxPoolPreparedStatementPerConnectionSize) {
this.maxPoolPreparedStatementPerConnectionSize = maxPoolPreparedStatementPerConnectionSize;
}
}
package com.renrengou.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import javax.sql.DataSource;
import java.sql.SQLException;
/**
* @author 彭伟
* @Date 2018/9/19 16:51
* 配置从数据源
*/
@Configuration
@ConfigurationProperties(prefix = "back.datasource.druid")
@MapperScan(basePackages = ClusterDataSourceConfig.PACKAGE, sqlSessionFactoryRef = "backSqlSessionFactory")
public class ClusterDataSourceConfig {
/**
* dao层的包路径
*/
static final String PACKAGE = "com.renrengou.mange.dao";
/**
* mapper文件的相对路径
*/
private static final String MAPPER_LOCATION = "classpath:mangemapper/*.xml";
private String filters;
private String url;
private String username;
private String password;
private String driverClassName;
private int initialSize;
private int minIdle;
private int maxActive;
private long maxWait;
private long timeBetweenEvictionRunsMillis;
private long minEvictableIdleTimeMillis;
private String validationQuery;
private boolean testWhileIdle;
private boolean testOnBorrow;
private boolean testOnReturn;
private boolean poolPreparedStatements;
private int maxPoolPreparedStatementPerConnectionSize;
@Bean(name = "backDataSource")
public DataSource backDataSource() throws SQLException {
DruidDataSource druid = new DruidDataSource();
// 监控统计拦截的filters
druid.setFilters(filters);
// 配置基本属性
druid.setDriverClassName(driverClassName);
druid.setUsername(username);
druid.setPassword(password);
druid.setUrl(url);
//初始化时建立物理连接的个数
druid.setInitialSize(initialSize);
//最大连接池数量
druid.setMaxActive(maxActive);
//最小连接池数量
druid.setMinIdle(minIdle);
//获取连接时最大等待时间,单位毫秒。
druid.setMaxWait(maxWait);
//间隔多久进行一次检测,检测需要关闭的空闲连接
druid.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
//一个连接在池中最小生存的时间
druid.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
//用来检测连接是否有效的sql
druid.setValidationQuery(validationQuery);
//建议配置为true,不影响性能,并且保证安全性。
druid.setTestWhileIdle(testWhileIdle);
//申请连接时执行validationQuery检测连接是否有效
druid.setTestOnBorrow(testOnBorrow);
druid.setTestOnReturn(testOnReturn);
//是否缓存preparedStatement,也就是PSCache,oracle设为true,mysql设为false。分库分表较多推荐设置为false
druid.setPoolPreparedStatements(poolPreparedStatements);
// 打开PSCache时,指定每个连接上PSCache的大小
druid.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);
return druid;
}
@Bean(name = "backTransactionManager")
public DataSourceTransactionManager backTransactionManager() throws SQLException {
return new DataSourceTransactionManager(backDataSource());
}
@Bean(name = "backSqlSessionFactory")
public SqlSessionFactory backSqlSessionFactory(@Qualifier("backDataSource") DataSource backDataSource) throws Exception {
final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(backDataSource);
sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
.getResources(ClusterDataSourceConfig.MAPPER_LOCATION));
return sessionFactory.getObject();
}
//---------------GET AND SET------------
public String getFilters() {
return filters;
}
public void setFilters(String filters) {
this.filters = filters;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getDriverClassName() {
return driverClassName;
}
public void setDriverClassName(String driverClassName) {
this.driverClassName = driverClassName;
}
public int getInitialSize() {
return initialSize;
}
public void setInitialSize(int initialSize) {
this.initialSize = initialSize;
}
public int getMinIdle() {
return minIdle;
}
public void setMinIdle(int minIdle) {
this.minIdle = minIdle;
}
public int getMaxActive() {
return maxActive;
}
public void setMaxActive(int maxActive) {
this.maxActive = maxActive;
}
public long getMaxWait() {
return maxWait;
}
public void setMaxWait(long maxWait) {
this.maxWait = maxWait;
}
public long getTimeBetweenEvictionRunsMillis() {
return timeBetweenEvictionRunsMillis;
}
public void setTimeBetweenEvictionRunsMillis(long timeBetweenEvictionRunsMillis) {
this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
}
public long getMinEvictableIdleTimeMillis() {
return minEvictableIdleTimeMillis;
}
public void setMinEvictableIdleTimeMillis(long minEvictableIdleTimeMillis) {
this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
}
public String getValidationQuery() {
return validationQuery;
}
public void setValidationQuery(String validationQuery) {
this.validationQuery = validationQuery;
}
public boolean isTestWhileIdle() {
return testWhileIdle;
}
public void setTestWhileIdle(boolean testWhileIdle) {
this.testWhileIdle = testWhileIdle;
}
public boolean isTestOnBorrow() {
return testOnBorrow;
}
public void setTestOnBorrow(boolean testOnBorrow) {
this.testOnBorrow = testOnBorrow;
}
public boolean isTestOnReturn() {
return testOnReturn;
}
public void setTestOnReturn(boolean testOnReturn) {
this.testOnReturn = testOnReturn;
}
public boolean isPoolPreparedStatements() {
return poolPreparedStatements;
}
public void setPoolPreparedStatements(boolean poolPreparedStatements) {
this.poolPreparedStatements = poolPreparedStatements;
}
public int getMaxPoolPreparedStatementPerConnectionSize() {
return maxPoolPreparedStatementPerConnectionSize;
}
public void setMaxPoolPreparedStatementPerConnectionSize(int maxPoolPreparedStatementPerConnectionSize) {
this.maxPoolPreparedStatementPerConnectionSize = maxPoolPreparedStatementPerConnectionSize;
}
}
其中这两个注解说明下:
@Primary :标志这个 Bean 如果在多个同类 Bean 候选时,该 Bean
优先被考虑。多数据源配置的时候注意,必须要有一个主数据源,用 @Primary 标志该 Bean。
@MapperScan: 扫描 Mapper 接口并容器管理。
需要注意的是sqlSessionFactoryRef 表示定义一个唯一 SqlSessionFactory 实例。
PageHelper是Mybatis的一个分页插件,非常的好用!这里强烈推荐!!!
添加依赖之后,只需要添加如下配置或代码就可以了。
第一种,在application.properties或application.yml添加
pagehelper:
helperDialect: mysql
offsetAsPageNum: true
rowBoundsWithCount: true
reasonable: false
@Bean
public PageHelper pageHelper(){
PageHelper pageHelper = new PageHelper();
Properties properties = new Properties();
//数据库
properties.setProperty("helperDialect", "mysql");
//是否将参数offset作为PageNum使用
properties.setProperty("offsetAsPageNum", "true");
//是否进行count查询
properties.setProperty("rowBoundsWithCount", "true");
//是否分页合理化
properties.setProperty("reasonable", "false");
pageHelper.setProperties(properties);
}
package com.renrengou.service.impl;
import com.renrengou.dao.TUserMapper;
import com.renrengou.mange.dao.UserMapper;
import com.renrengou.mange.modle.User;
import com.renrengou.modle.TUser;
import com.renrengou.service.TestService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* @author 彭伟
* @Date 2018/9/19 17:05
*/
@Service
public class TestServiceImpl implements TestService {
@Autowired
private TUserMapper tUserMapper;//主数据源
@Autowired
private UserMapper userMapper;//从数据源
@Transactional(value = "primaryTransactionManager", readOnly = true, rollbackFor = {RuntimeException.class, Exception.class})
@Override
public TUser getUser(Long id) {
TUser user = tUserMapper.selectByPrimaryKey(id);
return user;
}
@Transactional(value = "backTransactionManager", readOnly = true, rollbackFor = {RuntimeException.class, Exception.class})
@Override
public User getUserMange(Long id) {
User user = userMapper.selectByPrimaryKey(id);
return user;
}
@Transactional(value = "primaryTransactionManager", readOnly = false, rollbackFor = {RuntimeException.class, Exception.class})
@Override
public void addUser(TUser user) {
tUserMapper.insert(user);
int a = 1 / 0;
}
@Transactional(value = "backTransactionManager", readOnly = false, rollbackFor = {RuntimeException.class, Exception.class})
@Override
public void addUserMange(User user) {
userMapper.insert(user);
int a = 1 / 0;
}
}
package com.renrengou.controller;
import com.renrengou.mange.modle.User;
import com.renrengou.modle.TUser;
import com.renrengou.service.TestService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
/**
* @author 彭伟
* @Date 2018/9/19 17:10
*/
@RestController
public class TestController {
@Autowired
private TestService testService;
@GetMapping(value = "/tuser/{id}")
public TUser getUser(@PathVariable Long id) {
TUser user = testService.getUser(id);
return user;
}
@GetMapping(value = "/user/{id}")
public User getUserMange(@PathVariable Long id) {
User user = testService.getUserMange(id);
return user;
}
@GetMapping(value = "/addtuser")
public String addTuser() {
TUser user = new TUser();
user.setUserName("zhangsna");
testService.addUser(user);
return "成功";
}
@GetMapping(value = "/adduser")
public String adduser() {
User user = new User();
user.setUsername("zhangsna");
testService.addUserMange(user);
return "成功";
}
}
经过测试发现事务和查询都是有用的,事务在发生异常能够回滚
这里结果截图就不多上了,小编都测试过所有的接口都正常