dynamic-datasource-spring-boot-starter
提供了多数据源切换,通过它可以更加方便的集成多数据源。
创建工程
新建springboot
工程,引入相关依赖,maven
配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>top.wteng</groupId>
<artifactId>DynamicDSBootIntegration</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>DynamicDSBootIntegration</name>
<description>Dynamic Datasource Integration</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.4.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
数据源配置
dynamic-datasource-spring-boot-starter
使得多数据源集成十分方便,只需在resource/application.yaml
中配置数据源,这里配置两个mysql
数据源,分别对应ds0
与ds1
两个数据库,ds0
中有表t_user
,ds1
中有表t_order
,如下:
CREATE TABLE `ds0`.`t_user` (
`id` int(0) NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
CREATE TABLE `ds1`.`t_order` (
`id` int(0) NOT NULL AUTO_INCREMENT,
`number` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`user_id` int(0) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
数据源配置如下:
spring:
datasource:
dynamic:
primary: ds0 # 主数据源
strict: false # 是否严格匹配数据源,若为true,未设置数据源的库操作会报错,否则使用主数据源
datasource:
ds0: # 数据源1
url: jdbc:mysql://localhost:3306/ds0?useSSL=false
username: root
password: root
ds1: # 数据源2
url: jdbc:mysql://localhost:3306/ds1?useSSL=false
username: root
password: root
jpa: # jpa配置
generate-ddl: true
show-sql: false
hibernate:
ddl-auto: none
dynamic-datasource-spring-boot-starter
同时还可以配置主从库,以组划分,同一组内数据源进行负载均衡,所有以下划线 _
分割的数据源 首部即为组的名称,如ds2_1
与ds2_2
属于同一组ds2
,具体可查看说明文档。
dao
层实现
dao
层实现与单数据源下jpa
实现完全一致,只需通过@DS
注解标记每个实现类或方法应使用的数据源即可,以t_user
为例,定义实体如下:
package top.wteng.dds.entity;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.GenerationType;
@Entity(name = "t_user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column()
private String name;
@Column()
private String password;
public User() {
}
public User(Integer id, String name, String password) {
this.id = id;
this.name = name;
this.password = password;
}
public Integer getId() {
return this.id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return this.password;
}
public void setPassword(String password) {
this.password = password;
}
public User id(Integer id) {
setId(id);
return this;
}
public User name(String name) {
setName(name);
return this;
}
public User password(String password) {
setPassword(password);
return this;
}
@Override
public String toString() {
return "{" +
" id='" + getId() + "'" +
", name='" + getName() + "'" +
", password='" + getPassword() + "'" +
"}";
}
}
repository
定义如下:
package top.wteng.dds.repository;
import org.springframework.stereotype.Repository;
import org.springframework.data.jpa.repository.JpaRepository;
import top.wteng.dds.entity.User;
@Repository
public interface UserRep extends JpaRepository<User, Integer> {
}
为减少重复工作,定义基本增删改查的基类接口与实现,定义公共基类接口如下:
package top.wteng.dds.service;
import java.util.List;
public interface BaseService<T, ID> {
T findById(ID id);
List<T> findAll();
T save(T t);
List<T> saveAll(Iterable<T> entities);
void deleteById(ID id);
void delete(T t);
void deleteAll();
void deleteAll(Iterable<T> entities);
}
基类接口实现如下:
package top.wteng.dds.service.impl;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import top.wteng.dds.service.BaseService;
public class BaseServiceImpl<T, ID> implements BaseService<T, ID> {
protected final JpaRepository<T, ID> repository;
BaseServiceImpl(JpaRepository<T, ID> repository) {
this.repository = repository;
}
@Override
public T findById(ID id) {
return this.repository.findById(id).orElse(null);
}
@Override
public List<T> findAll() {
return this.repository.findAll();
}
@Override
public T save(T t) {
return this.repository.save(t);
}
@Override
public List<T> saveAll(Iterable<T> entities) {
return this.repository.saveAll(entities);
}
@Override
public void deleteById(ID id) {
this.repository.deleteById(id);
}
@Override
public void delete(T t) {
this.repository.delete(t);
}
@Override
public void deleteAll() {
this.repository.deleteAll();
}
@Override
public void deleteAll(Iterable<T> entities) {
this.repository.deleteAll(entities);
}
}
然后定义User
对应的服务类接口:
package top.wteng.dds.service;
import top.wteng.dds.entity.User;
public interface UserService extends BaseService<User, Integer> {
}
User
类接口实现,就是在这里通过@DS
注解标记使用的数据源,也可以标记具体方法,方法上的标记优先于类标记,当配置了主从多库时,数据源名称可以写具体名称或只写组名称,若写组名称,则在组内进行负载均衡:
package top.wteng.dds.service.impl;
import com.baomidou.dynamic.datasource.annotation.DS;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import top.wteng.dds.entity.User;
import top.wteng.dds.repository.UserRep;
import top.wteng.dds.service.UserService;
@Service
@DS("ds0") // 此类的操作使用ds0数据源
public class UserServiceImpl extends BaseServiceImpl<User, Integer> implements UserService {
UserServiceImpl(@Autowired UserRep userRep) {
super(userRep);
}
}
t_order
的dao
层实现与t_user
一致,只是在服务实现类OrderServiceImpl
上将数据源标记为ds1
即可。
测试
编写测试如下:
package top.wteng.dds;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import top.wteng.dds.entity.Order;
import top.wteng.dds.entity.User;
import top.wteng.dds.service.OrderService;
import top.wteng.dds.service.UserService;
@SpringBootTest
public class DynamicModuleTest {
@Autowired private UserService userService;
@Autowired private OrderService orderService;
@Test
public void doSomething() {
User user = new User();
user.setName("user_" + String.valueOf(System.currentTimeMillis()));
user.setPassword("password");
this.userService.save(user);
System.out.println("user saved ...");
Order order = new Order();
order.setNumber("O" + String.valueOf(System.currentTimeMillis()));
order.setUserId(user.getId());
this.orderService.save(order);
System.out.println("order saved ..");
List<User> users = this.userService.findAll();
System.out.println("========= all users =======");
users.forEach(u -> System.out.println(u.toString()));
List<Order> orders = this.orderService.findAll();
System.out.println("========= all orders =======");
orders.forEach(o -> System.out.println(o.toString()));
}
}
运行测试,即可看到输入,查看数据库,数据被存到了不同的库中: