springboot+mybatis多数据源配置,AOP注解动态切换数据源
应用场景:项目使用多数据源,并实现读写分离。
springboot默认加载application.properties或application.yml配置,配置规则已经定好且为单数据源,想要配置多数据源必须禁用默认加载,然后手动去配置多数据源,完整代码如下:
数据源配置:
application.properties
#springboot单据源配置 spring.datasource.url=jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false spring.datasource.username=root spring.datasource.password=root spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.max-idle=10 spring.datasource.max-wait=10000 spring.datasource.min-idle=5 spring.datasource.initial-size=5 #springboot多数据源配置 #数据源1 spring.datasource.db1.url=jdbc:mysql://127.0.0.1:3306/db1?useUnicode=true&characterEncoding=utf-8&useSSL=false spring.datasource.db1.username=root spring.datasource.db1.password=root spring.datasource.db1.driver-class-name=com.mysql.jdbc.Driver spring.datasource.db1.max-idle=10 spring.datasource.db1.max-wait=10000 spring.datasource.db1.min-idle=5 spring.datasource.db1.initial-size=5 #数据源2 spring.datasource.db2.url=jdbc:mysql://127.0.0.1:3306/db2?useUnicode=true&characterEncoding=utf-8&useSSL=false spring.datasource.db2.username=root spring.datasource.db2.password=root spring.datasource.db2.driver-class-name=com.mysql.jdbc.Driver spring.datasource.db2.max-idle=10 spring.datasource.db2.max-wait=10000 spring.datasource.db2.min-idle=5 spring.datasource.db2.initial-size=5 #mybatis mybatis.mapper-locations=classpath*:mapper/*.xml mybatis.type-aliases-package=com.springboot.dao
启动入口类
Application.java
package com.springboot; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.web.support.SpringBootServletInitializer; /** * springboot入口类,此类需要在所有用到的package上层 * exclude = {DataSourceAutoConfiguration.class} * 禁用springboot默认加载的application.properties单数据源配置 */ @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) public class Application { public static void main(String[] args) { SpringApplication.run(Application.class,args); } }
多数据源配置类
package com.springboot.config; 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; /** * 多数据源配置类 * Created by pure on 2018-05-06. */ @Configuration public class DataSourceConfig { //数据源1 @Bean(name = "datasource1") @ConfigurationProperties(prefix = "spring.datasource.db1") // application.properteis中对应属性的前缀 public DataSource dataSource1() { return DataSourceBuilder.create().build(); } //数据源2 @Bean(name = "datasource2") @ConfigurationProperties(prefix = "spring.datasource.db2") // application.properteis中对应属性的前缀 public DataSource dataSource2() { return DataSourceBuilder.create().build(); } /** * 动态数据源: 通过AOP在不同数据源之间动态切换 * @return */ @Primary @Bean(name = "dynamicDataSource") public DataSource dynamicDataSource() { DynamicDataSource dynamicDataSource = new DynamicDataSource(); // 默认数据源 dynamicDataSource.setDefaultTargetDataSource(dataSource1()); // 配置多数据源 Map<Object, Object> dsMap = new HashMap(); dsMap.put("datasource1", dataSource1()); dsMap.put("datasource2", dataSource2()); dynamicDataSource.setTargetDataSources(dsMap); return dynamicDataSource; } /** * 配置@Transactional注解事物 * @return */ @Bean public PlatformTransactionManager transactionManager() { return new DataSourceTransactionManager(dynamicDataSource()); } }
保存切换数据源:
package com.springboot.config; /** * Created by pure on 2018-05-06. */ public class DataSourceContextHolder { /** * 默认数据源 */ public static final String DEFAULT_DS = "datasource1"; private static final ThreadLocal<String> contextHolder = new ThreadLocal<>(); // 设置数据源名 public static void setDB(String dbType) { System.out.println("切换到{"+dbType+"}数据源"); contextHolder.set(dbType); } // 获取数据源名 public static String getDB() { return (contextHolder.get()); } // 清除数据源名 public static void clearDB() { contextHolder.remove(); } }
当前数据源:
package com.springboot.config; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; /** * Created by pure on 2018-05-06. */ public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { System.out.println("数据源为"+DataSourceContextHolder.getDB()); return DataSourceContextHolder.getDB(); } }
自定义注解
package com.springboot.config; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 自定义注解 */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface DS { String value() default "datasource1"; }
AOP动态切换
package com.springboot.config; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component; import java.lang.reflect.Method; /** * 自定义注解 + AOP的方式实现数据源动态切换。 * Created by pure on 2018-05-06. */ @Aspect @Component public class DynamicDataSourceAspect { @Before("@annotation(DS)") public void beforeSwitchDS(JoinPoint point){ //获得当前访问的class Class<?> className = point.getTarget().getClass(); //获得访问的方法名 String methodName = point.getSignature().getName(); //得到方法的参数的类型 Class[] argClass = ((MethodSignature)point.getSignature()).getParameterTypes(); String dataSource = DataSourceContextHolder.DEFAULT_DS; try { // 得到访问的方法对象 Method method = className.getMethod(methodName, argClass); // 判断是否存在@DS注解 if (method.isAnnotationPresent(DS.class)) { DS annotation = method.getAnnotation(DS.class); // 取出注解中的数据源名 dataSource = annotation.value(); } } catch (Exception e) { e.printStackTrace(); } // 切换数据源 DataSourceContextHolder.setDB(dataSource); } @After("@annotation(DS)") public void afterSwitchDS(JoinPoint point){ DataSourceContextHolder.clearDB(); } }
Mapper映射
<?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.springboot.dao.MoredataDao">
<!-- 查询所有user -->
<select id="getAllUser" resultType="java.util.Map">
select * from user
</select>
<!-- 添加数据并返回主键ID,接收主键,必须以实体类接收 -->
<!-- keyColumn="id"对应数据库字段,keyProperty="id"对应实体类属性 -->
<insert id="addUserGetID" parameterType="com.springboot.entity.User" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
insert into user(name,create_time) values(#{name},#{createTime})
</insert>
<insert id="addUser">
insert into user(name,create_time) values(#{name},sysdate())
</insert>
</mapper>
实体类
package com.springboot.entity; import java.util.Date; /** * user实体类 * Created by pure on 2018-05-06. */ public class User { private Long id; private String name; private Date createTime; public User(){} public User(String name,Date createTime){ this.name=name; this.createTime=createTime; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } }
dao层
package com.springboot.dao; import com.springboot.entity.User; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import java.util.List; import java.util.Map; /** * dao层 * Created by pure on 2018-05-06. */ @Mapper public interface MoredataDao { //使用xml配置形式查询 public List<Map> getAllUser(); public Long addUserGetID(User user); public void addUser(@Param("name") String name); }
service层
package com.springboot.service; import com.springboot.config.DS; import com.springboot.dao.MoredataDao; import com.springboot.entity.User; import org.apache.ibatis.annotations.Param; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; import java.util.Map; /** * service层 * Created by pure on 2018-05-06. */ @Service public class MoredataService { @Autowired private MoredataDao moredataDao; //使用数据源1查询 @DS("datasource1") public List<Map> getAllUser1(){ return moredataDao.getAllUser(); } //使用数据源2查询 @DS("datasource2") public List<Map> getAllUser2(){ return moredataDao.getAllUser(); } //使用数据源1插入数据 @DS("datasource1") public Long addUserGetID1(User user){ return moredataDao.addUserGetID(user); } //使用数据源1插入数据 @DS("datasource2") public Long addUserGetID2(User user){ return moredataDao.addUserGetID(user); } //使用数据源1插入数据 @DS("datasource1") public void addUser1(String name){ moredataDao.addUser(name); } //使用数据源2插入数据 @DS("datasource2") public void addUser2(String name){ moredataDao.addUser(name); } }
controller层
package com.springboot.controller; import com.springboot.entity.User; import com.springboot.service.MoredataService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Date; import java.util.List; import java.util.Map; /** * Created by pure on 2018-05-06. */ //@CrossOrigin @RestController @RequestMapping("/moredata") public class MoredataController { @Autowired private MoredataService moredataService; @RequestMapping(value = "/getDb1AllUser") public List<Map> getDb1AllUser() { List<Map> list = moredataService.getAllUser1(); return list; } @RequestMapping(value = "/getDb2AllUser") public List<Map> getDb2AllUser() { List<Map> list = moredataService.getAllUser2(); return list; } @RequestMapping(value = "/addDb1User") public String addDb1User(String name) { User user = new User(name,new Date()); Long rows = moredataService.addUserGetID1(user);//返回的是结果行数 return "{id:"+user.getId()+"}"; } @RequestMapping(value = "/addDb2User") public String addDb2User(String name) { User user = new User(name,new Date()); Long rows = moredataService.addUserGetID2(user); return "{id:"+user.getId()+"}"; } }
maven配置
<?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"> <modelVersion>4.0.0</modelVersion> <groupId>com.springboot</groupId> <artifactId>springboot3</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <!-- jdk1.8以上这里一定要配置上java的版本,如果是1.7版本的可不用配置 --> <java.version>1.8</java.version> </properties> <!-- springboot版本依赖 1.4.1.RELEASE--> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.8.RELEASE</version> <relativePath/> </parent> <dependencies> <!-- springboot核心包--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- springboot-aop包,AOP切面注解,Aspectd等相关注解 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <!-- springboot-mybatis --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.0</version> </dependency> <!-- jdbcTemple --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <!-- springboot测试模块包junit --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- 开发测试环境修改文件实时生效包,生产默认不使用 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency> <!-- springboot+freemarker整合包 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-freemarker</artifactId> </dependency> <!-- mysql数据库连接包 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <source>1.7</source> <target>1.7</target> </configuration> </plugin> </plugins> </build> </project>
项目结构:

运行结果:
使用数据源1

使用数据源2

数据库:


浙公网安备 33010602011771号