springboot-mybatisplus-druid主从多数据源配置

tech2023-02-02  114

主从架构

我们一般在应对高负载的情况,会将数据库设计为主从架构,主(master)负责完成负载比较小的数据修改,从(salve)负责完成负责比较大的数据查询。

dependencies

<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.3</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.0</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.1</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> </dependencies>

springboot mybatis-plus配置主从

mybatis-plus优势是可以实现像jpa一样完全的零xml开发dao层,并且这个框架只做增强,不影响mybatis原有的任何特性以及功能。

springboot yml文件配置 spring: datasource: slave: url: jdbc:mysql://XXXXX:3306/master?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver master: url: jdbc:mysql://XXXXX:3306/master?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver 动态DataSource配置 package com.xupp.springbootmybatis.config; import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder; import com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties; import com.baomidou.mybatisplus.core.MybatisConfiguration; import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor; import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean; import org.apache.ibatis.mapping.DatabaseIdProvider; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.annotation.MapperScan; import org.mybatis.spring.boot.autoconfigure.MybatisProperties; import org.omg.PortableInterceptor.Interceptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; 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.context.annotation.Profile; import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.ResourceLoader; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.util.StringUtils; import javax.annotation.Resource; import javax.sql.DataSource; import java.util.HashMap; import java.util.Map; @Configuration public class MyDataSource { // 主库数据源 @Bean @ConfigurationProperties("spring.datasource.master") public DataSource masterDataSource() { return DruidDataSourceBuilder.create().build(); } // 从库数据源 @Bean @ConfigurationProperties("spring.datasource.slave") public DataSource slaveDataSource() { return DruidDataSourceBuilder.create().build(); } // 数据源路由 @Bean public DataSource dynamicDatasource() { Map<Object, Object> dataSourceMap = new HashMap<>(); dataSourceMap.put(MultipleDataSourceHelper.MASTER, masterDataSource()); dataSourceMap.put(MultipleDataSourceHelper.SLAVE, slaveDataSource()); DynamicDataSource dds = new DynamicDataSource(); dds.setTargetDataSources(dataSourceMap); dds.setDefaultTargetDataSource(masterDataSource()); return dds; } } 配置多数据源的选择器 public class MultipleDataSourceHelper { public static final String MASTER = "master"; public static final String SLAVE = "slave"; private static ThreadLocal<String> contextHolder = new ThreadLocal<>(); public static void set(String db) { contextHolder.set(db); } public static String get() { return contextHolder.get(); } } mybatis配置sqlsessionfactor package com.xupp.springbootmybatis.config; import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.annotation.MapperScan; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import javax.sql.DataSource; @Configuration public class MybatisConfig { @Autowired private DataSource dynamicDatasource; @Bean public SqlSessionFactory sqlSessionFactory() throws Exception { //注意这里必须使用mybatis-plus提供的sessionfactor否则,mybatis-plus无法生效 MybatisSqlSessionFactoryBean sqlSessionFactoryBean = new MybatisSqlSessionFactoryBean(); // 给mybatis指定上面配置好的动态数据源 sqlSessionFactoryBean.setDataSource(dynamicDatasource); return sqlSessionFactoryBean.getObject(); } } 定义实体类 package com.xupp.springbootmybatis.entity; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; @Data @TableName("user") public class UserEntity { @TableId(value="id", type= IdType.AUTO) private Integer id; private String name; } 编写mapper public interface UserMapper extends BaseMapper<UserEntity> { } 编写service package com.xupp.springbootmybatis.service; import com.xupp.springbootmybatis.config.MultipleDataSourceHelper; import com.xupp.springbootmybatis.entity.UserEntity; import com.xupp.springbootmybatis.mapper.UserMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @Service @Transactional(propagation = Propagation.REQUIRED) public class UserService { @Autowired private UserMapper userMapper; public Object save(String name){ //选择的数据源 MultipleDataSourceHelper.set(MultipleDataSourceHelper.MASTER); UserEntity userEntity =new UserEntity(); userEntity.setName(name); int result=userMapper.insert(userEntity); return result; } //通过从节点进行数据的查询 public Object getAll(){ MultipleDataSourceHelper.set(MultipleDataSourceHelper.SLAVE); return userMapper.selectList(null); } } 编写controller package com.xupp.springbootmybatis.controller; import com.xupp.springbootmybatis.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/") public class UserController { @Autowired private UserService userService; //主节点进行更新 @PostMapping("/user") public Object save(String name){ return userService.save(name); } //从节点进行获取 @GetMapping("/all") public Object all(){ return userService.getAll(); } } 启动类 package com.xupp.springbootmybatis; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableScheduling; @SpringBootApplication //扫描mapper位置 @MapperScan(basePackages = "com.xupp.springbootmybatis.mapper") public class SpringbootMybatisApplication { public static void main(String[] args) { SpringApplication.run(SpringbootMybatisApplication.class, args); } }

实现的原理

这个方案主要是通过springboot2.0提供的AbstractRoutingDataSource进行实现的。 主要是通过determineCurrentLookupKey方法根,据用户定义的规则选择当前的数据源,这样我们可以在执行查询之前,设置使用的数据源。实现可动态路由的数据源,在每次数据库查询操作前执行。它的抽象方法 determineCurrentLookupKey() 决定使用哪个数据源。

确定当前的查找关键字。通常将其实现为检查线程绑定事务上下文。允许任意键。返回的键需要与由resolveSpecifiedLookupKey方法解析的存储的查找键类型匹配。

项目地址

https://github.com/ishaveanyone/springboot/tree/master/springboot-mybatis

最新回复(0)