SpringBoot + MyBatis Plus + ShardingJDBC 集成教程

127 阅读5分钟

SpringBoot + MyBatis Plus + ShardingJDBC 集成教程

1. 技术栈概述

  • SpringBoot:简化Spring应用开发的框架
  • MyBatis Plus:MyBatis的增强工具,提供了CRUD操作、代码生成等便捷功能
  • ShardingJDBC:Apache ShardingSphere的子项目,轻量级分布式数据库中间件,用于实现分库分表

2. 项目搭建

2.1 创建SpringBoot项目

可以通过Spring Initializr或IDE(如IntelliJ IDEA)创建SpringBoot项目。

2.2 添加依赖

pom.xml中添加所需依赖:

<dependencies>
    <!-- Spring Boot 核心依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- MyBatis Plus 依赖 -->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.4.3</version>
    </dependency>
    
    <!-- MyBatis Plus 代码生成器(可选) -->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-generator</artifactId>
        <version>3.4.1</version>
    </dependency>
    
    <!-- ShardingSphere-JDBC 依赖 -->
    <dependency>
        <groupId>org.apache.shardingsphere</groupId>
        <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
        <version>5.2.1</version>
    </dependency>
    
    <!-- 数据库驱动 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
    
    <!-- 数据库连接池 -->
    <dependency>
        <groupId>com.zaxxer</groupId>
        <artifactId>HikariCP</artifactId>
        <version>3.4.2</version>
    </dependency>
    
   <dependency>
     <groupId>org.yaml</groupId>
     <artifactId>snakeyaml</artifactId>
     <version>1.33</version>
   </dependency>

    <!-- Lombok 简化代码 -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.12</version>
        <optional>true</optional>
    </dependency>
    
    <!-- 代码生成器模板引擎(可选) -->
    <dependency>
        <groupId>org.freemarker</groupId>
        <artifactId>freemarker</artifactId>
    </dependency>
</dependencies>

3. 数据库准备

3.1 创建数据库和表

以用户表为例,我们将实现分库分表。创建两个数据库db0db1,每个数据库中创建两张表user_0user_1

db0数据库中的表:

CREATE TABLE `user_0` (
  `id` bigint NOT NULL,
  `name` varchar(255) DEFAULT NULL,
  `age` int DEFAULT NULL,
  `email` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

CREATE TABLE `user_1` (
  `id` bigint NOT NULL,
  `name` varchar(255) DEFAULT NULL,
  `age` int DEFAULT NULL,
  `email` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

db1数据库中的表结构与db0相同。

4. 配置ShardingJDBC

4.1 配置application.yml

application.yml中配置数据源和分片规则:

spring:
  shardingsphere:
    datasource:
      names: ds0,ds1  # 数据源名称列表
      ds0:  # 第一个数据源
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://localhost:3306/db0?serverTimezone=UTC
        username: root
        password: root
      ds1:  # 第二个数据源
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://localhost:3306/db1?serverTimezone=UTC
        username: root
        password: root
    rules:
      sharding:
        tables:
          user:  # 逻辑表名
            actual-data-nodes: ds$->{0..1}.user_$->{0..1}  # 实际数据节点
            database-strategy:  # 分库策略
              standard:
                sharding-column: id  # 分片键
                sharding-algorithm-name: database-inline  # 分库算法
            table-strategy:  # 分表策略
              standard:
                sharding-column: id  # 分片键
                sharding-algorithm-name: table-inline  # 分表算法
            key-generate-strategy:  # 主键生成策略
              column: id  # 主键列
              key-generator-name: snowflake  # 主键生成器
        sharding-algorithms:
          database-inline:  # 分库算法定义
            type: INLINE
            props:
              algorithm-expression: ds$->{id % 2}  # 分片表达式
          table-inline:  # 分表算法定义
            type: INLINE
            props:
              algorithm-expression: user_$->{id % 2}  # 分片表达式
        key-generators:
          snowflake:  # 主键生成器定义
            type: SNOWFLAKE
            props:
              worker-id: 123
    props:
      sql-show: true  # 显示SQL

mybatis-plus:
  mapper-locations: classpath*:mapper/**/*.xml
  type-aliases-package: com.example.entity
  configuration:
    map-underscore-to-camel-case: true  # 开启下划线转驼峰

5. 代码实现

5.1 实体类

package com.example.entity;

import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

@Data
@TableName("user")  // 逻辑表名
public class User {
    private Long id;
    private String name;
    private Integer age;
    private String email;
}

5.2 Mapper接口

package com.example.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.entity.User;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserMapper extends BaseMapper<User> {
}

5.3 Service层

package com.example.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.example.entity.User;

public interface UserService extends IService<User> {
}
package com.example.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.entity.User;
import com.example.mapper.UserMapper;
import com.example.service.UserService;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}

5.4 Controller层

package com.example.controller;

import com.example.entity.User;
import com.example.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/user")
public class UserController {
    
    @Autowired
    private UserService userService;
    
    // 添加用户
    @PostMapping
    public boolean save(@RequestBody User user) {
        return userService.save(user);
    }
    
    // 根据ID查询用户
    @GetMapping("/{id}")
    public User getById(@PathVariable Long id) {
        return userService.getById(id);
    }
    
    // 查询所有用户
    @GetMapping
    public List<User> list() {
        return userService.list();
    }
    
    // 更新用户
    @PutMapping
    public boolean update(@RequestBody User user) {
        return userService.updateById(user);
    }
    
    // 删除用户
    @DeleteMapping("/{id}")
    public boolean remove(@PathVariable Long id) {
        return userService.removeById(id);
    }
}

6. 启动类

package com.example;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan("com.example.mapper")  // 扫描Mapper接口
public class ShardingJdbcDemoApplication {
    
    public static void main(String[] args) {
        SpringApplication.run(ShardingJdbcDemoApplication.class, args);
    }
}

7. 分片策略详解

7.1 行表达式分片策略(INLINE)

这是最简单的分片策略,使用Groovy表达式。在配置中我们使用了:

  • 分库:ds$->{id % 2} - 根据id对2取模,决定数据存储在哪个数据库
  • 分表:user_$->{id % 2} - 根据id对2取模,决定数据存储在哪个表

7.2 其他分片策略

7.2.1 标准分片策略(Standard)

适合需要根据单一字段进行精准查询和范围查询的场景。

7.2.2 复合分片策略(Complex)

适合根据多个字段进行分片的场景。

7.2.3 Hint分片策略

不依赖SQL条件,通过编程方式指定分片键。

8. 自定义分片算法

如果内置算法不满足需求,可以实现自定义分片算法:

package com.example.config;

import org.apache.shardingsphere.sharding.api.sharding.standard.PreciseShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.standard.RangeShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.standard.StandardShardingAlgorithm;

import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Set;

public class CustomShardingAlgorithm implements StandardShardingAlgorithm<Long> {
    
    @Override
    public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Long> shardingValue) {
        Long value = shardingValue.getValue();
        for (String targetName : availableTargetNames) {
            if (targetName.endsWith(String.valueOf(value % 2))) {
                return targetName;
            }
        }
        throw new UnsupportedOperationException();
    }
    
    @Override
    public Collection<String> doSharding(Collection<String> availableTargetNames, RangeShardingValue<Long> shardingValue) {
        Set<String> result = new LinkedHashSet<>();
        // 实现范围查询的分片逻辑
        return result;
    }
    
    @Override
    public void init() {
        // 初始化逻辑
    }
    
    @Override
    public String getType() {
        return "CUSTOM";  // 算法类型名称
    }
}

