springboot配置多数据源-Dynamic
导包
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.1.0</version>
</dependency>
配置
spring:
datasource:
dynamic:
druid: #以下是全局默认值,可以全局更改
#监控统计拦截的filters
filters: stat
#配置初始化大小/最小/最大
initial-size: 1
min-idle: 1
max-active: 20
#获取连接等待超时时间
max-wait: 60000
#间隔多久进行一次检测,检测需要关闭的空闲连接
time-between-eviction-runs-millis: 60000
#一个连接在池中最小生存的时间
min-evictable-idle-time-millis: 300000
validation-query: SELECT 'x'
test-while-idle: true
test-on-borrow: false
test-on-return: false
#打开PSCache,并指定每个连接上PSCache的大小。oracle设为true,mysql设为false。分库分表较多推荐设置为false
pool-prepared-statements: false
max-pool-prepared-statement-per-connection-size: 20
stat:
merge-sql: true
log-slow-sql: true
slow-sql-millis: 3000
# 配置默认数据源
primary: master
# true:找不到数据源报错,false:找不到数据源则使用数据源
strict: false
datasource:
# 数据源1,名为 master
master:
username: root
password: 123456
url: jdbc:mysql://localhost:3306/resource?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&autoReconnect=true&failOverReadOnly=false&useSSL=false
driver-class-name: com.mysql.cj.jdbc.Driver
# 数据源2,名为 readbase
readbase:
username: root
password: 123456
url: jdbc:mysql://localhost:3306/user?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&autoReconnect=true&failOverReadOnly=false&useSSL=false
driver-class-name: com.mysql.cj.jdbc.Driver
# 数据源3,名为 writebase
writebase:
username: root
password: 123456
url: jdbc:mysql://localhost:3306/user_info?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&autoReconnect=true&failOverReadOnly=false&useSSL=false
driver-class-name: com.mysql.cj.jdbc.Driver
数据源名称最好不要包含下滑线,下滑线的数据源切换不了
primary: master
配置后,全局默认master
主数据源,只要没有@DS
注解显式申明切换数据源,默认就是master主数据源。
使用
@DS
可以注解在方法上、类上、接口、枚举,同时存在方法注解优先于类上注解。
@DS("readbase")
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
建议在
service
类中使用,不建议在mapper
上面使用解决事务内不能切换数据源:可以在被调用方方法或者类上创建一个新事务
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
外部事务回滚,内部不会回滚,内部回滚,外部事务会回滚。如果调用方与被调用方在同一个
Service
类中,propagation = Propagation.REQUIRES_NEW
会被忽略掉,调用时不会再创建新事务。
注意的问题
一:涉及需要切换数据源时
1.不能使用事务,否则数据源不会切换,使用的还是是第一次加载的数据源 。
删除 操作多数据源的方法或者类、接口 上的 注解 @Transactional() 即可。
2.第一次加载的数据源之后,第二次(第三次...)操作其它数据源,如果数据源不存在,使用的还是第一
次加载的数据源
3.数据源名称最好不要包含下滑线,下滑线的数据源切换不了
二:其他
1.接口中A、B两个方法,A无@Transactional标签,B有,上层通过A间接调用B,此时事务不生效。
2.接口中异常(运行时异常)被捕获而没有被抛出。
默认配置下,spring 只有在抛出的异常为运行时 unchecked 异常时才回滚该事务,
也就是抛出的异常为RuntimeException 的子类(Errors也会导致事务回滚),
而抛出 checked 异常则不会导致事务回滚 。可通过 @Transactional rollbackFor进行配置。
3.多线程下事务管理因为线程不属于 spring 托管,故线程不能够默认使用 spring 的事务,
也不能获取spring 注入的 bean 。
在被 spring 声明式事务管理的方法内开启多线程,多线程内的方法不被事务控制。
一个使用了@Transactional 的方法,如果方法内包含多线程的使用,方法内部出现异常,
不会回滚线程中调用方法的事务。
类配置多数据源
yml 配置
spring:
datasource:
# 通过bean加载,层级任意
datasource:
readbase:
url: jdbc:postgresql://192.168.11.20:5432/test1?stringtype=unspecified¤tSchema=public
username: postgres
password: xxx@abcd
driver-class-name: org.postgresql.Driver
minimum-idle: 5
maximum-pool-size: 40
auto-commit: true
idle-timeout: 60000
max-lifetime: 1800000
connection-timeout: 60000
connection-test-query: SELECT 1
writebase:
url: jdbc:postgresql://192.168.11.21:5432/test2?stringtype=unspecified¤tSchema=public
username: postgres
password: xxx@abcd
driver-class-name: org.postgresql.Driver
minimum-idle: 5
maximum-pool-size: 40
auto-commit: true
idle-timeout: 60000
max-lifetime: 1800000
connection-timeout: 60000
connection-test-query: SELECT 1
# Mybatis-plus相关配置
mybatis-plus:
# xml扫描,多个目录用逗号或者分号分隔(告诉 Mapper 所对应的 XML 文件位置)
mapper-locations: classpath:/mapper/**/*.xml
configuration:
# 是否开启自动驼峰命名规则映射:从数据库列名到Java属性驼峰命名的类似映射
map-underscore-to-camel-case: true
# 返回map时true:当查询数据为空时字段返回为null,false:不加这个查询数据为空时,字段将被隐藏
call-setters-on-nulls: true
# 这个配置会将执行的sql打印出来,在开发或测试的时候可以用
# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
数据源配置
package com.config;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
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 javax.sql.DataSource;
@Configuration
public class DataSourceConfig {
@Primary
@Bean(name = "db1DataSourceProperties")
@ConfigurationProperties(prefix = "spring.datasource.datasource.readbase")
public DataSourceProperties geocodeDataSourceProperties() {
return new DataSourceProperties();
}
@Primary
@Bean(name = "db1DataSource")
public DataSource geocodeDataSource(@Qualifier("db1DataSourceProperties") DataSourceProperties dataSourceProperties) {
return dataSourceProperties.initializeDataSourceBuilder().build();
}
@Bean(name = "db2DataSourceProperties")
@ConfigurationProperties(prefix = "spring.datasource.datasource.writebase")
public DataSourceProperties geocode1DataSourceProperties() {
return new DataSourceProperties();
}
@Bean(name = "db2DataSource")
public DataSource geocode1DataSource(@Qualifier("db2DataSourceProperties") DataSourceProperties dataSourceProperties) {
return dataSourceProperties.initializeDataSourceBuilder().build();
}
}
db1配置
package com.config;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
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;
@Configuration
@MapperScan(basePackages = "com.xx.xx.mapper.db1", sqlSessionTemplateRef = "db1SqlSessionTemplate")
public class Db1DataSourceConfig {
@Primary
@Bean("ds1SqlSessionFactory")
public MybatisSqlSessionFactoryBean ds1SqlSessionFactory(@Qualifier("db1DataSource") DataSource dataSource) throws Exception {
MybatisConfiguration configuration = new MybatisConfiguration();
MybatisSqlSessionFactoryBean bean = new MybatisSqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setMapperLocations(new PathMatchingResourcePatternResolver().
getResources("classpath*:mapper/db1/*.xml"));
configuration.addInterceptor(new MybatisPlusInterceptor());
// 设置数据库大小写读取不敏感
configuration.setMapUnderscoreToCamelCase(true);
return bean;
}
@Primary
@Bean("db1SqlSessionTemplate")
public SqlSessionTemplate db1SqlSessionTemplate(
@Qualifier("ds1SqlSessionFactory") SqlSessionFactory sessionFactory) {
return new SqlSessionTemplate(sessionFactory);
}
@Primary
@Bean("db1TransactionManager")
public DataSourceTransactionManager ds1TransactionManager(@Qualifier("db1DataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
db2配置
package com.config;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
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 org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
@Configuration
@MapperScan(basePackages = "com.xx.xx.mapper.db2", sqlSessionTemplateRef = "db2SqlSessionTemplate")
public class Db2DataSourceConfig {
@Bean("ds2SqlSessionFactory")
public MybatisSqlSessionFactoryBean ds2SqlSessionFactory(@Qualifier("db2DataSource") DataSource dataSource) throws Exception {
MybatisConfiguration configuration = new MybatisConfiguration();
MybatisSqlSessionFactoryBean bean = new MybatisSqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setMapperLocations(new PathMatchingResourcePatternResolver().
getResources("classpath*:mapper/db2/*.xml"));
configuration.addInterceptor(new MybatisPlusInterceptor());
// 设置数据库大小写读取不敏感
configuration.setMapUnderscoreToCamelCase(true);
return bean;
}
@Bean("db2SqlSessionTemplate")
public SqlSessionTemplate db1SqlSessionTemplate(
@Qualifier("ds2SqlSessionFactory") SqlSessionFactory sessionFactory) {
return new SqlSessionTemplate(sessionFactory);
}
@Bean("db2TransactionManager")
public PlatformTransactionManager db1TransactionManager(@Qualifier("db2DataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
mybatis与mybatis-plus 配置
// mybatis
@Primary
@Bean("db1SqlSessionFactory")
public SqlSessionFactory ds1SqlSessionFactory(@Qualifier("db1DataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean();
sqlSessionFactory.setDataSource(dataSource);
sqlSessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().
getResources("classpath*:mapper/db1/*.xml"));
sqlSessionFactory.setConfigLocation(new PathMatchingResourcePatternResolver().
getResource("classpath:/mapper/mybatis-config.xml"));
return sqlSessionFactory.getObject();
}
// mybatis-plus
@Bean("ds2SqlSessionFactory")
public MybatisSqlSessionFactoryBean ds2SqlSessionFactory(@Qualifier("db2DataSource") DataSource dataSource) throws Exception {
MybatisConfiguration configuration = new MybatisConfiguration();
MybatisSqlSessionFactoryBean bean = new MybatisSqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setMapperLocations(new PathMatchingResourcePatternResolver().
getResources("classpath*:mapper/db2/*.xml"));
bean.setConfigLocation(new PathMatchingResourcePatternResolver().
getResource("classpath:/mapper/mybatis-config.xml"));
configuration.addInterceptor(new MybatisPlusInterceptor());
// 设置数据库大小写读取不敏感
configuration.setMapUnderscoreToCamelCase(true);
return bean;
}
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--show sql in control-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
<setting name="callSettersOnNulls" value="true"/>
</settings>
<!--page info-->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<!-- <property name="param1" value="value1"/>-->
<property name="reasonable" value="true"/>
</plugin>
</plugins>
</configuration>
数据源为db1
package com.xx.xx.mapper.db1;
/**
Mapper 接口
*/
@Mapper
public interface UserMapper extends BaseMapper<User> {
}
启动类
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class DynamicApplication{
public static void main(String[] args) {
SpringApplication.run(DynamicApplication.class, args);
}
}
springboot执行sql脚本文件
创建脚本
在项目的resource 目录下新建一个sql目录, 用于存放建表语句
然后在sql 目录下创建 xxx-schema.sql, xxx-data.sql 脚本. 需要注意区分每个后缀代表的含义:
schema: 代表存放的是DDL(数据库定义语言): 对表结构的增删改在这里
data: 代表存放的是DML(数据库操作语言): 对表中数据的操作在这里
配置
-
.properties
# 需要加上这句,否则不会自动执行sql文件 spring.datasource.initialization-mode=always # schema.sql中一般存放的是建表语句DDL spring.datasource.schema = classpath:/sql/xxx-schema.sql # data.sql中一般存放的是需要插入更新等sql语句DML spring.datasource.data = classpath:/sql/xxx-data.sql # 遇到错误继续执行 spring.datasource.data.continue-on-error: true
-
.yml
spring:
datasource:
initialization-mode: always
schema:
- classpath:/sql/xxx-schema.sql
data:
- classpath:/sql/xxx-data.sql
continue-on-error: true
因为在没有加上这个配置之前, 每次初始化都会执行一遍配置的SQL脚本内的SQL语句.
如果在第一次启动并建表成功后再次重启就会因项目在启动时执行SQL脚本并出现表已存在的错误导致项目启动失败
添加该属性之后, 则会忽略错误, 让项目初始化成功! 这样, 也符合我们想要在项目初始化的时候自动执行SQL脚本的思想
sql脚本
USE `user_info`;
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
CREATE TABLE IF NOT EXISTS `t_user` (
`id` varcher(32) NOT NULL COMMENT '主键',
`create_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0) COMMENT '创建时间',
`update_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0) ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '更新时间',
`del_flag` int(1) NOT NULL DEFAULT 0 COMMENT '删除标志',
`username` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '用户名',
`password` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '密码',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '用户信息表' ROW_FORMAT = Dynamic;
参考链接:
https://blog.csdn.net/LittleBlackyoyoyo/article/details/109379791
https://blog.csdn.net/qq_43371556/article/details/122976456