AbstractRoutingDataSource动态路由方式:
利用AbstractRoutingDataSource实现动态切换数据源的功能
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <java.version>1.8</java.version> </properties> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.2.RELEASE</version> <relativePath/> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.5</version> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.0</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.13</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.7.3</version> </dependency> </dependencies> </project>
配置数据源
application.properties
#主数据源 spring.datasource.primary.url=jdbc:mysql://localhost:3306/test spring.datasource.primary.username=test spring.datasource.primary.password=test spring.datasource.primary.driver-class-name=com.mysql.jdbc.Driver #second数据源 spring.datasource.secondary.url=jdbc:mysql://localhost:3306/test2 spring.datasource.secondary.username=test2 spring.datasource.secondary.password=test2 spring.datasource.secondary.driver-class-name=com.mysql.jdbc.Driver # MyBatis mybatis.type-aliases-package=com.durid.model mybatis.mapper-locations=classpath:mappers/*.xml
@Primary注解在哪个ds,默认使用那个ds
package com.durid.config; import com.durid.dds.DRoutingDataSource; import org.mybatis.spring.SqlSessionFactoryBean; import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.PlatformTransactionManager; import javax.sql.DataSource; import java.util.HashMap; import java.util.Map; @Configuration public class DataSourceConfig { /** * @primary 设置在哪个ds,就优先读取那个ds * * @return */ @Bean(name = "primary") @Primary @ConfigurationProperties(prefix="spring.datasource.primary") public DataSource primaryDataSource() { return DataSourceBuilder.create().build(); } @Bean(name = "secondary") @ConfigurationProperties(prefix="spring.datasource.secondary") public DataSource secondaryDataSource() { return DataSourceBuilder.create().build(); } /** * 动态数据源设置 * @return */ @Bean(name = "ddataSource") public DataSource ddataSource(){ DRoutingDataSource dRoutingDataSource = new DRoutingDataSource(); //设置默认的数据源 dRoutingDataSource.setDefaultTargetDataSource(primaryDataSource()); //配置多数据源 Map<Object, Object> ddsMap = new HashMap<>(2); ddsMap.put("primary", primaryDataSource()); ddsMap.put("secondary", secondaryDataSource()); dRoutingDataSource.setTargetDataSources(ddsMap); return dRoutingDataSource; } /** * mybatis前缀必需 * 参考application.properties的配置 * 用来映射xml和model * mybatis.type-aliases-package * mybatis.mapper-locations * * @return */ @Bean @ConfigurationProperties(prefix = "mybatis") public SqlSessionFactoryBean sqlSessionFactoryBean() { SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(ddataSource()); return sqlSessionFactoryBean; } @Bean public PlatformTransactionManager transactionManager(){ return new DataSourceTransactionManager(ddataSource()); } }
继承AbstractRoutingDataSource,配合ThreadLocal存储数据源key
package com.durid.dds; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; public class DRoutingDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DdsHolder.getDS(); } }
package com.durid.dds; public class DdsHolder { private static final ThreadLocal<String> HOLDER = new ThreadLocal<>(); public static void setDS(String ddsName) { if (ddsName == null){ throw new RuntimeException("set ddsMode failed"); } HOLDER.set(ddsName); } public static void release(){ HOLDER.remove(); } public static String getDS(){ return HOLDER.get(); } }
通过注解和切面的方式实现数据源的动态切换:
package com.durid.annotation; import java.lang.annotation.*; @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface TargetDataSource { String value(); }
package com.durid.aop; import com.durid.annotation.TargetDataSource; import com.durid.dds.DdsHolder; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; @Aspect @Component public class DdsAspect { @Before("@annotation(targetDataSource))") public void switchDataSource(TargetDataSource targetDataSource) { DdsHolder.setDS(targetDataSource.value()); } @After("@annotation(targetDataSource))") public void restoreDataSource(TargetDataSource targetDataSource) { DdsHolder.release(); } }
model类:
package com.durid.model; import lombok.Data; /** * Created by hao.g on 18/3/12. */ @Data public class User { private Long id; private String name; private int age; }mapper类:
package com.durid.mapper; import com.durid.model.User; import org.apache.ibatis.annotations.Mapper; @Mapper public interface UserMapper { User selectById(long id); }
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace="com.durid.mapper.UserMapper"> <resultMap id="baseResultMap" type="com.durid.model.User"> <id column="id" property="id" javaType="java.lang.Long" jdbcType="BIGINT" /> <result column="name" property="name" javaType="java.lang.String" jdbcType="VARCHAR" /> <result column="age" property="age" javaType="java.lang.Integer" jdbcType="BIGINT" /> </resultMap> <select id="selectById" resultMap="baseResultMap" parameterType="java.lang.Long"> SELECT * FROM user WHERE id = #{id} </select> </mapper>Service类,可以使用注解在目标service方法完成数据源切换:
package com.durid.service; import com.durid.annotation.TargetDataSource; import com.durid.mapper.UserMapper; import com.durid.model.User; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class UserService { @Autowired private UserMapper userMapper; /** * 动态切换数据源 * @param id * @return */ // @TargetDataSource("primary") @TargetDataSource("secondary") public User selectById(long id){ return userMapper.selectById(id); } }测试类:
package com.test; import com.durid.App; import com.durid.aop.DdsAspect; import com.durid.service.UserService; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest(classes = App.class) public class AppTests { @Autowired private UserService userService; @Test public void test(){ System.out.println(userService.selectById(1).getName()); } }