近来参与了spring-boot主从数据库的设计,参考了一些网上资料,发现不完整.
整理了个完整版的^ ^!
spring-boot主从数据库
是从springmvc的思路上来做的,主要就是配置主,从DataSource,
再继承AbstractRoutingDataSource,重写determineCurrentLookupKey()方法,
通过Context结合AOP进行数据主,从库的切换.
Spring boot提供了AbstractRoutingDataSource根据用户定义的规则选择当前的数据库,这样我们可以在执行查询之前,设置读取从库,在执行完成后,恢复到主库。
1.实现可动态路由的数据源,在每次数据库查询操作前执行
ReadWriteSplitRoutingDataSource.java
package com.bag.config.mybatis;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class ReadWriteSplitRoutingDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DbContextHolder.getDbType();
}
}
2.线程私有路由配置,用于ReadWriteSplitRoutingDataSource动态读取配置
DbContextHolder.java
package com.bag.config.mybatis;
public class DbContextHolder {
public enum DbType {
MASTER, SLAVE
}
private static final 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将设置数据库的操作从代码中抽离,这里的粒度控制在方法级别,所以利用注解的
形式标注这个方法涉及的数据库事务只读,走从库.
3.只读注解,用于标注方法的数据库操作只走从库.
ReadOnlyConnectionction.java
package com.bag.config.mybatis;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ReadOnlyConnection {
}
ReadOnlyConnectionInterceptor.java
package com.bag.config.mybatis;
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;
/**
* Spring AOP对注解的拦截
*/
@Aspect
@Component
public class ReadOnlyConnectionInterceptor implements Ordered {
public static final Logger logger = LoggerFactory.getLogger(ReadOnlyConnectionInterceptor.class);
/**
* 切换到从库
*
* @param proceedingJoinPoint
* @param readOnlyConnection
* @return
* @throws Throwable
*/
@Around("@annotation(readOnlyConnection)")
public Object proceed(ProceedingJoinPoint proceedingJoinPoint, ReadOnlyConnection readOnlyConnection) throws Throwable {
try {
// logger.info("设置只读数据库");
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
@ReadOnlyConnection
public List
return repository.findAll(new PageRequest(page, limit));
}
配置文件:
application.properties
# 主数据库
spring.datasource.url=
spring.datasource.username=
spring.datasource.password=
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.testOnBorrow=true
spring.datasource.validation-query=SELECT 1
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.initial-size=5
spring.datasource.min-idle=5
spring.datasource.max-active=20
spring.datasource.maxWait=200000
spring.datasource.timeBetweenEvictionRunsMillis=30000
spring.datasource.minEvictableIdleTimeMillis=30000
spring.datasource.validationQuery=SELECT 1 FROM DUAL
spring.datasource.testWhileIdle=true
spring.datasource.testOnReturn=false
spring.datasource.poolPreparedStatements=true
spring.datasource.maxPoolPreparedStatementPerConnectionSize=20
spring.datasource.filters=stat,wall,log4j
spring.datasource.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
#超过时间限制是否回收
spring.datasource.removeAbandoned=true
#超时时间;单位为秒。180秒=3分钟
spring.datasource.removeAbandonedTimeout=5
# 从数据源
spring.slave.type=com.alibaba.druid.pool.DruidDataSource
spring.slave.driver-class-name=com.mysql.jdbc.Driver
spring.slave.url=
spring.slave.username=
spring.slave.password=
spring.slave.initial-size=5
spring.slave.min-idle=5
spring.slave.max-active=20