前言
本篇是我的spring cloud脚手架项目的第四篇。上篇讲的是feign接口。我们的spring boot项目已经可以做一个最基本的接口返回和微服务提供了。本篇讲的是所有项目常用的数据库相关配置的接入
参考博客:
https://blog.csdn.net/u012702547/article/details/103029910
https://blog.csdn.net/m0_37809146/article/details/86673372
https://blog.csdn.net/ypp91zr/article/details/83988919
代码
maven
<!--mybatis starter-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<!--引入mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>6.0.6</version>
</dependency>
<!--druid 依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<!--atomikos依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jta-atomikos</artifactId>
<version>2.0.3.RELEASE</version>
</dependency>
<!-- pagehelper分页插件依赖 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.5</version>
</dependency>
DataSourceConfig
因为引入JIT分布式事务了。所以在多数据源上使用XA两段式提交了。所以drudi的DataSource也要切换为对应的DruidXADataSource
import com.alibaba.druid.pool.xa.DruidXADataSource;
import com.atomikos.jdbc.AtomikosDataSourceBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties("spring.datasource.druid.db1")
public DataSource druidDataSourceOne() {
return new DruidXADataSource();
}
@Bean
@ConfigurationProperties("spring.datasource.druid.db2")
public DataSource druidDataSourceTwo() {
return new DruidXADataSource();
}
@Bean
public DataSource dataSourceOne(DataSource druidDataSourceOne) {
AtomikosDataSourceBean sourceBean = new AtomikosDataSourceBean();
sourceBean.setXaDataSource((DruidXADataSource) druidDataSourceOne);
// 必须为数据源指定唯一标识
sourceBean.setUniqueResourceName("db1");
return sourceBean;
}
@Bean
public DataSource dataSourceTwo(DataSource druidDataSourceTwo) {
AtomikosDataSourceBean sourceBean = new AtomikosDataSourceBean();
sourceBean.setXaDataSource((DruidXADataSource) druidDataSourceTwo);
sourceBean.setUniqueResourceName("db2");
return sourceBean;
}
}
@MapperScan(basePackages="")中就是对应数据源应mapper文件应该存放的位置
MyBatisConfigOne
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.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import javax.annotation.Resource;
import javax.sql.DataSource;
@Configuration
@MapperScan(basePackages = "com.chen.dao.mapper1",sqlSessionFactoryRef = "sqlSessionFactory1",sqlSessionTemplateRef = "sqlSessionTemplate1")
public class MyBatisConfigOne {
@Resource(name = "dataSourceOne")
DataSource dsOne;
@Bean
SqlSessionFactory sqlSessionFactory1() {
SqlSessionFactory sessionFactory = null;
try {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dsOne);
sessionFactory = bean.getObject();
} catch (Exception e) {
e.printStackTrace();
}
return sessionFactory;
}
@Bean
SqlSessionTemplate sqlSessionTemplate1() {
return new SqlSessionTemplate(sqlSessionFactory1());
}
//去除单个事务管理
// @Bean
// public DataSourceTransactionManager transactionManager1() {
// return new DataSourceTransactionManager(dsOne);
// }
}
MyBatisConfigTwo
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.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import javax.annotation.Resource;
import javax.sql.DataSource;
@Configuration
@MapperScan(basePackages = "com.chen.dao.mapper2",sqlSessionFactoryRef = "sqlSessionFactory2",sqlSessionTemplateRef = "sqlSessionTemplate2")
public class MyBatisConfigTwo {
@Resource(name = "dataSourceTwo")
DataSource dsTwo;
@Bean
SqlSessionFactory sqlSessionFactory2() {
SqlSessionFactory sessionFactory = null;
try {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dsTwo);
sessionFactory = bean.getObject();
} catch (Exception e) {
e.printStackTrace();
}
return sessionFactory;
}
@Bean
SqlSessionTemplate sqlSessionTemplate2() {
return new SqlSessionTemplate(sqlSessionFactory2());
}
//去除单个事务管理
// @Bean
// public DataSourceTransactionManager transactionManager2() {
// return new DataSourceTransactionManager(dsTwo);
// }
}
dao模块添加maven特殊配置,让mapper.xml文件也会加入到jar包中去
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
yml中db相关配置
spring:
application:
name: SERVICE-A
#数据库配置
datasource:
druid:
db1:
url: jdbc:mysql://127.0.0.1:3306/chen_data?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
# 初始化时建立物理连接的个数。初始化发生在显示调用 init 方法,或者第一次 getConnection 时
initialSize: 5
# 最小连接池数量
minIdle: 5
# 最大连接池数量
maxActive: 10
# 获取连接时最大等待时间,单位毫秒。配置了 maxWait 之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置 useUnfairLock 属性为 true 使用非公平锁。
maxWait: 60000
# Destroy 线程会检测连接的间隔时间,如果连接空闲时间大于等于 minEvictableIdleTimeMillis 则关闭物理连接。
timeBetweenEvictionRunsMillis: 60000
# 连接保持空闲而不被驱逐的最小时间
minEvictableIdleTimeMillis: 300000
# 用来检测连接是否有效的 sql 因数据库方言而异, 例如 oracle 应该写成 SELECT 1 FROM DUAL
validationQuery: SELECT 1
# 建议配置为 true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于 timeBetweenEvictionRunsMillis,执行 validationQuery 检测连接是否有效。
testWhileIdle: true
# 申请连接时执行 validationQuery 检测连接是否有效,做了这个配置会降低性能。
testOnBorrow: false
# 归还连接时执行 validationQuery 检测连接是否有效,做了这个配置会降低性能。
testOnReturn: false
# 是否自动回收超时连接
removeAbandoned: true
# 超时时间 (以秒数为单位)
remove-abandoned-timeout: 1800
db2:
url: jdbc:mysql://127.0.0.1:3306/chen_data2?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
# 初始化时建立物理连接的个数。初始化发生在显示调用 init 方法,或者第一次 getConnection 时
initialSize: 6
# 最小连接池数量
minIdle: 6
# 最大连接池数量
maxActive: 10
# 获取连接时最大等待时间,单位毫秒。配置了 maxWait 之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置 useUnfairLock 属性为 true 使用非公平锁。
maxWait: 60000
# Destroy 线程会检测连接的间隔时间,如果连接空闲时间大于等于 minEvictableIdleTimeMillis 则关闭物理连接。
timeBetweenEvictionRunsMillis: 60000
# 连接保持空闲而不被驱逐的最小时间
minEvictableIdleTimeMillis: 300000
# 用来检测连接是否有效的 sql 因数据库方言而异, 例如 oracle 应该写成 SELECT 1 FROM DUAL
validationQuery: SELECT 1
# 建议配置为 true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于 timeBetweenEvictionRunsMillis,执行 validationQuery 检测连接是否有效。
testWhileIdle: true
# 申请连接时执行 validationQuery 检测连接是否有效,做了这个配置会降低性能。
testOnBorrow: false
# 归还连接时执行 validationQuery 检测连接是否有效,做了这个配置会降低性能。
testOnReturn: false
# 是否自动回收超时连接
removeAbandoned: true
# 超时时间 (以秒数为单位)
remove-abandoned-timeout: 1800
# WebStatFilter 用于采集 web-jdbc 关联监控的数据。
web-stat-filter:
# 是否开启 WebStatFilter 默认是 true
enabled: true
# 需要拦截的 url
url-pattern: /*
# 排除静态资源的请求
exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"
# Druid 内置提供了一个 StatViewServlet 用于展示 Druid 的统计信息。
stat-view-servlet:
#是否启用 StatViewServlet 默认值 true
enabled: true
# 需要拦截的 url
url-pattern: /druid/*
# 允许清空统计数据
reset-enable: true
login-username: druid
login-password: druid
网上还有一些是关于AOP切面自动切换数据源的。我个人认为没有啥必要,在MyBatisConfig中配置多个数据源,然后分别放到不同的mapper中做区分就可以了。这样也比较简单易懂。
然后就是写一个controller,一个manager,里面做一个查询,开启pagehelper,写一个多数据源插入,抛出问题,查看回滚
manager代码
#pagehelper的测试方法,注意不要在多个mapper中同时分页,会有问题
public String getOneByPage() {
ChenTestExample example = new ChenTestExample();
ChenTestExample.Criteria criteria = example.createCriteria();
#开启分页
PageHelper.startPage(1, 5);
List<ChenTest> list = chenTestMapper.selectByExample(example);
PageInfo<ChenTest> pageInfo = new PageInfo<>(list);
#结束分页,获得分页结果
log.info("list1:{}", JSON.toJSONString(list));
log.info("page1:{}",JSON.toJSONString(pageInfo));
return "byPage1";
}
#添加注解即可开启XA分布式事务,最后我写了一个已知错误可以查看是否回滚数据
@Transactional(propagation = Propagation.REQUIRED)
public String insertOne() {
log.info("manager#insertOne");
ChenTest chenTest = new ChenTest();
chenTest.setGmtCreate(LocalDateTime.now());
chenTest.setGmtModified(LocalDateTime.now());
chenTest.setName("chentest1");
chenTest.setNum(5);
int result = chenTestMapper.insertSelective(chenTest);
ChenTest2 chenTest2 = new ChenTest2();
chenTest2.setGmtCreate(LocalDateTime.now());
chenTest2.setGmtModified(LocalDateTime.now());
chenTest2.setName("chentest2");
chenTest2.setNum(6);
int result2 = chenTest2Mapper.insertSelective(chenTest2);
#故意写一个异常,查看是否2个数据源都回滚了
int error = 0;
int b = 1/error;
return "1";
}
然后启动项目在127.0.0.1/druid还可以看到druid相关监控信息
调用方法插入多个数据源时报错会回滚多个数据源,pagehelper也正常分页了。这里一下,pagehepler只要在spring boot中添加启动maven即可自动启动,如果有其他配置需要配置的话可以在yml中再写
结尾
这样我们的项目的数据库相关的链接就做好了。本来还想再写一个sharding相关的,种种原因(其实就是mysql不够多)也就放弃了。
github地址:https://github.com/alex9567/SpringCloudScaffold