最新要闻

广告

手机

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

家电

SpringBoot利用自定义注解实现多数据源

来源:博客园

自定义多数据源

SpringBoot利用自定义注解实现多数据源,前置知识:注解、Aop、SpringBoot整合Mybaits

1、搭建工程

创建一个SpringBoot工程,并引入依赖


(资料图片)

                    org.springframework.boot            spring-boot-starter-web                                    org.springframework.boot            spring-boot-starter-aop                            org.mybatis.spring.boot            mybatis-spring-boot-starter            2.3.0                            com.mysql            mysql-connector-j            runtime                                    com.alibaba            druid-spring-boot-starter            1.2.18                            org.springframework.boot            spring-boot-starter-test            test            

2、定义多数据源注解

/** * 1、定义多数据源注解 * @author ss_419 * TODO 这个注解将来可以加在service类上或者方法上,通过value属性来指定类或者方法应该使用那个数据源 */@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.TYPE, ElementType.METHOD})public @interface DataSource {    String value() default DataSourceType.DEFAULT_DS_NAME;}

3、创建一个多数据上下文对象

这个类用来存储当前线程所使用的数据源名称

/** * TODO 这个类用来存储当前线程所使用的数据源名称 * * @author ss_419 * @version 1.0 * @date 2023/5/21 09:21 */public class DynamicDataSourceContextHolder {    private static final ThreadLocal CONTEXT_HOLDER = new ThreadLocal<>();    public static void setDataSourceType(String dataSourceType) {        CONTEXT_HOLDER.set(dataSourceType);    }    public static String getDataSourceType() {        return CONTEXT_HOLDER.get();    }    public static void clearDataSourceType() {        CONTEXT_HOLDER.remove();    }}

4、配置aop

  • @annotation(org.pp.dd.annotation.DataSource) 如果有@DataSource注解就给拦截下来

  • @within(org.pp.dd.annotation.DataSource) 表示类上有@DataSource注解就将类中的方法给拦截下来

