Poison

读写分离

之前介绍了多数据源的接入,参见 Multiple Datasource ,后来一个数据分析的项目(大部分都是读操作)需要用到读写分离,在分析时读取从库的数据,避免增加对线上数据库的压力,少部分写操作依然写主库,然后再被同步至从库,根据同事的建议,希望采用注解方式实现,从而在开发时只需加上特定的注解即可表明此 DAO 操作主库还是从库,原理依然与之前类似,以下是调整的部分:

  1. 定义主从数据库的枚举,因为项目中大多数走从库,所以吧 SLAVE 写在了第一个

    1
    2
    3
    4
    5
    6
    7
    /**
    * Created by Poison on 8/15/2016.
    */
    public enum Database {
    SLAVE,
    MASTER
    }
  2. 定义切换数据源的注解,注解基础可参见 Lesson: Annotations ,根据同事需要只定义了类级别的注解,保留到运行时,读者完全可以根据自身需要自由发挥

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    /**
    * Created by Poison on 8/16/2016.
    * This is a marker annotation.
    * Use this annotation on DAO interface level, represent that all methods in this interface will operate your specified database.
    */
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    public @interface DataSource {
    Database value() default Database.SLAVE;
    }
  3. 修改 AOP 相关代码,只有在 DAO 接口上应用了 @DataSource 注解,并且注解值为 Database.MASTER 时才走主库

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    /**
    * Created by Poison on 8/15/2016.
    */
    @Aspect
    @Component
    public class AroundDataSource {

    @Around("execution(* me.tianshuang.dao..*.*(..))")
    public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
    Object result;
    if (hasMasterAnnotation(pjp)) {
    DataSourceContextHolder.setDataSource(Database.MASTER);
    result = pjp.proceed();
    DataSourceContextHolder.restoreToSlaveDataSource();
    } else {
    result = pjp.proceed();
    }
    return result;
    }

    private boolean hasMasterAnnotation(ProceedingJoinPoint pjp) {
    Class<?> declaringClass = ((MethodSignature) pjp.getSignature()).getMethod().getDeclaringClass();
    if (declaringClass.isAnnotationPresent(DataSource.class)) {
    DataSource dataSource = declaringClass.getAnnotation(DataSource.class);
    if (dataSource.value() == Database.MASTER) {
    return true;
    }
    }
    return false;
    }

    }