序言:这个读写分离摘自于Springboot多数据源快速启动器,读写分离的话并不是按照传统方式的springmvc看方法命名那样,而是在每个方法上加注解或者类上添加注解表明选用的数据源。支持事物!
1、创建Springboot项目
勾选Web,Mysql,MyBatis三个模块,最终创建springboot项目成功
2、pom.xml 添加属性依赖
<!-- springboot-aop包,AOP切面注解,Aspectd等相关注解 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency>
3、添加application.properties 属性配置
server.port=8088 #数据源1 spring.datasource.db1.jdbc-url=jdbc:mysql://127.0.0.1:3306/test_msg2?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.jdbc-url=jdbc:mysql://127.0.0.1:3306/test_msg3?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.springboot2.mjt04.dao
4、编写代码
4.0 实体类
public class User { private int id; private String name; private int age; public User(){} public User(String name, int age){ this.name = name; this.age = age; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
4.1 创建UserDao.xml
<?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.springboot2.mjt04.dao.UserDao"> <!-- 查询所有user --> <select id="getAllUser" resultType="java.util.Map"> select * from user </select> <!-- 添加数据并返回主键ID,接收主键,必须以实体类接收 --> <!-- keyColumn="id"对应数据库字段,keyProperty="id"对应实体类属性 --> <insert id="addUserGetID" parameterType="com.springboot2.mjt04.entity.User" useGeneratedKeys="true" keyColumn="id" keyProperty="id"> insert into user(name,age) values(#{name},#{age}) </insert> <insert id="addUser"> insert into user(name,age) values(#{name},#{age}) </insert> </mapper>
4.2 UserDao
@Mapper public interface UserDao { //使用xml配置形式查询 List<Map> getAllUser(); Long addUserGetID(User user); void addUser(User user); }
4.3 UserService
@Service public class UserService { @Autowired private UserDao 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(User user){ moredataDao.addUser(user); } //使用数据源2插入数据 @DS("datasource2") public void addUser2(User user){ moredataDao.addUser(user); } @Transactional public void test1(){ moredataDao.addUserGetID(new User("mdx1",18)); int a =10/0; moredataDao.addUserGetID(new User("mdxl1",20)); } }
4.4 创建controller
@Controller public class RWController { @Autowired UserService userService; //走数据源1库 @GetMapping(value = "/test1") @ResponseBody public String testOne(){ User user = new User("mjt01",20); userService.addUser1(user); return "success"; } //使用数据源2插入数据 @GetMapping(value = "/test2") @ResponseBody public String testTwo(){ User user = new User("mjt02",20); userService.addUser2(user); return "success"; } }
4.5 添加mysql多数据源配置文件类
5个配置文件,切记如果照着写的话,小心里面的路径要准确对应上
//import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.jdbc.DataSourceBuilder; 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()); } }
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(); } }
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface DS { String value() default "datasource1"; }
public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { System.out.println("数据源为"+DataSourceContextHolder.getDB()); return DataSourceContextHolder.getDB(); } }
@Aspect @Component public class DynamicDataSourceAspect { @Before("@annotation(com.springboot2.mjt04.config.mysql.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(com.springboot2.mjt04.config.mysql.DS)") public void afterSwitchDS(JoinPoint point){ DataSourceContextHolder.clearDB(); } }
最终目录结构图:
5、注意的问题:
5.1 出现这个错误 jdbcUrl is required with driverClassName.运行出现这个过程是因为在多数据源的情况下,springboot2.0与1.X版本都不同配置属性,解决jdbcUrl is required with driverClassName.问题,点击这里,但是我现在已经改过来了,以前是
spring.datasource.db1.url=jdbc:mysql://127.0.0.1:3306/test_msg2?useUnicode=true&characterEncoding=utf-8&useSSL=false
而现在添加了jdbc-url
spring.datasource.db1.jdbc-url=jdbc:mysql://127.0.0.1:3306/test_msg2?useUnicode=true&characterEncoding=utf-8&useSSL=false
5.2 包名路径跟换
以前1.X版本为
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
而现在2.X版本
import org.springframework.boot.jdbc.DataSourceBuilder;
注意一下就好,因为我都给大家改好了。
5.3 事物可以正常使用,也支持读写分离,不过在事物的方法可能比springmvc的项目麻烦些,毕竟目前我的技术只能用注解才能切换数据源,目前我没有成功找到在配置类根据方法前缀名就可以判断数据源的。
5.4
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
这个是application启动类上面的一个注解,表明是不使用DataSourceAutoCOnfiguration.class,因为我们的是多个数据源,不能默认使用一个单数据源,所以要加上这个。
成功!不懂可以私信我或者留言就好。