Spring boot实现数据库读写分离的方法
背景
数据库配置主从之后,如何在代码层面实现读写分离?
用户自定义设置数据库路由
Springboot提供了AbstractRoutingDataSource根据用户定义的规则选择当前的数据库,这样我们可以在执行查询之前,设置读取从库,在执行完成后,恢复到主库。
实现可动态路由的数据源,在每次数据库查询操作前执行
ReadWriteSplitRoutingDataSource.java
importorg.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
*@authorsongrgg
*@since1.0
*/
publicclassReadWriteSplitRoutingDataSourceextendsAbstractRoutingDataSource{
@Override
protectedObjectdetermineCurrentLookupKey(){
returnDbContextHolder.getDbType();
}
}
线程私有路由配置,用于ReadWriteSplitRoutingDataSource动态读取配置
DbContextHolder.java
/**
*@authorsongrgg
*@since1.0
*/
publicclassDbContextHolder{
publicenumDbType{
MASTER,
SLAVE
}
privatestaticfinalThreadLocal<DbType>contextHolder=newThreadLocal<>();
publicstaticvoidsetDbType(DbTypedbType){
if(dbType==null){
thrownewNullPointerException();
}
contextHolder.set(dbType);
}
publicstaticDbTypegetDbType(){
returncontextHolder.get()==null?DbType.MASTER:contextHolder.get();
}
publicstaticvoidclearDbType(){
contextHolder.remove();
}
}
AOP优化代码
利用AOP将设置数据库的操作从代码中抽离,这里的粒度控制在方法级别,所以利用注解的形式标注这个方法涉及的数据库事务只读,走从库。
只读注解,用于标注方法的数据库操作只走从库。
ReadOnlyConnection.java
packagecom.wallstreetcn.hatano.config;
importjava.lang.annotation.ElementType;
importjava.lang.annotation.Retention;
importjava.lang.annotation.RetentionPolicy;
importjava.lang.annotation.Target;
/**
*Indicatesthedatabaseoperationsisboundtotheslavedatabase.
*AOPinterceptorwillsetthedatabasetotheslavewiththisinterface.
*@authorsongrgg
*@since1.0
*/
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public@interfaceReadOnlyConnection{
}
ReadOnlyConnectionInterceptor.java
importorg.aspectj.lang.ProceedingJoinPoint;
importorg.aspectj.lang.annotation.Around;
importorg.aspectj.lang.annotation.Aspect;
importorg.slf4j.Logger;
importorg.slf4j.LoggerFactory;
importorg.springframework.core.Ordered;
importorg.springframework.stereotype.Component;
/**
*Interceptthedatabaseoperations,binddatabasetoread-onlydatabaseasthisannotation
*isapplied.
*@authorsongrgg
*@since1.0
*/
@Aspect
@Component
publicclassReadOnlyConnectionInterceptorimplementsOrdered{
privatestaticfinalLoggerlogger=LoggerFactory.getLogger(ReadOnlyConnectionInterceptor.class);
@Around("@annotation(readOnlyConnection)")
publicObjectproceed(ProceedingJoinPointproceedingJoinPoint,ReadOnlyConnectionreadOnlyConnection)throwsThrowable{
try{
logger.info("setdatabaseconnectiontoreadonly");
DbContextHolder.setDbType(DbContextHolder.DbType.SLAVE);
Objectresult=proceedingJoinPoint.proceed();
returnresult;
}finally{
DbContextHolder.clearDbType();
logger.info("restoredatabaseconnection");
}
}
@Override
publicintgetOrder(){
return0;
}
}
UserService.java
@ReadOnlyConnection
publicList<User>getUsers(Integerpage,Integerlimit){
returnrepository.findAll(newPageRequest(page,limit));
}
配置Druid数据库连接池
build.gradle
compile("com.alibaba:druid:1.0.18")
groovy依赖注入
配置dataSource为可路由数据源
context.groovy
importcom.alibaba.druid.pool.DruidDataSource
importDbContextHolder
importReadWriteSplitRoutingDataSource
**SOMEINITIALIZEDCODELOADPROPERTIES**
defdataSourceMaster=newDruidDataSource()
dataSourceMaster.url=properties.get('datasource.master.url')
println("mastersetto"+dataSourceMaster.url)
dataSourceMaster.username=properties.get('datasource.master.username')
dataSourceMaster.password=properties.get('datasource.master.password')
defdataSourceSlave=newDruidDataSource()
dataSourceSlave.url=properties.get('datasource.slave.url')
println("slavesetto"+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
]
}
}
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。