在配置文件中引用自定义算法:

spring:
  shardingsphere:
    rules:
      sharding:
        sharding-algorithms:
          custom-algorithm:
            type: CLASS_BASED
            props:
              strategy: STANDARD
              algorithmClassName: com.example.config.CustomShardingAlgorithm

9. 测试

9.1 测试数据插入

使用Postman或其他工具发送POST请求:

POST http://localhost:8080/user
Content-Type: application/json

{
  "name": "张三",
  "age": 25,
  "email": "zhangsan@example.com"
}

9.2 测试数据查询

查询所有用户:

GET http://localhost:8080/user

根据ID查询用户:

GET http://localhost:8080/user/{id}

10. 常见问题与解决方案

10.1 数据不一致

问题:分片键选择不当导致数据分布不均匀

解决方案:选择离散度高的字段作为分片键,如用户ID、订单ID等

10.2 跨库查询性能问题

问题:未使用分片键的查询会导致全库扫描

解决方案

  • 尽量在查询中包含分片键
  • 合理设计表结构,减少跨库Join操作
  • 考虑使用绑定表

10.3 主键冲突

问题:不同分片可能产生重复的主键

解决方案:使用分布式ID生成器,如雪花算法(Snowflake)

11. 最佳实践

  1. 分片键选择:选择查询频繁、离散度高的字段
  2. 避免热点数据:确保数据均匀分布在各个分片
  3. 合理设置分片数量:避免过多分片导致的资源浪费
  4. 使用绑定表:相关联的表使用相同的分片键和分片策略
  5. 注意事务一致性:跨库事务需要特别处理
  6. 监控与调优:实时监控分片执行情况,及时优化

12. 总结

通过本文的教程,我们学习了如何在SpringBoot项目中集成MyBatis Plus和ShardingJDBC实现分库分表。ShardingJDBC作为一款轻量级的中间件,以jar包形式提供服务,无需额外部署,非常适合中小型项目使用。结合MyBatis Plus的便捷功能,可以大幅提高开发效率。

在实际项目中,需要根据业务需求选择合适的分片策略,并注意处理可能出现的问题,如数据一致性、跨库查询性能等。