/** * TODO * * @author ss_419 * @version 1.0 * @date 2023/5/21 09:42 */@Component@Aspect@Order(11)public class DataSourceAspect {    /**     * 定义切点     *     * @annotation(org.pp.dd.annotation.DataSource) 如果有@DataSource注解就给拦截下来     * @within(org.pp.dd.annotation.DataSource) 表示类上有@DataSource注解就将类中的方法给拦截下来     */    @Pointcut("@annotation(org.pp.dd.annotation.DataSource) || @within(org.pp.dd.annotation.DataSource)")    public void pc() {    }    /**     * 环绕通知     *     * @param pjp     * @return     */    @Around("pc()")    public Object around(ProceedingJoinPoint pjp) {        // 获取方法上的有效注解        DataSource dataSource = getDataSource(pjp);        if (dataSource != null) {            // 获取注解中数据源的名称            String value = dataSource.value();            DynamicDataSourceContextHolder.setDataSourceType(value);        }        try {            return pjp.proceed();        } catch (Throwable e) {            throw new RuntimeException(e);        } finally {            DynamicDataSourceContextHolder.clearDataSourceType();        }    }    private DataSource getDataSource(ProceedingJoinPoint pjp) {        MethodSignature signature = (MethodSignature) pjp.getSignature();        // 获取方法上的注解        DataSource annotation = AnnotationUtils.findAnnotation(signature.getMethod(), DataSource.class);        if (annotation != null) {            // 说明方法上有注解            return annotation;        }        return (DataSource) AnnotationUtils.findAnnotation(signature.getDeclaringType(), DataSource.class);    }}

5、读取参数DruidProperties

/** * TODO 读取数据源 * * @author ss_419 * @version 1.0 * @date 2023/5/21 10:20 */@ConfigurationProperties(prefix = "spring.datasource")public class DruidProperties {    private String type;    private String driverClassName;    private Map> ds;    private Integer initialSize;    private Integer minIdle;    private Integer maxActive;    private Integer maxWait;    /**     * 在这个方法中设置公共属性     * @param dataSource     * @return     */    public DataSource dataSource(DruidDataSource dataSource){        dataSource.setInitialSize(initialSize);        dataSource.setMinIdle(minIdle);        dataSource.setMaxActive(maxActive);        dataSource.setMaxWait(maxWait);        return dataSource;    }    public String getType() {        return type;    }    public void setType(String type) {        this.type = type;    }    public String getDriverClassName() {        return driverClassName;    }    public void setDriverClassName(String driverClassName) {        this.driverClassName = driverClassName;    }    public Map> getDs() {        return ds;    }    public void setDs(Map> ds) {        this.ds = ds;    }    public Integer getInitialSize() {        return initialSize;    }    public void setInitialSize(Integer initialSize) {        this.initialSize = initialSize;    }    public Integer getMinIdle() {        return minIdle;    }    public void setMinIdle(Integer minIdle) {        this.minIdle = minIdle;    }    public Integer getMaxActive() {        return maxActive;    }    public void setMaxActive(Integer maxActive) {        this.maxActive = maxActive;    }    public Integer getMaxWait() {        return maxWait;    }    public void setMaxWait(Integer maxWait) {        this.maxWait = maxWait;    }}

6、加载数据源LoadDataSource

/** * TODO 加载数据源 * * @author ss_419 * @version 1.0 * @date 2023/5/21 10:30 */@Component@EnableConfigurationProperties(DruidProperties.class)public class LoadDataSource {    @Autowired    DruidProperties druidProperties;    public Map loadAllDataSource() {        Map map = new HashMap<>();        Map> ds = druidProperties.getDs();        try {            Set keySet = ds.keySet();            for (String key : keySet) {                map.put(key, druidProperties.dataSource((DruidDataSource) DruidDataSourceFactory.createDataSource(ds.get(key))));            }        } catch (Exception e) {            throw new RuntimeException(e);        }        return map;    }}

7、定义数据源管理器

当系统需要调用数据源的时候,数据源以key-value存起来,当需要数据源时调用determineCurrentLookupKey()方法来获取数据源。由于本人实力原因,解答不了大家这里的疑惑。大致功能 通过修改本地线程的值,来实现数据源的切换。

/** * TODO 设置数据源 * 当系统需要调用数据源的时候,数据源以key-value存起来,当需要数据源时调用determineCurrentLookupKey()方法 * @author ss_419 * @version 1.0 * @date 2023/5/21 10:47 */@Componentpublic class DynamicDataSource extends AbstractRoutingDataSource {    public DynamicDataSource(LoadDataSource loadDataSource) {        //1、设置所有的数据源        Map allDs = loadDataSource.loadAllDataSource();        super.setTargetDataSources(new HashMap<>(allDs));        //2、设置默认数据源        super.setDefaultTargetDataSource(allDs.get(DataSourceType.DEFAULT_DS_NAME));        super.afterPropertiesSet();    }    /**     * 这个方法用来返回数据源名称,当系统需要获取数据源的时候会自动调用该方法获取数据源名称     * @return     */    @Override    protected Object determineCurrentLookupKey() {        return DynamicDataSourceContextHolder.getDataSourceType();    }}

定一个用于存储数据库类型的接口,这个接口类似于枚举类:

/** * TODO * * @author ss_419 * @version 1.0 * @date 2023/5/21 10:54 */public interface DataSourceType {    String DEFAULT_DS_NAME = "master";    String DS_SESSION_KEY = "ds_session_key";}

8、测试

创建User实体:

/** * TODO * * @author ss_419 * @version 1.0 * @date 2023/5/21 11:15 */public class User {    private Integer id;    private String username;    private String password;    @Override    public String toString() {        return "User{" +                "id=" + id +                ", username="" + username + "\"" +                ", password="" + password + "\"" +                "}";    }    public Integer getId() {        return id;    }    public void setId(Integer id) {        this.id = id;    }    public String getUsername() {        return username;    }    public void setUsername(String username) {        this.username = username;    }    public String getPassword() {        return password;    }    public void setPassword(String password) {        this.password = password;    }}

创建UserService:

@Service// 在类上加注解的效果,会使该类的所有方法都切入到新的数据源中//@DataSourcepublic class UserService {    @Autowired    UserMapper userMapper;    // 在方法上加注解的效果,只会让指定的方法切入到另一个数据源中    //@DataSource("slave")    public List findUsers(){        return userMapper.findAllUsers();    }}

创建UserMapper:

@Mapperpublic interface UserMapper {    @Select("SELECT * FROM user")    List findAllUsers();}

测试类:

@SpringBootTestclass DynamicDatasourcesApplicationTests {    @Autowired    UserService userService;    @Test    void contextLoads() {        List users = userService.findUsers();        users.stream()                .forEach(user -> System.out.println(user));    }}

默认选择主库的数据源:

执行结果如下:

在Service上加上注解,指定数据源为从库:执行结果如下:

关键词: