修饰符
访问修饰符必须为public,不写默认为public
关键字
关键字为@interface;
注解名称
自定义名称
注解类型元素
注解类型元素是注解中内容,可以理解成自定义接口的实现部分;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSourceSign {
/**
* 切换数据源名称
*/
//返回类型 方法 默认值。
DbType value() default DbType.MASTER;
}
JDK中有一些元注解,主要有@Target,@Retention,@Document,@Inherited用来修饰注解。
具体参考
二、数据源配置
spring:
datasource:
master:
jdbc-url: jdbc:mysql://192.168.102.31:3306/test
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
slave1:
jdbc-url: jdbc:mysql://192.168.102.56:3306/test
username: pig # 只读账户
password: 123456
driver-class-name: com.mysql.j
dbc.Driver
参考另一种实现方式
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSourceSign {
/**
* 切换数据源名称
*/
DbType value() default DbType.MASTER;
}
public class DynamicDbContextHolder {
public enum DbType {
MASTER, SLAVE, OTHER
}
/**
* 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
* 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
*/
private static final ThreadLocal contextHolder = new ThreadLocal();
/**
* 设置数据源变量
* @param dbType
*/
public static void setDbType(DbType dbType) {
if (dbType == null) {
throw new NullPointerException();
}
contextHolder.set(dbType);
}
/**
* 获取数据源变量
* @return
*/
public static DbType getDbType() {
return contextHolder.get() == null ? DbType.MASTER : contextHolder.get();
}
/**
* 清空数据源变量
*/
public static void clearDbType() {
contextHolder.remove();
}
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSourceSign {
DbTypeEnum value() default DbTypeEnum.MASTER;
}
/**
* 问题:自定义数据源注解与@Transaction同时使用时不生效
* 自定义数据源注解与@Transaction注解同一个方法,会先执行@Transaction注解,即获取数据源在切换数据源之前,所以会导致自定义注解失效。
* 解决方法:定义切换数据源的注解的AOP切面(DynamicDataSourceAspect )上添加注解【@Order(-1),ordel的value越小,就越先执行】,
* 保证该AOP在@Transactional之前执行
*/
@Aspect
@Order(-1)
@Component
public class DataSourceAspect {
@Pointcut("@annotation(com.jht.jsicp.common.dynamicdb.annotation.DataSourceSign)")
public void dsPointCut() {
}
@Around("dsPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
DataSourceSign dataSource = method.getAnnotation(DataSourceSign.class);
if (dataSource != null) {
DynamicDbContextHolder.setDbType(dataSource.value());
}
try {
return point.proceed();
} finally {
// 销毁数据源 在执行方法之后
DynamicDbContextHolder.clearDbType();
}
}
}
——————————————————————————————————另一种写法———————————————————————————————————————————————
@Aspect
@Component
public class DynamicDbAspect implements PriorityOrdered {
public static final Logger logger = LoggerFactory.getLogger(DynamicDbAspect.class);
/**
* 切换到master主库
* @param proceedingJoinPoint
* @param Page
* @return
* @throws Throwable
*/
@Around("@annotation(master)")
public Object proceed(ProceedingJoinPoint proceedingJoinPoint, Master master) throws Throwable {
try {
//logger.info("set database connection to master only");
DynamicDbContextHolder.setDbType(DynamicDbContextHolder.DbType.MASTER);
Object result = proceedingJoinPoint.proceed();
return result;
} finally {
DynamicDbContextHolder.clearDbType();
//logger.info("restore master database connection");
}
}/**
* 切换到slave从库
* @param proceedingJoinPoint
* @param Slave
* @return
* @throws Throwable
*/
@Around("@annotation(slave)")
public Object proceed(ProceedingJoinPoint proceedingJoinPoint, Slave slave) throws Throwable {
try {
//logger.info("set database connection to slave only");
DynamicDbContextHolder.setDbType(DynamicDbContextHolder.DbType.SLAVE);
Object result = proceedingJoinPoint.proceed();
return result;
} finally {
DynamicDbContextHolder.clearDbType();
//logger.info("restore slave database connection");
}
}
@Override
public int getOrder() {
return 1;
}
}
Spring boot提供了AbstractRoutingDataSource 根据用户定义的规则选择当前的数据源,这样我们可以在执行查询之前,设置使用的数据源。实现可动态路由的数据源,在每次数据库查询操作前执行。它的抽象方法 determineCurrentLookupKey() 决定使用哪个数据源。
public class DynamicRoutingDataSource extends AbstractRoutingDataSource {
public static final Logger logger = LoggerFactory.getLogger(DynamicRoutingDataSource.class);
@Override
protected Object determineCurrentLookupKey() {
return DynamicDbContextHolder.getDbType();
}
}
//参考
@Configuration
@EnableTransactionManagement
public class DataSourceConfiguration {
public static final Logger logger = LoggerFactory.getLogger(DataSourceConfiguration.class);
@Value("${druid.type}") #druid.type=com.alibaba.druid.pool.DruidDataSource
private Class extends DataSource> dataSourceType;
#mybatis配置
#mybatis.mapper-locations=classpath*:mapper/*.xml
@Value("${mybatis.mapper-locations}")
private String mapperLocations;
#mybatis.configLocation=classpath:mybatis-config.xml
@Value("${mybatis.configLocation}")
private String configLocation;
#mybatis.type-aliases-package=com.jht.jscc
@Value("${mybatis.type-aliases-package}")
private String entityPackage;
/**
* 主数据库
* @return
*/
@Primary
@Bean(name = "masterDataSource")
@ConfigurationProperties(prefix = "druid.jsicp.master")
public DataSource masterDataSource() {
return DataSourceBuilder.create().type(dataSourceType).build();
}
/**
* 从数据库(如有多个按这种方式增加)
* @return
*/
@Bean(name = "slaveDataSource")
@ConfigurationProperties(prefix = "druid.jsicp.slave")
public DataSource slaveDataSource() {
return DataSourceBuilder.create().type(dataSourceType).build();
}
/**
* 从数据库(如有多个按这种方式增加)
* @return
*/
@Bean(name = "otherDataSource")
@ConfigurationProperties(prefix = "druid.jsicp.other")
public DataSource otherDataSource() {
return DataSourceBuilder.create().type(dataSourceType).build();
}
@Bean(name = "dataSource")
public AbstractRoutingDataSource dataSource(@Qualifier("masterDataSource") DataSource masterDataSource ,
@Qualifier("slaveDataSource") DataSource slaveDataSource,
@Qualifier("otherDataSource") DataSource otherDataSource) {
DynamicRoutingDataSource proxy = new DynamicRoutingDataSource();
Map
@EnableTransactionManagement
@Configuration
public class MyBatisConfig {
@Resource(name = "myRoutingDataSource")
private DataSource myRoutingDataSource;
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(myRoutingDataSource);
sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
return sqlSessionFactoryBean.getObject();
}
@Bean
public PlatformTransactionManager platformTransactionManager() {
return new DataSourceTransactionManager(myRoutingDataSource);
}
}