下面是 Spring Boot 优雅整合多数据源的完整攻略。
1. 背景
Spring Boot 为我们提供了非常便捷的开发方式,但在项目中使用多数据源时,代码会变得比较冗长和难以维护。所以,需要一种更加简洁优美的方式来整合多数据源。
2. 实现方式
Spring Boot 优雅整合多数据源的方式,主要是通过使用 Spring 自带的 AbstractRoutingDataSource
类,来实现动态切换数据源的功能。具体实现步骤如下:
2.1 配置多数据源
在 application.yml
文件中,配置多个数据源,例如:
spring:
datasource:
first:
url: jdbc:mysql://localhost:3306/db_first
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
second:
url: jdbc:mysql://localhost:3306/db_second
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
2.2 自定义数据源路由类
创建一个继承 AbstractRoutingDataSource
的数据源路由类:
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSource();
}
}
这个类的作用是,根据指定的数据源标识,动态选择对应的数据源。其中,determineCurrentLookupKey
方法返回的数据源标识是通过 DataSourceContextHolder
类获取的。
2.3 自定义数据源上下文
创建一个 DataSourceContextHolder
类,用来存储当前使用的数据源标识:
public class DataSourceContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
public static void setDataSource(String dataSource) {
contextHolder.set(dataSource);
}
public static String getDataSource() {
return contextHolder.get();
}
public static void clearDataSource() {
contextHolder.remove();
}
}
其中,ThreadLocal
类用于保证多线程下的数据安全性。
2.4 定义数据源切换注解
创建一个数据源切换注解,例如:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DataSourceSwitch {
String value();
}
这个注解的作用是,指定使用哪个数据源。在使用时将该注解加到需要切换数据源的方法上。
2.5 定义切面
创建一个切面类,用来实现数据源的切换:
@Aspect
@Component
public class DataSourceAspect {
@Around("@annotation(dataSourceSwitch)")
public Object switchDataSource(ProceedingJoinPoint point, DataSourceSwitch dataSourceSwitch) throws Throwable {
String dataSource = dataSourceSwitch.value();
DataSourceContextHolder.setDataSource(dataSource);
try {
return point.proceed();
} finally {
DataSourceContextHolder.clearDataSource();
}
}
}
这个切面类的作用是,在切入的方法执行前,根据注解指定的数据源,将对应的数据源标识设置到 DataSourceContextHolder
中,让 DynamicDataSource
类可以根据该标识选择对应的数据源。执行后,清空数据源标识,保证数据安全。
2.6 使用数据源切换注解
在需要切换数据源的方法上,加上 @DataSourceSwitch
注解,并指定使用哪个数据源,例如:
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
@DataSourceSwitch("first")
public List<User> getUsersFromFirstDataSource() {
return userDao.selectAll();
}
@Override
@DataSourceSwitch("second")
public List<User> getUsersFromSecondDataSource() {
return userDao.selectAll();
}
}
这里的 UserDao
是数据访问层的实现,在具体使用时,此处应该使用实现 UserDao
接口的具体类。
3. 示例
下面使用两个简单的例子来演示,如何使用上述方式整合多数据源。
3.1 示例一
在这个例子中,我们将从两个不同的数据库中选取相同的数据,并进行合并。
3.1.1 创建数据源
我们创建两个数据库:db_user1
和 db_user2
,并分别在其中创建一张名为 t_user
的表,其中字段包括 id
和 username
。
3.1.2 配置数据源
在 application.yml
文件中,配置两个数据源:
spring:
datasource:
first:
url: jdbc:mysql://localhost:3306/db_user1
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
second:
url: jdbc:mysql://localhost:3306/db_user2
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
3.1.3 实现数据访问层
创建一个 UserDao
接口,实现读取全部数据的方法:
public interface UserDao {
List<User> selectAll();
}
创建一个 User
类,用于封装数据:
@Data
public class User {
private Long id;
private String username;
}
实现两个具体的数据访问层类,分别针对不同的数据源:
@Repository("userDao1")
public class UserDao1Impl implements UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public List<User> selectAll() {
return jdbcTemplate.query("select * from t_user", new BeanPropertyRowMapper<>(User.class));
}
}
@Repository("userDao2")
public class UserDao2Impl implements UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public List<User> selectAll() {
return jdbcTemplate.query("select * from t_user", new BeanPropertyRowMapper<>(User.class));
}
}
这里的 JdbcTemplate
是 Spring Boot 提供的一个操作数据库的工具类。
3.1.4 测试数据访问层
创建一个 UserController
控制器类:
@RestController
public class UserController {
@Autowired
@Qualifier("userDao1")
private UserDao userDao1;
@Autowired
@Qualifier("userDao2")
private UserDao userDao2;
@GetMapping("/users")
public List<User> getUsers() {
List<User> users1 = userDao1.selectAll();
List<User> users2 = userDao2.selectAll();
users1.addAll(users2);
return users1;
}
}
在这个例子中,我们从不同的数据源读取数据,并将这些数据合并在一起返回给客户端。可以通过访问 http://localhost:8080/users
来测试数据访问层的正确性。
3.2 示例二
在这个例子中,我们将从两个不同的数据库中选取不同的数据,并返回给客户端。
3.2.1 创建数据源
我们创建两个数据库:db_user1
和 db_user2
。其中,db_user1
中包含一张名为 t_user
的表,字段包括 id
和 username
;db_user2
中包含一张名为 t_address
的表,字段包括 id
和 address
。
3.2.2 配置数据源
在 application.yml
文件中,配置两个数据源:
spring:
datasource:
first:
url: jdbc:mysql://localhost:3306/db_user1
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
second:
url: jdbc:mysql://localhost:3306/db_user2
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
3.2.3 实现数据访问层
创建一个 UserDao
接口,实现读取全部用户数据的方法:
public interface UserDao {
List<User> selectAll();
}
创建一个 AddressDao
接口,实现读取全部地址数据的方法:
public interface AddressDao {
List<Address> selectAll();
}
创建一个 User
类,用于封装用户数据:
@Data
public class User {
private Long id;
private String username;
}
创建一个 Address
类,用于封装地址数据:
@Data
public class Address {
private Long id;
private String address;
}
实现两个具体的数据访问层类,分别针对不同的数据源:
@Repository("userDao1")
public class UserDao1Impl implements UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public List<User> selectAll() {
return jdbcTemplate.query("select * from t_user", new BeanPropertyRowMapper<>(User.class));
}
}
@Repository("addressDao2")
public class AddressDao2Impl implements AddressDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public List<Address> selectAll() {
return jdbcTemplate.query("select * from t_address", new BeanPropertyRowMapper<>(Address.class));
}
}
3.2.4 测试数据访问层
创建一个 UserController
控制器类:
@RestController
public class UserController {
@Autowired
@Qualifier("userDao1")
private UserDao userDao1;
@Autowired
@Qualifier("addressDao2")
private AddressDao addressDao2;
@GetMapping("/users")
public Map<String, List<?>> getUsers() {
Map<String, List<?>> result = new HashMap<>();
result.put("users", userDao1.selectAll());
result.put("addresses", addressDao2.selectAll());
return result;
}
}
在这个例子中,我们从不同的数据源读取数据,并将这些数据分别返回给客户端。可以通过访问 http://localhost:8080/users
来测试数据访问层的正确性。
4. 总结
通过使用 Spring Boot 自带的 AbstractRoutingDataSource
类和自定义数据源路由类,以及使用 @Aspect
注解和自定义数据源切换注解,我们可以实现对多个数据源的优雅整合。这种方式相对传统的多数据源实现方式,代码更加简洁美观,易于维护。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Spring Boot 优雅整合多数据源 - Python技术站