Mybatis-plus 分表

一、简介

MybatisPlus提供处理动态表名的接口TableNameHandler,可以通过这个接口获取当前执行的SQL和数据库表名

  • TableNameHandler 接口源码
public interface TableNameHandler {

    /**
     * 生成动态表名
     *
     * @param sql       当前执行 SQL
     * @param tableName 表名
     * @return String
     */
    String dynamicTableName(String sql, String tableName);
}

二、分表demo

场景:用户每次登录,都要记录下来,分表数量为10个

  • SQL脚本
CREATE TABLE user_login_record_0 (
       `id` BIGINT ( 20 ) NOT NULL AUTO_INCREMENT COMMENT '主键id',
       `user_id` BIGINT ( 20 ) NOT NULL COMMENT '商品id',
       `login_type` INT ( 11 ) NOT NULL COMMENT '登录方式:0:手机号码登录,1:微信登录,2:一键登录',
       `login_time` datetime ( 3 ) DEFAULT CURRENT_TIMESTAMP ( 3 ) COMMENT '登录时间',
       PRIMARY KEY ( `id` )
) ENGINE = INNODB COMMENT '用户登录记录表_0';

CREATE TABLE user_login_record_1 (
       `id` BIGINT ( 20 ) NOT NULL AUTO_INCREMENT COMMENT '主键id',
       `user_id` BIGINT ( 20 ) NOT NULL COMMENT '商品id',
       `login_type` INT ( 11 ) NOT NULL COMMENT '登录方式:0:手机号码登录,1:微信登录,2:一键登录',
       `login_time` datetime ( 3 ) DEFAULT CURRENT_TIMESTAMP ( 3 ) COMMENT '登录时间',
       PRIMARY KEY ( `id` )
) ENGINE = INNODB COMMENT '用户登录记录表_1';

CREATE TABLE user_login_record_2 (
       `id` BIGINT ( 20 ) NOT NULL AUTO_INCREMENT COMMENT '主键id',
       `user_id` BIGINT ( 20 ) NOT NULL COMMENT '商品id',
       `login_type` INT ( 11 ) NOT NULL COMMENT '登录方式:0:手机号码登录,1:微信登录,2:一键登录',
       `login_time` datetime ( 3 ) DEFAULT CURRENT_TIMESTAMP ( 3 ) COMMENT '登录时间',
       PRIMARY KEY ( `id` )
) ENGINE = INNODB COMMENT '用户登录记录表_2';

CREATE TABLE user_login_record_3 (
       `id` BIGINT ( 20 ) NOT NULL AUTO_INCREMENT COMMENT '主键id',
       `user_id` BIGINT ( 20 ) NOT NULL COMMENT '商品id',
       `login_type` INT ( 11 ) NOT NULL COMMENT '登录方式:0:手机号码登录,1:微信登录,2:一键登录',
       `login_time` datetime ( 3 ) DEFAULT CURRENT_TIMESTAMP ( 3 ) COMMENT '登录时间',
       PRIMARY KEY ( `id` )
) ENGINE = INNODB COMMENT '用户登录记录表_3';

CREATE TABLE user_login_record_4 (
       `id` BIGINT ( 20 ) NOT NULL AUTO_INCREMENT COMMENT '主键id',
       `user_id` BIGINT ( 20 ) NOT NULL COMMENT '商品id',
       `login_type` INT ( 11 ) NOT NULL COMMENT '登录方式:0:手机号码登录,1:微信登录,2:一键登录',
       `login_time` datetime ( 3 ) DEFAULT CURRENT_TIMESTAMP ( 3 ) COMMENT '登录时间',
       PRIMARY KEY ( `id` )
) ENGINE = INNODB COMMENT '用户登录记录表_4';

CREATE TABLE user_login_record_5 (
       `id` BIGINT ( 20 ) NOT NULL AUTO_INCREMENT COMMENT '主键id',
       `user_id` BIGINT ( 20 ) NOT NULL COMMENT '商品id',
       `login_type` INT ( 11 ) NOT NULL COMMENT '登录方式:0:手机号码登录,1:微信登录,2:一键登录',
       `login_time` datetime ( 3 ) DEFAULT CURRENT_TIMESTAMP ( 3 ) COMMENT '登录时间',
       PRIMARY KEY ( `id` )
) ENGINE = INNODB COMMENT '用户登录记录表_5';

