在做这个组件之前,我想应该考滤如下几个问题
对于第一个问题,因为这个组件是做的一个Spring一个分库分表的组件,我们大可使用SpringAop的特性来解决这个问题,比如:我自定义一个注解,只要在Mybatis的接口上标记了这个注解,在通过Spring Aop完美解决,那么在插入的时候我就需要分库分表的操作。
对于第二个问题,我可以采用Hash散列的方式来处理,在Java中的HashMap类中的put方法,我们完全可以借鉴这种思路。我想了解过HashMap源码的同鞋可能更加的清楚一些,在HashMap类中有一个hash的方扰动方法,这个方法中他把key的hash值进行了一个高半位和低半位的混合异或运算。这个便更好的增加随机性,更好的让数据均匀的散列。
可以看下下面这张图在使用了扰动函数后,数据分配的更加的均匀,这个可以更好的减少了hash碰撞。所以在解决第二个问题的时候,我们可以把这种Hash散列运用到数据库路由上。
那我们先来解决第一个问题。如何让程序知道,我们在某个数据库操作的时候需要进行分库分表,这里涉及到知识有自定义注解和Spring AOP相关的
project
└─src
└─main
├─java
│ └─com
│ └─yqs
│ └─db
│ └─router
│ ├─annotation
│ ├─config
│ ├─dynamic
│ ├─strategy
│ │ └─impl
│ └─util
└─resources
└─META-INF
先来大致的分下包:
先来看下在自定义注解中用到的元注解
注解有3种不同的生命周期 Class、Source、Runtime
- 注解只保留在源代码中,当把Java文件编辑成Class字节码的时候,此注解消失。
- 注解只保留在Class字节码中,当JVM在加载Class文件的时候,此注解消失。
- 注解在运行期间一直保留。
这个枚举类中就对应了上面说的三种生命周期
类型 | 描述 |
---|---|
CLASS | 注解只保留在Class字节码中,当JVM在加载Class文件的时候 |
SOURCE | 注解只保留在源代码中 |
RUNTIME | 注解在运行期间一直保留 |
Target和上面说的那个注解一样,都是Java的元注解,Target注解标记当前注解可以标记在哪些位置,这里只看本文章用到的注解,注解也可以标记在不同的地方,比如 类、方法、字段、构造方法。
@Target({ElementType.TYPE,ElementType.METHOD})
复制代码
我想你一定在代码见过如上代码,此代码表明,当前的注解可以作用在方法,类上。加在其它的位置就会出现语法的错误。
我想在做这个项目之前应该先了解下这个组件的配置文件是什么样子的,这样在看后面的文章的时候可能更容易理解一些
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.url=jdbc:mysql://ip:port/vipuser?useSSL=false
server.port=8080
mybatis.mapper-locations=classpath:mapper/*.xml
# 有几个库
db-router.db.datasource.dbCount=3
# 每一个库里有几个表
# 这里所说的表是相同的表比如有一个user表,
# 然后会多复制出几个相同的表比如命名成 user_01,user_02,user_03,user_04
db-router.db.datasource.tbCount=4
# 这个配置你可以理解成默认的数据源是哪一个
db-router.db.datasource.default=db00
# 这个配置是除了默认的数据源外的数据源
db-router.db.datasource.list=db01,db02
db-router.db.datasource.db00.driver-class-name=com.mysql.jdbc.Driver
db-router.db.datasource.db00.url=jdbc:mysql://ip:port/vipuser?useSSL=false
db-router.db.datasource.db00.username=root
db-router.db.datasource.db00.password=123456
db-router.db.datasource.db01.driver-class-name=com.mysql.jdbc.Driver
db-router.db.datasource.db01.url=jdbc:mysql://ip:port/vipuser?useSSL=false
db-router.db.datasource.db01.username=root
db-router.db.datasource.db01.password=123456
db-router.db.datasource.db02.driver-class-name=com.mysql.jdbc.Driver
db-router.db.datasource.db02.url=jdbc:mysql://ip:port/vipuser?useSSL=false
db-router.db.datasource.db02.username=root
db-router.db.datasource.db02.password=123456
复制代码
首页,我们上面说过要根据存入数据库的数据来计算将数据分配到哪个库哪张表里面。所以在定义这个注解的时候,我需要一个注解内的参数用来表明,根据哪个字段的数据计算所分配的库表。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface DBRouter {
/**
* 需要进行分表分库的字段
* 通过此字段来计算分到哪个表哪个库
* @return
*/
String key() default