Springboot动态数据源使用

/ 技术 / 无站内评论 / 843浏览
SSM动态切换数据源看这篇文章:https://sanii.cn/article/191

我们用到的是一个开源的框架:dynamic-datasource-spring-boot-starter 

码云

https://gitee.com/gouchao/dynamic-datasource-spring-boot-starter

GitHub

https://github.com/baomidou/dynamic-datasource-spring-boot-starter

简介

dynamic-datasource-spring-boot-starter 基于 springBoot2.0.

它适用于读写分离,一主多从的环境。

主数据库使用 INSERT UPDATE DELETE 操作.

从数据库使用 SELECT 操作.

如果你的项目比较复杂,建议使用 sharding-jdbc .

依赖

<dependency>
  <groupId>com.baomidou</groupId>
  <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
  <version>1.1.0</version>
</dependency>

配置主从数据源

spring.datasource.dynamic.master 配置唯一主数据源(写库)

spring.datasource.dynamic.slave 配置每一个从数据源(读库)

spring:
datasource:
dynamic:
master:
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf8&useSSL=false
slave:
one:
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/test1?characterEncoding=utf8&useSSL=false
two:
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/test2?characterEncoding=utf8&useSSL=false

使用

使用 @DS 注解切换数据源。

可以注解在方法上,可以注解在service实现或mapper接口方法上。

注解结果
没有@DS主库
@DS("slave")存在slave指定slave,不存在主库
@DS根据DynamicDataSourceStrategy策略,选择一个从库。默认负载均衡策略。

官方实例

@Service
public class UserServiceImpl implements UserService {

@Autowired private JdbcTemplate jdbcTemplate;

@DS("one") public List<Map<String, Object>> selectAll() { return jdbcTemplate.queryForList("select * from user"); }

@Override @DS public List<Map<String, Object>> selectByCondition() { return jdbcTemplate.queryForList("select * from user where age >10"); }

}

在mybatis环境下也可注解在mapper接口层。

public interface UserMapper {

@Insert("INSERT INTO user (name,age) values (#{name},#{age})") boolean addUser(@Param("name") String name, @Param("age") Integer age);

@Update("UPDATE user set name=#{name}, age=#{age} where id =#{id}") boolean updateUser(@Param("id") Integer id, @Param("name") String name, @Param("age") Integer age);

@Delete("DELETE from user where id =#{id}") boolean deleteUser(@Param("id") Integer id);

@Select("SELECT * FROM user") @DS List<User> selectAll();

}

测试

我们现在本地创建三个数据库进行测试


注意:如果使用JPA,除了主库以外的数据库表必须存在(手动创建),因为在启动的时候JPA只会帮你创建主库的表。

测试实体

/
* @Auther: shouliang.wang
* @Date: 2018/7/20 16:11
* @Description: 动态数据源测试实体
*/
@Data
@Entity
public class DynamicBean {

@Id
private int id;
private int age;
private String name;

}

DAO

继承JPA,常用CRUD已经帮我们集成

/
* @Auther: shouliang.wang
* @Date: 2018/7/20 16:12
* @Description: 动态数据源测试实体DAO
/
@Repository
public interface DynamicDao extends JpaRepository<DynamicBean,Integer>{
}

Service

/**
* @Auther: shouliang.wang
* @Date: 2018/7/20 16:19
* @Description: 测试动态数据源Service
/
@Service
public class DynamicService {

//注入DAO
@Autowired
public DynamicDao dynamicDao;

//默认主库
public DynamicBean addUser(DynamicBean bean){
return dynamicDao.save(bean);
}

/
* 添加一条数据
* 指定从库 one
*/
@DS("one")
public DynamicBean addUser2(DynamicBean bean){
return dynamicDao.save(bean);
}
/

* 添加一条数据
* 指定从库 two
/
@DS("two")
public DynamicBean addUser3(DynamicBean bean){
return dynamicDao.save(bean);
}

/**
* 默认主库
根据ID查询
/
public Optional<DynamicBean> getUser(int id){
return dynamicDao.findById(id);
}
/**
* 指定从库one
* 根据ID查询
/
@DS("one")
public Optional<DynamicBean> getUser2(int id){
return dynamicDao.findById(id);
}
/
* 指定从库two
* 根据ID查询
*/
@DS("two")
public Optional<DynamicBean> getUser3(int id){
return dynamicDao.findById(id);
}

/

* 采用负载均衡策略读取从库
* 根据ID查询
*/
@DS
public Optional<DynamicBean> getUser4(int id){
return dynamicDao.findById(id);
}
}

测试

我们分别往三个库插入不同的数据

主库:id=1&age=18&name=san

one:id=2&age=19&name=san

two:id=3&age=20&name=san

请求结果成功,看下数据库(这里只是截图two)

成功分别向三个不同库插入数据成功

再来看看查询数据

测试结果

就不一一截图了,测试N边了。。都是成功的。

我们重点测试下方法4,根据负载均衡策略查询从库。(one 和 two库)

...测试发现从库每次查询的从库都不同,证明了采用负载均衡策略。

(返回null是因为2这个ID只在one的从库中,two库只有3)。

集成druid

springBoot2.x默认使用HikariCP,但在国内Druid的使用者非常庞大,此项目特地对其进行了适配,完成多数据源下使用Druid进行监控。

  1. 项目引入druid-spring-boot-starter依赖。
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.9</version>
</dependency>
  1. 排除原生Druid的快速配置类。
@SpringBootApplication(exclude = DruidDataSourceAutoConfigure.class)
public class Application {

public static void main(String[] args) { SpringApplication.run(Application.class, args); }

}

为什么要排除?

DruidDataSourceAutoConfigure在DynamciDataSourceAutoConfiguration之前,其会注入一个DataSourceWrapper,会在原生的spring.datasource下找url,username,password等。而我们动态数据源的配置路径是变化的。

  1. 其他属性依旧如原生druid-spring-boot-starter的配置。(以下demo是在官网例子中copy出来的。)https://gitee.com/baomidou/dynamic-datasource-example
spring:

datasource:
druid:
stat-view-servlet:
loginUsername: admin
loginPassword: 123456
dynamic:
master:
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://47.100.20.186:3306/dynamic?characterEncoding=utf8&useSSL=false
druid:
initial-size: 3
max-active: 8
min-idle: 2
max-wait: -1
min-evictable-idle-time-millis: 30000
max-evictable-idle-time-millis: 30000
time-between-eviction-runs-millis: 0
validation-query: select 1
validation-query-timeout: -1
test-on-borrow: false
test-on-return: false
test-while-idle: true
pool-prepared-statements: true
max-open-prepared-statements: 100
filters: stat,wall
share-prepared-statements: true
slave:
one:
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://47.100.20.186:3308/dynamic?characterEncoding=utf8&useSSL=false
two:
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://47.100.20.186:3308/dynamic?characterEncoding=utf8&useSSL=false
druid:
initial-size: 2
max-active: 6
three:
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://47.100.20.186:3309/dynamic?characterEncoding=utf8&useSSL=false
druid:
initial-size: 3
max-active: 10
initialization-mode: always

如上即可配置访问用户和密码。

召唤蕾姆
琼ICP备18000156号

鄂公网安备 42011502000211号