美文网首页
spring boot自定义注解实现多数据源

spring boot自定义注解实现多数据源

作者: 董二弯 | 来源:发表于2020-04-02 00:20 被阅读0次

    实际开发中,很多项目都是单数据源。但是还是有一些项目需要多数据源的。
    比如当项目中存储的数据量很大,一个服务器扛不住,需要将一部分数据存储在另外一个服务器里,然而我们还要每天去访问这些存储的数据。
    这篇文章将以自定义注解的方式实现Springboot项目的多数据源配置,使用的数据库为mysql。

    搭建MySQL服务

    若没有mysql环境可参考 使用Docker搭建MySQL服务来搭建mysql环境。

    模拟多数据库

    搭建环境后,新建两个数据库,一个当主库,一个当从库。 两个数据库中建相同的一个user表。


    image.png

    添加数据


    image.png

    项目实现

    项目结构

    image.png

    引入pom文件

    <dependencies>
            <!-- 添加web启动坐标 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <!-- 添加mybatis依赖坐标 -->
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>2.1.2</version>
            </dependency>
    
            <!-- 添加mysql驱动器坐标 -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <scope>runtime</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-jdbc</artifactId>
            </dependency>
            
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
    
            <!-- 添加AOP坐标 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-aop</artifactId>
            </dependency>
    
            <!-- 添加druid数据源坐标 -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid-spring-boot-starter</artifactId>
                <version>1.1.10</version>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
                <exclusions>
                    <exclusion>
                        <groupId>org.junit.vintage</groupId>
                        <artifactId>junit-vintage-engine</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
        </dependencies>
    

    动态数据源配置

    这里使用的数据源为druid,实现数据源之间的切换用@DataSource自定义注解,配置Aop进行切换 application.yml 配置文件:

    server:
      port: 8200
    spring:
      datasource:
        type: com.alibaba.druid.pool.DruidDataSource
        druid:
          master: # 主数据源
            driverClassName: com.mysql.jdbc.Driver
            username: root
            password: root
            url: jdbc:mysql://localhost:3306/master?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8
          slave: # 从数据源
            driverClassName: com.mysql.jdbc.Driver
            username: root
            password: root
            url: jdbc:mysql://localhost:3306/slave?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8
    mybatis:
      mapper-locations: classpath:mapper/*.xml
    

    多数据源配置类

    @Configuration
    @Component
    public class DynamicDataSourceConfig {
    
        @Bean
        @ConfigurationProperties("spring.datasource.druid.master")
        public DataSource masterDataSource(){
            return DruidDataSourceBuilder.create().build();
        }
    
        @Bean
        @ConfigurationProperties("spring.datasource.druid.slave")
        public DataSource slaveDataSource(){
            return DruidDataSourceBuilder.create().build();
        }
    
        @Bean
        @Primary
        public DynamicDataSource dataSource(DataSource masterDataSource, DataSource slaveDataSource) {
            Map<Object, Object> targetDataSources = new HashMap<>();
            targetDataSources.put("master",masterDataSource);
            targetDataSources.put("slave", slaveDataSource);
            return new DynamicDataSource(masterDataSource, targetDataSources);
        }
    }
    
    public class DynamicDataSource  extends AbstractRoutingDataSource {
    
        private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
    
        public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
            super.setDefaultTargetDataSource(defaultTargetDataSource);
            super.setTargetDataSources(targetDataSources);
            super.afterPropertiesSet();
        }
    
        @Override
        protected Object determineCurrentLookupKey() {
            return getDataSource();
        }
    
        public static void setDataSource(String dataSource) {
            contextHolder.set(dataSource);
        }
    
        public static String getDataSource() {
            return contextHolder.get();
        }
    
        public static void clearDataSource() {
            contextHolder.remove();
        }
    }
    

    AbstractRoutingDataSource 根据用户定义的规则选择当前的数据源,这样我们可以在执行查询之前,设置使用的数据源。实现可动态路由的数据源,在每次数据库查询操作前执行。它的抽象方法 determineCurrentLookupKey() 决定使用哪个数据源。

    自定义@DataSource注解

    /**
     * 自定义多数据源注解
     *
     * @author dzy 2019-12-23 11:43:09
     */
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface DataSource {
        String name() default "";
    }
    

    作用于方法上,name = "master"代表操作主数据库,name="slave"代表操作从数据库。

    Aop切面类配置

    @Aspect
    @Component
    public class DataSourceAspect {
    
        @Pointcut("@annotation(com.example.dynamic.infra.annotation.DataSource)")
        public void dataSourcePointCut() {
    
        }
    
        @Around("dataSourcePointCut()")
        public Object around(ProceedingJoinPoint point) throws Throwable {
            MethodSignature signature = (MethodSignature) point.getSignature();
            Method method = signature.getMethod();
    
            DataSource dataSource = method.getAnnotation(DataSource.class);
            if(dataSource == null){
                DynamicDataSource.setDataSource("master");
            }else {
                DynamicDataSource.setDataSource(dataSource.name());
            }
            try {
                return point.proceed();
            } finally {
                DynamicDataSource.clearDataSource();
            }
        }
    }
    

    启动配置

    @SpringBootApplication(exclude= {DataSourceAutoConfiguration.class})
    @Import({DynamicDataSourceConfig.class})
    public class DynamicDataSourceApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(DynamicDataSourceApplication.class, args);
        }
    
    }
    

    测试

    controller:

    @RestController
    @RequestMapping("/v1/user")
    public class UserController {
    
        @Autowired
        private UserService userService;
    
        @GetMapping("/{name}/list")
        public List<User> list(@PathVariable("name")String name){
            if(name.equals("master")){
                return userService.selectUserListWithMaster();
            }else{
                return userService.selectUserListWithSlave();
            }
        }
    }
    
    

    service:

    public interface UserService {
    
        /**
         * 查询用户列表(主库)
         *
         * @return User
         */
        List<User> selectUserListWithMaster();
    
        /**
         * 查询用户列表(从库)
         *
         * @return User
         */
        List<User> selectUserListWithSlave();
    }
    
    @Service
    public class UserServiceImpl implements UserService {
        @Autowired
        private UserRepository userRepository;
    
        @Override
        public List<User> selectUserListWithMaster() {
            return userRepository.selectUserListWithMaster();
        }
    
        @Override
        public List<User> selectUserListWithSlave() {
            return userRepository.selectUserListWithSlave();
        }
    }
    

    repository

    public interface UserRepository {
        /**
         * 查询用户列表(主库)
         *
         * @return User
         */
        List<User> selectUserListWithMaster();
    
        /**
         * 查询用户列表(从库)
         *
         * @return User
         */
        List<User> selectUserListWithSlave();
    }
    
    @Repository
    public class UserRepositoryImpl implements UserRepository {
        @Autowired
        private UserMapper userMapper;
    
        @Override
        @DataSource(name = "master")
        public List<User> selectUserListWithMaster() {
            return userMapper.selectUserList();
        }
    
        @Override
        @DataSource(name = "slave")
        public List<User> selectUserListWithSlave() {
            return userMapper.selectUserList();
        }
    }
    

    mapper

    @Mapper
    public interface UserMapper {
        /**
         * 查询用户列表
         *
         * @return
         */
        List<User> selectUserList();
    }
    
    <mapper namespace="com.example.dynamic.infra.mapper.UserMapper">
        <select id="selectUserList" resultType="com.example.dynamic.domain.User">
            SELECT
               id,
               name
            FROM user
        </select>
    </mapper>
    

    user实体

    @Data
    public class User {
        private Long id;
        private String name;
    }
    

    效果图

    访问master


    image.png

    访问slave


    image.png

    相关文章

      网友评论

          本文标题:spring boot自定义注解实现多数据源

          本文链接:https://www.haomeiwen.com/subject/mzpauhtx.html