首页 > 开发 > Java > 正文

Spring boot实现数据库读写分离的方法

2024-07-13 10:03:15
字体:
来源:转载
供稿:网友

背景

数据库配置主从之后,如何在代码层面实现读写分离?

用户自定义设置数据库路由

Spring boot提供了AbstractRoutingDataSource根据用户定义的规则选择当前的数据库,这样我们可以在执行查询之前,设置读取从库,在执行完成后,恢复到主库。

实现可动态路由的数据源,在每次数据库查询操作前执行

ReadWriteSplitRoutingDataSource.java

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;/** * @author songrgg * @since 1.0 */public class ReadWriteSplitRoutingDataSource extends AbstractRoutingDataSource {  @Override  protected Object determineCurrentLookupKey() {    return DbContextHolder.getDbType();  }}

线程私有路由配置,用于ReadWriteSplitRoutingDataSource动态读取配置

DbContextHolder.java

/** * @author songrgg * @since 1.0 */public class DbContextHolder {  public enum DbType {    MASTER,    SLAVE  }  private static final ThreadLocal<DbType> contextHolder = new ThreadLocal<>();  public static void setDbType(DbType dbType) {    if(dbType == null){      throw new NullPointerException();    }    contextHolder.set(dbType);  }  public static DbType getDbType() {    return contextHolder.get() == null ? DbType.MASTER : contextHolder.get();  }  public static void clearDbType() {    contextHolder.remove();  }}

AOP优化代码

利用AOP将设置数据库的操作从代码中抽离,这里的粒度控制在方法级别,所以利用注解的形式标注这个方法涉及的数据库事务只读,走从库。

只读注解,用于标注方法的数据库操作只走从库。

ReadOnlyConnection.java

package com.wallstreetcn.hatano.config;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;/** * Indicates the database operations is bound to the slave database. * AOP interceptor will set the database to the slave with this interface. * @author songrgg * @since 1.0 */@Target({ElementType.METHOD, ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)public @interface ReadOnlyConnection {}

ReadOnlyConnectionInterceptor.java

import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.core.Ordered;import org.springframework.stereotype.Component;/** * Intercept the database operations, bind database to read-only database as this annotation * is applied. * @author songrgg * @since 1.0 */@Aspect@Componentpublic class ReadOnlyConnectionInterceptor implements Ordered {  private static final Logger logger = LoggerFactory.getLogger(ReadOnlyConnectionInterceptor.class);  @Around("@annotation(readOnlyConnection)")  public Object proceed(ProceedingJoinPoint proceedingJoinPoint, ReadOnlyConnection readOnlyConnection) throws Throwable {    try {      logger.info("set database connection to read only");      DbContextHolder.setDbType(DbContextHolder.DbType.SLAVE);      Object result = proceedingJoinPoint.proceed();      return result;    } finally {      DbContextHolder.clearDbType();      logger.info("restore database connection");    }  }  @Override  public int getOrder() {    return 0;  }}

UserService.java

@ReadOnlyConnectionpublic List<User> getUsers(Integer page, Integer limit) {  return repository.findAll(new PageRequest(page, limit));}

配置Druid数据库连接池

build.gradle

compile("com.alibaba:druid:1.0.18")

groovy依赖注入

配置dataSource为可路由数据源

context.groovy

import com.alibaba.druid.pool.DruidDataSourceimport DbContextHolderimport ReadWriteSplitRoutingDataSource** SOME INITIALIZED CODE LOAD PROPERTIES **def dataSourceMaster = new DruidDataSource()dataSourceMaster.url = properties.get('datasource.master.url')println("master set to " + dataSourceMaster.url)dataSourceMaster.username = properties.get('datasource.master.username')dataSourceMaster.password = properties.get('datasource.master.password')def dataSourceSlave = new DruidDataSource()dataSourceSlave.url = properties.get('datasource.slave.url')println("slave set to " + dataSourceSlave.url)dataSourceSlave.username = properties.get('datasource.slave.username')dataSourceSlave.password = properties.get('datasource.slave.password') beans {  dataSource(ReadWriteSplitRoutingDataSource) { bean ->    targetDataSources = [        (DbContextHolder.DbType.MASTER): dataSourceMaster,        (DbContextHolder.DbType.SLAVE): dataSourceSlave    ]  }}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持VeVb武林网。


注:相关教程知识阅读请移步到JAVA教程频道。
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表