CREATE TABLE user_login_record_6 (
       `id` BIGINT ( 20 ) NOT NULL AUTO_INCREMENT COMMENT '主键id',
       `user_id` BIGINT ( 20 ) NOT NULL COMMENT '商品id',
       `login_type` INT ( 11 ) NOT NULL COMMENT '登录方式:0:手机号码登录,1:微信登录,2:一键登录',
       `login_time` datetime ( 3 ) DEFAULT CURRENT_TIMESTAMP ( 3 ) COMMENT '登录时间',
       PRIMARY KEY ( `id` )
) ENGINE = INNODB COMMENT '用户登录记录表_6';

CREATE TABLE user_login_record_7 (
       `id` BIGINT ( 20 ) NOT NULL AUTO_INCREMENT COMMENT '主键id',
       `user_id` BIGINT ( 20 ) NOT NULL COMMENT '商品id',
       `login_type` INT ( 11 ) NOT NULL COMMENT '登录方式:0:手机号码登录,1:微信登录,2:一键登录',
       `login_time` datetime ( 3 ) DEFAULT CURRENT_TIMESTAMP ( 3 ) COMMENT '登录时间',
       PRIMARY KEY ( `id` )
) ENGINE = INNODB COMMENT '用户登录记录表_7';

CREATE TABLE user_login_record_8 (
       `id` BIGINT ( 20 ) NOT NULL AUTO_INCREMENT COMMENT '主键id',
       `user_id` BIGINT ( 20 ) NOT NULL COMMENT '商品id',
       `login_type` INT ( 11 ) NOT NULL COMMENT '登录方式:0:手机号码登录,1:微信登录,2:一键登录',
       `login_time` datetime ( 3 ) DEFAULT CURRENT_TIMESTAMP ( 3 ) COMMENT '登录时间',
       PRIMARY KEY ( `id` )
) ENGINE = INNODB COMMENT '用户登录记录表_8';

CREATE TABLE user_login_record_9 (
       `id` BIGINT ( 20 ) NOT NULL AUTO_INCREMENT COMMENT '主键id',
       `user_id` BIGINT ( 20 ) NOT NULL COMMENT '商品id',
       `login_type` INT ( 11 ) NOT NULL COMMENT '登录方式:0:手机号码登录,1:微信登录,2:一键登录',
       `login_time` datetime ( 3 ) DEFAULT CURRENT_TIMESTAMP ( 3 ) COMMENT '登录时间',
       PRIMARY KEY ( `id` )
) ENGINE = INNODB COMMENT '用户登录记录表_9';

# 设置id自增初始化值
ALTER TABLE user_login_record_0 AUTO_INCREMENT = 10;
ALTER TABLE user_login_record_1 AUTO_INCREMENT = 1;
ALTER TABLE user_login_record_2 AUTO_INCREMENT = 2;
ALTER TABLE user_login_record_3 AUTO_INCREMENT = 3;
ALTER TABLE user_login_record_4 AUTO_INCREMENT = 4;
ALTER TABLE user_login_record_5 AUTO_INCREMENT = 5;
ALTER TABLE user_login_record_6 AUTO_INCREMENT = 6;
ALTER TABLE user_login_record_7 AUTO_INCREMENT = 7;
ALTER TABLE user_login_record_8 AUTO_INCREMENT = 8;
ALTER TABLE user_login_record_9 AUTO_INCREMENT = 9;

-- 全局级别:设置id自增步长
SET GLOBAL auto_increment_increment=10;
  • yml配置
spring:
  application:
    name: springboot-mybatisplus
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
    username: root
    password: 123456

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    # 全局配置,开启缓存(一级缓存默认开启,二级缓存需手动开启)
    cache-enabled: true
  mapper-locations: classpath:/mapper/**/*.xml
  type-aliases-package: com.coolw.mybatisplus.entity

基于数据库ID自增,自定义动态表名处理器,实现接口TableNameHandler

package com.coolw.mybatisplus.config;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.handler.TableNameHandler;

import java.util.HashMap;
import java.util.Map;

