当前位置: 代码迷 >> 综合 >> 基于springboot 2.2.2+sharding+mybatis实现读写分离
  详细解决方案

基于springboot 2.2.2+sharding+mybatis实现读写分离

热度:90   发布时间:2023-12-25 23:30:14.0

在上一篇文章,我们做了mysql的读写分离。本篇文章从代码层实现读写分离。
一、背景
  当我们的项目所使用的数据库查询的访问量,访问频率,及访问的并发量远大于修改的时候,我们需要将访问数据库的方式读写分离。比如我们的业务系统中有大量的数据查询、报表等业务时。同时也为我们后面做多主多从、分库分表打下基础。
  在数据库层面,我们实现了一主两从的架构,进行读写分离。在业务服务和数据库之间这里选择使用sharding-jdbc来实现,至于sharding-jdbc和mycat优缺点选择,在这里就不做过多的描述,下面对sharding-jdbc做个简单的介绍。
  Sharding-JDBC是的分布式数据库中间件解决方案。Sharding-JDBC定位于轻量级的Java框架,它使用客户端直连数据库,可理解为增强版的JDBC驱动,完全兼容JDBC和各种ORM框架。
  1. 适用于任何基于Java的ORM框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template或直接使用JDBC。
  2. 基于任何第三方的数据库连接池,如:DBCP, C3P0, BoneCP, Druid, HikariCP等。
  3. 支持任意实现JDBC规范的数据库。目前支持MySQL,Oracle,SQLServer和PostgreSQL。
应用架构图如下:
在这里插入图片描述

二、java代码
2.1 技术栈
  代码技术栈使用:spring boot 2.2.2、mybatis2.1.3、druid1.1.10、sharding4.1.0、mysql5.7、idgenerator1.4.2进行读写分离实现。
2.2 工程结构
在这里插入图片描述
2.3 pom文件依赖包

<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>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></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><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.1.10</version></dependency><dependency><groupId>org.apache.shardingsphere</groupId><artifactId>sharding-jdbc-spring-boot-starter</artifactId><version>4.1.0</version></dependency><dependency><groupId>com.dangdang</groupId><artifactId>sharding-jdbc-self-id-generator</artifactId><version>1.4.2</version></dependency></dependencies>

2.4 application.properties文件配置

#服务端口
server.port=8081
#tomcat编码
server.tomcat.uri-encoding=UTF-8
#数据源名称,多数据源以逗号分隔
spring.shardingsphere.datasource.names=master,slave0,slave1
#数据库连接池类名称
spring.shardingsphere.datasource.master.type=com.alibaba.druid.pool.DruidDataSource
#数据库驱动类名
spring.shardingsphere.datasource.master.driver-class-name=com.mysql.cj.jdbc.Driver
#数据库URL连接
spring.shardingsphere.datasource.master.url=jdbc:mysql://192.168.145.183:3306/order?useUnicode=true&characterEncoding=utf8&tinyInt1isBit=false&useSSL=false&serverTimezone=GMT
#数据库用户名
spring.shardingsphere.datasource.master.username=root
#数据库密码
spring.shardingsphere.datasource.master.password=123456
spring.shardingsphere.datasource.slave0.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.slave0.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.slave0.url=jdbc:mysql://192.168.145.184:3306/order?useUnicode=true&characterEncoding=utf8&tinyInt1isBit=false&useSSL=false&serverTimezone=GMT
spring.shardingsphere.datasource.slave0.username=root
spring.shardingsphere.datasource.slave0.password=123456
spring.shardingsphere.datasource.slave1.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.slave1.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.slave1.url=jdbc:mysql://192.168.145.185:3306/order?useUnicode=true&characterEncoding=utf8&tinyInt1isBit=false&useSSL=false&serverTimezone=GMT
spring.shardingsphere.datasource.slave1.username=root
spring.shardingsphere.datasource.slave1.password=123456
#读写分离数据源名称
spring.shardingsphere.masterslave.name=ms
#主数据源名称
spring.shardingsphere.masterslave.master-data-source-name=master
#从数据源名称,多个从数据源用逗号分隔
spring.shardingsphere.masterslave.slave-data-source-names=slave0,slave1
#负载均衡算法名称
spring.shardingsphere.masterslave.load-balance-algorithm-type=round_robin
#开启SQL显示
spring.shardingsphere.props.sql.show=true
#mybatis配置别名扫描
mybatis.type-aliases-package=com.general.**.entity
#mybatis配置mapper的xml位置
mybatis.mapper-locations=classpath:mybatis/mapper/**/*.xml
logging.level.root=info

关于官网上的配置,好像有一定的出入,使用官网的配置不太容易让人理解,所以这里使用的是根据4.1版本json的解析配置。解决这个配置文件以后,下面的代码和我们日常写的业务代码以及使用方面没有什么区别。
2.5 启动类

