前言
本篇是我的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