/**
 * 自定义动态表名处理器,基于id的方式进行分表
 * <p>
 * TableNameHandler:MybatisPlus提供处理动态表名的接口,可以通过这个接口获取当前执行的SQL和数据库表名
 * <p>
 * 需要设置每个表的id自增初始值 及 id自增步长(mysql仅支持全局修改)
 * 分库分表一般需要用分布式ID生成(例如雪花算法),使用中间件shareding jdbc(推荐) 或者 mycat
 *
 * @author coolw
 * @version 1.0
 * @date 2023/9/22 8:59
 */
public class IdTableNameHandler implements TableNameHandler {

    /**
     * 哪些表可以使用这个动态表名规则
     * 表名 -> 分几张表
     */
    private static final Map<String, Integer> CONFIG_TABLE_INFO_MAP = new HashMap<>();

    static {
        CONFIG_TABLE_INFO_MAP.put("user_login_record", 10);
    }

    /**
     * 保存分表的id,使用ThreadLocal存储,避免多线程数据冲突
     */
    private static final ThreadLocal<Long> ID_DATA = new ThreadLocal<>();

    /**
     * 初始化当前分表的id
     */
    public static void initCurrentId(Long id) {
        ID_DATA.set(id);
    }

    /**
     * 获取当前分表的id
     */
    public static Long getCurrentId() {
        return ID_DATA.get();
    }

    /**
     * 分表使用结束后,手动进行remove清除分表的id,防止内存泄露
     */
    public static void removeCurrentId() {
        ID_DATA.remove();
    }

    /**
     * 获取动态表名
     */
    @Override
    public String dynamicTableName(String sql, String tableName) {
        // 判断当前表名是否在动态表名配置中
        if (StrUtil.isBlank(tableName) || !CONFIG_TABLE_INFO_MAP.containsKey(tableName)) {
            return tableName;
        }

        // 分表的个数
        int tableSize = CONFIG_TABLE_INFO_MAP.get(tableName);

        // 当前分表的id
        Long currentId = getCurrentId();

        // 当前id的数据存储在第几张表
        int tableIndex = (int) (currentId % tableSize);

        // 清理当前id
        removeCurrentId();

        return tableName + "_" + tableIndex;
    }
}

需要将自定义表名动态处理器,配置到mybatis-plus拦截器中

package com.coolw.mybatisplus.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.DynamicTableNameInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * mybatis-plus 配置
 *
 * @author coolw
 * @version 1.0
 * @date 2023/2/1 15:53
 */
@Configuration
public class MybatisPlusConfig {

    /**
     * Mybatis plus 拦截器
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 分页插件
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        // 动态表名
        interceptor.addInnerInterceptor(dynamicTableNameInnerInterceptor());
        return interceptor;
    }

    /**
     * 注册动态表名拦截器
     */
    private DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor() {
        DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor = new DynamicTableNameInnerInterceptor();

        dynamicTableNameInnerInterceptor.setTableNameHandler(new IdTableNameHandler());

        return dynamicTableNameInnerInterceptor;
    }
}
  • UserLoginRecord
package com.coolw.mybatisplus.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.coolw.common.api.BaseDomain;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.util.Date;

/**
 * 用户登录记录表
 *
 * @author coolw
 * @version 1.0
 * @date 2023/9/22 8:53
 */
@TableName("user_login_record")
@Getter
@Setter
@NoArgsConstructor
public class UserLoginRecord extends BaseDomain {
    private static final long serialVersionUID = -5994848718492490449L;

    @TableId(type = IdType.AUTO)
    private Long id;

    private Long userId;

    private Integer loginType;

    private Date loginTime;

    public UserLoginRecord(Long userId, Integer loginType) {
        this.userId = userId;
        this.loginType = loginType;
    }
}
  • UserService
package com.coolw.mybatisplus.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.coolw.mybatisplus.entity.UserEntity;

public interface UserService extends IService<UserEntity> {
}
  • UserServiceImpl
package com.coolw.mybatisplus.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.coolw.mybatisplus.dao.UserMapper;
import com.coolw.mybatisplus.entity.UserEntity;
import com.coolw.mybatisplus.service.UserService;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, UserEntity> implements UserService {
}
  • UserLoginRecordMapper
