欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

java自定义注解实现多数据源切换

程序员文章站 2022-05-17 08:55:20
...

文章目录

应用场景

项目需要从自己的数据库上读取和管理数据外,还有一部分业务涉及到其他多个数据库。

为了能够灵活地指定具体的数据库,本文基于注解和AOP的方法实现多数据源自动切换。在使用过程中,只需要添加注解就可以使用,简单方便。

入门代码

废话不多说,开整

工程目录如下
java自定义注解实现多数据源切换

sql文件
test1库goods表,test2库order_form表

/*
 Navicat MySQL Data Transfer

 Source Server         : localhost
 Source Server Type    : MySQL
 Source Server Version : 50712
 Source Host           : localhost:3306
 Source Schema         : test1

 Target Server Type    : MySQL
 Target Server Version : 50712
 File Encoding         : 65001

 Date: 09/01/2020 14:52:01
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for goods
-- ----------------------------
DROP TABLE IF EXISTS `goods`;
CREATE TABLE `goods`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `goods_id` int(10) NULL DEFAULT NULL,
  `stock` int(10) NULL DEFAULT NULL,
  `group_id` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of goods
-- ----------------------------
INSERT INTO `goods` VALUES (1, 31, 100, '');

SET FOREIGN_KEY_CHECKS = 1;


/*
 Navicat MySQL Data Transfer

 Source Server         : localhost
 Source Server Type    : MySQL
 Source Server Version : 50712
 Source Host           : localhost:3306
 Source Schema         : test2

 Target Server Type    : MySQL
 Target Server Version : 50712
 File Encoding         : 65001

 Date: 09/01/2020 13:53:07
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for order_form
-- ----------------------------
DROP TABLE IF EXISTS `order_form`;
CREATE TABLE `order_form`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `goods_id` int(20) NULL DEFAULT NULL,
  `goods_name` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL,
  `user_id` int(10) NULL DEFAULT NULL,
  `create_time` timestamp(0) NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP(0),
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 46 CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

pom.xml

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.wangtao</groupId>
    <artifactId>datasource</artifactId>
    <version>1.0-SNAPSHOT</version>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.1.RELEASE</version>
        <relativePath/>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!-- springboot-aop包,AOP切面注解,Aspectd等相关注解 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
    </dependencies>
</project>

application.yml

server:
  port: 8080
spring:
  datasource:
    test1:
      driver-class-name: com.mysql.jdbc.Driver
      initial-size: 5
      max-idle: 10
      max-wait: 10000
      min-idle: 5
      name: test1
      password: 123456
      test-on-borrow: false
      test-while-idle: true
      time-between-eviction-runs-millis: 18800
      jdbc-url: jdbc:mysql://localhost:3306/test1?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true
      username: root
      validation-query: SELECT 1
    test2:
      driver-class-name: com.mysql.jdbc.Driver
      initial-size: 5
      max-idle: 10
      max-wait: 10000
      min-idle: 5
      name: test2
      password: 123456
      test-on-borrow: fa
      test-while-idle: true
      time-between-eviction-runs-millis: 18800
      jdbc-url: jdbc:mysql://localhost:3306/test2?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true
      username: root
      validation-query: SELECT 1

自定义注解DS

package com.wangtao.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface DS {
    String value() default "";
}

数据源配置类
DataSourceConfig.java

package com.wangtao.config.dataSource;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;


@Configuration
public class DataSourceConfig {

    //test1数据源
    @Bean(name = "test1")
    @ConfigurationProperties(prefix = "spring.datasource.test1") // application.properteis中对应属性的前缀
    public DataSource test1() {
        return DataSourceBuilder.create().build();
    }

    //test2数据源
    @Bean(name = "test2")
    @ConfigurationProperties(prefix = "spring.datasource.test2") // application.properteis中对应属性的前缀
    public DataSource test2() {
        return DataSourceBuilder.create().build();
    }

    /**
     * 动态数据源: 通过AOP在不同数据源之间动态切换
     *
     * @return
     */
    @Primary
    @Bean(name = "dynamicDataSource")
    public DataSource dynamicDataSource() {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        // 默认数据源
        dynamicDataSource.setDefaultTargetDataSource(test1());
        // 配置多数据源
        Map<Object, Object> dsMap = new HashMap();
        dsMap.put("test1", test1());
        dsMap.put("test2", test2());
        dynamicDataSource.setTargetDataSources(dsMap);
        return dynamicDataSource;
    }

    /**
     * 配置@Transactional注解事物
     *
     * @return
     */
    @Bean
    public PlatformTransactionManager transactionManager() {
        return new DataSourceTransactionManager(dynamicDataSource());
    }

}

DataSourceContextHolder.java

package com.wangtao.config.dataSource;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class DataSourceContextHolder {

    //    默认数据源
    public static final String DEFAULT_DS = "test1";

    private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();

    /**
     * 设置数据源名
     *
     * @param dbType
     */
    public static void setDB(String dbType) {
        log.info("切换到{" + dbType + "}数据源");
        contextHolder.set(dbType);
    }

    /**
     * 获取数据源名
     *
     * @return
     */
    public static String getDB() {
        return (contextHolder.get());
    }

    /**
     * 清除数据源名
     */
    public static void clearDB() {
        contextHolder.remove();
    }

}

