Spring Boot 优雅整合多数据源

yizhihongxing

下面是 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_user1db_user2,并分别在其中创建一张名为 t_user 的表,其中字段包括 idusername

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_user1db_user2。其中,db_user1 中包含一张名为 t_user 的表,字段包括 idusernamedb_user2 中包含一张名为 t_address 的表,字段包括 idaddress

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技术站

(0)
上一篇 2023年5月20日
下一篇 2023年5月20日

相关文章

  • 详解Java中的延时队列 DelayQueue

    详解Java中的延时队列 DelayQueue 概述 DelayQueue是Java中的一个实现了Delayed的队列,它按照剩余时间从少到多的顺序对元素进行排序,每个元素都有一个过期时间,只有过期的元素才能被取出。 延时队列的实现 延时队列的实现需要实现Delayed接口,并重写getDelay()方法和compareTo()方法。 public inte…

    Java 2023年5月26日
    00
  • springboot 传参校验@Valid及对其的异常捕获方式

    下面我来详细讲解一下“springboot 传参校验@Valid及对其的异常捕获方式”的完整攻略。 1. 什么是@Valid注解 Spring Boot 在处理 Web 请求时,通常会使用数据绑定将请求中的数据映射到 Controller 中的方法参数列表里。当数据格式不正确或缺失时,我们往往会在方法中手动校验数据,这会增加开发的耗时,也容易产生错误。而@V…

    Java 2023年5月27日
    00
  • Spring依赖注入的几种方式分享梳理总结

    Spring依赖注入的几种方式分享梳理总结 什么是依赖注入(Dependency Injection,DI) 简单来说,依赖注入就是将对象所依赖的其他对象注入到其内部。这样可以达到解耦的效果,提高代码的可维护性。 通常,依赖注入需要依赖容器完成,目前比较常用的容器有Spring、Guice等。 Spring依赖注入的几种方式 1.构造注入(Construct…

    Java 2023年5月19日
    00
  • 详解Java Fibonacci Search斐波那契搜索算法代码实现

    详解Java Fibonacci Search斐波那契搜索算法代码实现 什么是斐波那契搜索算法? 斐波那契搜索算法是一种基于斐波那契数列的搜索算法,它主要用于在一个有序的列表中查找指定的元素。斐波那契搜索算法相对于传统的二分查找算法,在查找长度较大的有序列表时,具有更好的效率表现。 算法实现 以下是按照Java语言实现的完整的斐波那契搜索算法代码: publ…

    Java 2023年5月19日
    00
  • 一篇超详细的Spring Boot整合Mybatis文章

    Spring Boot整合MyBatis完整攻略 Spring Boot是一个快速开发框架,可以帮助开发人员快速构建Web应用程序。在Spring Boot中,整合MyBatis可以帮助我们更方便地操作数据库。本文将介绍如何在Spring Boot中整合MyBatis,并提供两个示例。 整合MyBatis 在Spring Boot中整合MyBatis需要以下…

    Java 2023年5月15日
    00
  • jquery zTree异步加载简单实例分享

    首先,让我们来了解一下什么是 jQuery zTree 以及异步加载。 jQuery zTree 是什么? jQuery zTree 是一款基于 jQuery 的树形视图插件,它具有结构清晰、功能强大和使用简便的特点。它可以帮助我们轻松实现一个树形结构的网页,比如分类列表、目录树、导航菜单等等。 异步加载是什么? 当我们需要渲染的树形结构数据较大时,如果一次…

    Java 2023年6月15日
    00
  • JSON各种转换问题(json转List,json转对象等)

    让我为你详细讲解一下“JSON各种转换问题(json转List,json转对象等)”的完整攻略。 JSON转List 假设有一个JSON数组如下: [ { "name": "张三", "age": 23 }, { "name": "李四", "age…

    Java 2023年5月26日
    00
  • Java中Date类和Calendar类的常用实例小结

    我来为你详细讲解 Java 中 Date 类和 Calendar 类的常用实例小结。 一、Date类的常用实例 1. 获取当前的日期和时间 使用 java.util.Date 类提供的无参构造方法可以获取当前的日期和时间。例如: Date date = new Date(); // 获取当前的日期和时间 2. 格式化日期 使用 SimpleDateForma…

    Java 2023年5月20日
    00
合作推广
合作推广
分享本页
返回顶部