/**
 * 用户登录记录表Mapper
 *
 * @author coolw
 * @version 1.0
 * @date 2023/9/22 8:55
 */
public interface UserLoginRecordMapper extends BaseMapper<UserLoginRecord> {

    /**
     * 根据用户id获取登录记录
     *
     * @param userId 用户id
     * @return 登录记录
     */
    @Select("select * from user_login_record where user_id = #{userId}")
    List<UserLoginRecord> selectListByUserId(@Param("userId") Long userId);
}
  • DynamicTableNameTest 测试类
    新增
package com.coolw.mybatisplus.dynamictable;

import cn.hutool.core.util.IdUtil;
import com.coolw.mybatisplus.config.IdTableNameHandler;
import com.coolw.mybatisplus.dao.UserLoginRecordMapper;
import com.coolw.mybatisplus.entity.UserLoginRecord;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.List;
import java.util.Random;

/**
 * 分表:动态表名测试类
 *
 * @author coolw
 * @version 1.0
 * @date 2023/9/22 9:18
 */
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest
public class DynamicTableNameTest {

    @Autowired
    private UserLoginRecordMapper userLoginRecordMapper;

    @Test
    public void testInsert() {
        long userId = IdUtil.getSnowflakeNextId();

        // 设置分表的id
        IdTableNameHandler.initCurrentId(1L);
        UserLoginRecord userLoginRecord1 = new UserLoginRecord(userId, 1);
        userLoginRecordMapper.insert(userLoginRecord1);

        // 设置分表的id
        IdTableNameHandler.initCurrentId(2L);
        UserLoginRecord userLoginRecord2 = new UserLoginRecord(userId, 2);
        userLoginRecordMapper.insert(userLoginRecord2);
    }

    @Test
    public void testInsert1() {
        Random random = new Random();

        long userId = IdUtil.getSnowflakeNextId();

        for (int i = 1; i <= 20; i++) {
            // 设置分表的id
            IdTableNameHandler.initCurrentId((long) i);
            UserLoginRecord userLoginRecord = new UserLoginRecord(userId, random.nextInt(3));
            userLoginRecordMapper.insert(userLoginRecord);
        }
    }

    @Test
    public void testSelectList() {
        // 设置分表的id
        IdTableNameHandler.initCurrentId(12L);
        List<UserLoginRecord> records = userLoginRecordMapper.selectList(null);
        log.info("records:{}", records);
    }

    @Test
    public void testSelectListByUserId() {
        long userId = 1705033771829153792L;

        // 设置分表的id
        IdTableNameHandler.initCurrentId(12L);
        List<UserLoginRecord> records = userLoginRecordMapper.selectListByUserId(userId);
        log.info("records:{}", records);
    }
}

  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
ShardingSphere:SpringBoot2+MybatisPlus+读写分离+分库分表课程目标快速的掌握读写分离+分表的实战,即插即用适用人群IT从业人员,开发人员,Java从业者,互联网从业者,性能调优人群课程简介ShardingSphere是一套开源的分布式数据库中间件解决方案组成的生态圈。它由Sharding-JDBC、Sharding-Proxy和Sharding-Sidecar(计划中)这3款相互独立的产品组成,shardingSphere定位为关系型数据库中间件。 Sharding-JDBCSharding-JDBC是Sharding-Sphere的第一个产品,也是Sharding-Sphere的前身,是当当网开源的一个产品。定位为轻量级的Java框架,在Java的JDBC层提供额外服务。 它使用客户端直连数据库,以jar包形式提供服务,无需额外部署和依赖,可理解为增强版的JDBC驱动,完全兼容JDBC和各种ORM框架。他们均提供标准化的数据分片、读写分离、柔性事务和数据治理功能,可适用于如Java同构、异构语言、容器、云原生等各种多样化的应用场景。Sharding-JDBC可以通过Java,YAML,Spring命名空间和Spring Boot Starter四种方式配置,开发者可根据场景选择适合的配置方式。课程特色 本章节以尽量短的时间,为使用者提供最简单的ShardingSphere的快速入门。课程说明该课程属于系列课程,分为读写分离,分库不分表,不分库分表,分库分表,读写分离+分库分表共5个回合。本课程属于其中一个回合,请各位小哥哥们注意,课程的标题哦~

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

coo_lw

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值