DynamicDataSource.java

package com.wangtao.config.dataSource;

import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;


@Slf4j
public class DynamicDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        log.info("数据源为" + DataSourceContextHolder.getDB());
        return DataSourceContextHolder.getDB();
    }


}

数据源切面
DynamicDataSourceAspect.java

package com.wangtao.config.dataSource;

import com.wangtao.annotation.DS;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Aspect
@Component
@Order(1)
public class DynamicDataSourceAspect {
    @Before("@annotation(com.wangtao.annotation.DS)")
    public void beforeSwitchDS(JoinPoint point) {
        //获得当前访问的class
        Class<?> className = point.getTarget().getClass();
        //获得访问的方法名
        String methodName = point.getSignature().getName();
        //得到方法的参数的类型
        Class[] argClass = ((MethodSignature) point.getSignature()).getParameterTypes();
        String dataSource = DataSourceContextHolder.DEFAULT_DS;
        try {
            // 得到访问的方法对象
            Method method = className.getMethod(methodName, argClass);
            // 判断是否存在@DS注解
            if (method.isAnnotationPresent(DS.class)) {
                DS annotation = method.getAnnotation(DS.class);
                // 取出注解中的数据源名
                dataSource = annotation.value();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 切换数据源
        DataSourceContextHolder.setDB(dataSource);
    }

    @After("@annotation(com.wangtao.annotation.DS)")
    public void afterSwitchDS(JoinPoint point) {
        DataSourceContextHolder.clearDB();
    }

}

controller层
DataSourceController.java

package com.wangtao.controller;

import com.wangtao.enity.Goods;
import com.wangtao.enity.Order;
import com.wangtao.service.DataSourceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.Arrays;
import java.util.List;

@Controller
public class DataSourceController {
    
    @Autowired
    private DataSourceService dataSourceService;

    @RequestMapping("/test")
    @ResponseBody
    public List test() {
        List<Order> orderlist = dataSourceService.findOrderForm();
        List<Goods> goodslist = dataSourceService.findGoods();
        return Arrays.asList(orderlist,goodslist);
    }
}

entity
Goods.java

package com.wangtao.enity;

import lombok.Data;

import javax.persistence.*;

@Entity
@Data
@Table(name = "goods")
public class Goods {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY) // 自增类型的主键
    private int id;

    @Column(name = "goods_id")
    private int goodsId;

    private int stock;

    @Column(name = "group_id")
    private String groupId;


}

Order.java

package com.wangtao.enity;

import lombok.Data;

import javax.persistence.*;
import java.util.Date;

@Data
@Entity
@Table(name = "order_form")
public class Order {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY) // 自增类型的主键
    private int id;

    @Column(name = "goods_id")
    private int goodsId;

    @Column(name = "goods_name")
    private String goodsName;

    @Column(name="user_id")
    private int userId;

    @Column(name = "create_time")
    private Date createTime;

}

mapper文件
DataSourceMapper.java

package com.wangtao.mapper;

import com.wangtao.enity.Goods;
import com.wangtao.enity.Order;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

import java.util.List;

@Mapper
public interface DataSourceMapper {

	@Select(" select * from goods ")
	List<Goods> findGoods();

	@Select(" select * from order_form ")
	List<Order> findOrderForm();

}

service层
DataSourceService.java

package com.wangtao.service;

import com.wangtao.enity.Goods;
import com.wangtao.enity.Order;

import java.util.List;

public interface DataSourceService {
    List<Order> findOrderForm();

    List<Goods> findGoods();
}

DataSourceServiceImpl.java

package com.wangtao.service.impl;

import com.wangtao.annotation.DS;
import com.wangtao.mapper.DataSourceMapper;
import com.wangtao.service.DataSourceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class DataSourceServiceImpl implements DataSourceService {

    @Autowired
    private DataSourceMapper dataSourceMapper;

    @DS("test2")
    @Override
    public List findOrderForm() {
        return dataSourceMapper.findOrderForm();
    }

    @DS("test1")
    @Override
    public List findGoods() {
        return dataSourceMapper.findGoods();
    }
}

启动类
Application.java

package com.wangtao;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@ComponentScan(value = "com.wangtao")
@EnableTransactionManagement(order = 10)
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class,args);
    }
}

这里注意一下@EnableTransactionManagement这个注解很重要,我就在遇到过在配了shiroconfig的地方切换不了数据源,加上这个就好使了,原因是shiro底层也使用了aop,把优先级设置一下,就好使了。

启动项目,访问http://localhost:8080/test
可以看到页面输出java自定义注解实现多数据源切换
控制台输出
java自定义注解实现多数据源切换
就这样完成了多数据源切换,完毕…

相关标签: 工作常用