@MapperScan("com.general.**.mapper")
@ComponentScan(value = "com.general")
@SpringBootApplication(exclude = DruidDataSourceAutoConfigure.class)
public class ProductionOrdersApplication {
    public static void main(String[] args) {
    SpringApplication.run(ProductionOrdersApplication.class, args);}/*** 创建分布式ID对象* @return 分布式ID对象*/@Beanpublic IdGenerator getIdGenerator() {
    return new CommonSelfIdGenerator();}
}

2.6 请求控制

@RestController
@RequestMapping("/proorderweb")
public class ProOrderWeb {
    @Autowiredprivate ProOrderService proOrderService;@Autowiredprivate IdGenerator idGenerator;/*** 查询所有生产订单* @return 所有生产订单*/@RequestMapping("/query")public Object query() {
    return proOrderService.findByAll();}/*** 保存订单* @return*/@RequestMapping("/insert")public Object insert() {
    ProOrder proOrder = new ProOrder();//这里使用sharding的一个分布式ID组件,底层是使用snowflake算法实现。proOrder.setId(idGenerator.generateId().longValue());proOrder.setProOrderCode("测试编码" + proOrder.getId());proOrder.setProOrderName("测试名称" + proOrder.getId());proOrderService.insert(proOrder);return "数据保存成功!";}
}

2.7 业务接口

public interface ProOrderService {
    /*** 查询所有生产订单* @return 生产订单集合*/public List<ProOrder> findByAll();/*** 保存生产订单* @param proOrder*/public void insert(ProOrder proOrder);/*** 更新生产订单* @param proOrder*/public void update(ProOrder proOrder);/*** 删除生产订单* @param proOrder*/public void delete(ProOrder proOrder);}

2.8 业务接口实现

@Service
public class ProOrderServiceImpl implements ProOrderService {
    @Autowiredprivate ProOrderMapper proOrderMapper;/*** 查询所有生产订单* @return 生产订单集合*/@Overridepublic List<ProOrder> findByAll() {
    return proOrderMapper.findByAll();}/*** 保存生产订单* @param proOrder*/@Overridepublic void insert(ProOrder proOrder) {
    proOrderMapper.insert(proOrder);}/*** 更新生产订单* @param proOrder*/@Overridepublic void update(ProOrder proOrder) {
    proOrderMapper.update(proOrder);}/*** 删除生产订单* @param proOrder*/@Overridepublic void delete(ProOrder proOrder) {
    proOrderMapper.delete(proOrder);}
}

2.9 DAO接口

@Repository
public interface ProOrderMapper {
    /*** 查询所有生产订单* @return*/public List<ProOrder> findByAll();/*** 保存生产订单* @param proOrder 生产订单*/public void insert(ProOrder proOrder);/*** 更新生产订单* @param proOrder 生产订单*/public void update(ProOrder proOrder);/*** 删除生产订单* @param proOrder 生产订单*/public void delete(ProOrder proOrder);
}

2.10 实体类

public class ProOrder implements Serializable {
    /****/private static final long serialVersionUID = 1L;private Long id;private String proOrderCode;private String proOrderName;public Long getId() {
    return id;}public void setId(Long id) {
    this.id = id;}public String getProOrderCode() {
    return proOrderCode;}public void setProOrderCode(String proOrderCode) {
    this.proOrderCode = proOrderCode;}public String getProOrderName() {
    return proOrderName;}public void setProOrderName(String proOrderName) {
    this.proOrderName = proOrderName;}
}

2.11 ProOrderMapper.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.general.proorder.mapper.ProOrderMapper" ><resultMap id="BaseResultMap" type="com.general.proorder.entity.ProOrder"><id column="id" property="id" /><result column="pro_order_code"  property="proOrderCode" /><result column="pro_order_name"  property="proOrderName" /></resultMap>	<select id="findByAll" resultMap="BaseResultMap">select * from pro_order</select> <insert id="insert" parameterType="com.general.proorder.entity.ProOrder">insert into pro_order(id, pro_order_code, pro_order_name) values(#{id}, #{proOrderCode}, #{proOrderName})</insert>  <update id="update" parameterType="com.general.proorder.entity.ProOrder">update pro_order set pro_order_code = #{proOrderCode}, pro_order_name = #{proOrderName}where id = #{id};</update>   <delete id="delete" parameterType="com.general.proorder.entity.ProOrder">delete from pro_order where id = #{id};</delete>  
</mapper>

**2.12 测试

  1. 查询测试**
      启动工程以后,在浏览器里输入http://127.0.0.1:8081/proorderweb/query,在前台多刷新两次。
    得到结果如下图:
    在这里插入图片描述
    在后台我们看到访问不同的slave节点进行查询,如下图:
    在这里插入图片描述
      从上图结果能看出来已经在两个slave节点上进行轮询查询。
    2. 写入操作
      启动工程以后,在浏览器里输入http://127.0.0.1:8081/proorderweb/query,在前台多刷新两次。
    得到结果如下图:
    在这里插入图片描述
      在后台我们看到访问的是master节点,如下图:
    在这里插入图片描述
      从上图结果能看出来,写入时通过master节点进行的。到此整个读写分离已经全部实现。
  相关解决方案