一、水平分表

  • 当业务需求的数据量过大时,一个表格存储数据会非常之多,故时长采用水平分表的方式来减少每张表的数据量即是提升查询数据库时的效率。
  • 水平分表时,各表的结构完全一样,表名不同。
  • 例如:这里我们建了10张user表,每张表的结构完全一致,表名由0~9。
  • 表中包含有id和name属性且都采用varchar的存储类型。
  • 为什么id没有采用int自增的形式?
  • 大型项目极有可能采用分布式数据库,若采用自增的方式,会导致id重复。且id也不一定只是由纯数字组成,id不由数据库自主生成后,可在后台代码中是使用工具类进行id生成。

在这里插入图片描述

在这里插入图片描述

二、项目实现

目录结构

不需要的文件我已经手动马赛克,避免干扰。
简单以一个用户信息的新增和查询为例子。

在这里插入图片描述

pom.xml

本次使用mybatis mybatis-plus为例。

        <!--spring mybatis-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.2.0</version>
        </dependency>

        <!--mybatis-plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.3.1</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

application.yaml

server:
  port: 9090
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/test?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8
    username: root
    password: root

mybatis-plus:
  mapper-locations: classpath:mybatis/*.xml #mapper位置
  type-aliases-package: com/example/demo/po #设置别名,类名的小写

工具类

R返回类

package com.example.demo.utils;

import com.example.demo.utils.inter.ResultCode;
import lombok.Data;

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


@Data
public class R<T> {

  private Boolean success;

  private Integer code;

  private String message;

  private T data;


  public R() {
  }

  public static <T> R<T> ok(){
      R<T> r = new R<>();
      r.setSuccess(true);
      r.setCode(ResultCode.SUCCESS);
      r.setMessage("成功");
      return r;
  }
  public static <T> R<T> error() {
      R<T> r = new R<>();
      r.setSuccess(false);
      r.setCode(ResultCode.ERROR);
      r.setMessage("失败");
      return r;
  }

  public R<T> success(Boolean success) {
      this.setSuccess(success);
      return this;
  }

  public R<T> message(String message) {
      this.setMessage(message);
      return this;
  }

  public R<T> code(Integer code) {
      this.setCode(code);
      return this;
  }

  public void setData(T data) {
      this.data = data;
  }

  public R<T> data(T data) {
      this.setData(data);
      return this;
  }

  public R<T> setReLoginData() {
      Map<String, Integer> resultMap = new HashMap<>();
      resultMap.put("code", -100);
      this.data((T) resultMap);
      return this;
  }

  public R<T> setErrorCode(Integer errorCode) {
      Map<String, Integer> resultMap = new HashMap<>();
      resultMap.put("errorCode", errorCode);
      this.data((T) resultMap);
      return this;
  }
}

通过封装的返回类来实现数据的返回,同时可以自定义返回code(各种错误码),便于项目规范即管理。

ResultCode:返回码

package com.example.demo.utils.inter;

public interface ResultCode {
    Integer SUCCESS = 2000;

    Integer ERROR = 3000;
}

TableNameUtil:根据自己定义的规则获取表名

这里可以通过字符串拼接,实现user_0~user_9的返回。
数据存储规则: id最后一个数字为0即对应user_0,同理9对应user_9。

package com.example.demo.utils;

import org.springframework.stereotype.Component;

@Component
public class TableNameUtil {

    public String getUserTableName(String id){
        return "user_" id.substring(id.length()-1);
    }
}

IdUtil:根据自己定义的规则生成唯一ID

这里的生成规则是通过UUID 随机0~ 9的数字来实现唯一ID的生成。大型项目一般会有一个专门由于生成唯一序列号的服务器~。

package com.example.demo.utils;
import org.springframework.stereotype.Component;
import java.util.UUID;

@Component
public class IdUtil {

    public synchronized String generatorUserId(){
        return UUID.randomUUID() String.valueOf(Math.random()*10);
    }
}

实体类

po类(Persistent Object )

package com.example.demo.po;

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

import java.io.Serializable;

@Data
@TableName("user_0")//mybatis-plus匹配一张表即可,因为所有表的结构完全一致。
public class User implements Serializable {
    @TableId
    private String id;

    private String name;
}

dto类(Data Transfer Object)

注: 用例比较简单,没有几个参数,实际时新增和查询可能都会封装许多参数。
UserIn: 新增User时用于传递数据,id由后台生成,前端传输数据时就不必存在id字段。

package com.example.demo.po.dto;

import lombok.Data;

@Data
public class UserIn {
    private String name;
}

QueryUserId:查询时使用

package com.example.demo.po.dto;

import lombok.Data;

@Data
public class QueryUserId {
    private String id;
}

Mapper层

UserMapper

package com.example.demo.mapper;

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

@Mapper
public interface UserMapper extends BaseMapper<User> {
//这里只是简单定义了两个接口,本打算写个批量插入的,
//但是这必须要求批量插入时的所有表的id最后一位数字是相同的(即是同一张表),
//这里不符合,因为这里是在插入时随机生成的,就和循环单独插入一样了~
    void insertUser(@Param("tableName") String tableName, @Param("user") User user);

    User selectUserById(@Param("tableName") String tableName, @Param("id") String id);
}

UserMapper.xml

与UserMapper相绑定,通过@Param指定传参后xml中进行调用时的名称。
动态表名的实现也是通过传参实现的,这里直接传递参数"tableName"让xml进行获取。
注: 这里使用${tableName}而不使用#{tableNaem}是因为在使用#进行预加载时,由于此时未能获取tableName,即无法确定具体的表格,预加载会报错。
不错的博客,详细介绍

<?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.example.demo.mapper.UserMapper">
    <insert id="insertUser" parameterType="com.example.demo.po.User">
        insert into ${tableName}(id, name) values (#{user.id}, #{user.name});
    </insert>
    <select id="selectUserById" parameterType="java.lang.String" resultType="com.example.demo.po.User">
        select * from ${tableName} where id = '${id}'
    </select>
</mapper>

Service层

package com.example.demo.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.example.demo.po.User;

public interface UserService extends IService<User> {

    void insertUser(User user);

    User selectUserById(String id);
}

impl实现类

package com.example.demo.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.demo.mapper.UserMapper;
import com.example.demo.po.User;
import com.example.demo.service.UserService;
import com.example.demo.utils.IdUtil;
import com.example.demo.utils.TableNameUtil;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

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

    @Resource
    private IdUtil idUtil;

    @Resource
    private TableNameUtil tableNameUtil;

    @Override
    public void insertUser(User user) {
        String id = idUtil.generatorUserId();//生成Id
        user.setId(id);
        String tableName = tableNameUtil.getUserTableName(id);//根据id获取表名
        baseMapper.insertUser(tableName,user);
    }

    @Override
    public User selectUserById(String id) {
        String tableName = tableNameUtil.getUserTableName(id);
        return baseMapper.selectUserById(tableName, id);
    }
}

Controller层

package com.example.demo.controller;

import com.example.demo.po.User;
import com.example.demo.po.dto.UserIn;
import com.example.demo.service.UserService;
import com.example.demo.utils.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;

@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {

    @Resource
    private UserService userService;

    @PostMapping("/insertUser")
    public R insertUser(@RequestBody UserIn userIn){
        User user = new User();
        user.setName(userIn.getName());
        userService.insertUser(user);
        return R.ok();
    }

    @GetMapping("/selectUserById")
    public R selectUserById(@RequestBody String id){
        User user = userService.selectUserById(id);
        return R.ok().data(user);
    }
}

过程及结果截图展示

通过Apifox调用接口

在这里插入图片描述

创建用户User

使用自动生成数据,结果返回成功

在这里插入图片描述

…多输入些数据…查看数据库中数据

在这里插入图片描述

在这里插入图片描述

… id查询用户

在这里插入图片描述

测试成功

三、扩展

还可以在xml中通过调用Java方法来实现生成表名的操作,这里就不需要mapper传递tableName参数了,但是需要用于获取tableName的参数。在学习中遇到这种方式,做个记录。
这种方式比较特殊,一般很少使用。

UserMapper新增

    void insertUserI(@Param("user") User user);
    
    User selectUserByIdI(@Param("id") String id);

UserMapper.xml新增

    <insert id="insertUserI" parameterType="com.example.demo.po.User">
        insert into "${@com.example.demo.utils.TableNameUtil@getUserTableName(id)}"(id, name) values (#{user.id}, #{user.name});
    </insert>
    <select id="selectUserByIdI" parameterType="java.lang.String" resultType="com.example.demo.po.User">
        select * from "${@com.example.demo.utils.TableNameUtil@getUserTableName(id)}" where id = '${id}'
    </select>
    或通过bind标签进行绑定
    <insert id="insertUserI" parameterType="com.example.demo.po.User">
        <bind name="tableName" value="@com.example.demo.utils.TableNameUtil@getUserTableName(id)"/>
        insert into ${tableName}(id, name) values (#{user.id}, #{user.name});
    </insert>
    <select id="selectUserByIdI" parameterType="java.lang.String" resultType="com.example.demo.po.User">
        <bind name="tableName" value="@com.example.demo.utils.TableNameUtil@getUserTableName(id)"/>
        select * from ${tableName} where id = '${id}'
    </select>

Service层新增

    void insertUserI(@Param("user") User user);

    User selectUserByIdI(@Param("id") String id);

Controller层新增

    @PostMapping("/insertUserI")
    public R insertUserI(@RequestBody UserIn userIn){
        User user = new User();
        user.setName(userIn.getName());
        userService.insertUser(user);
        log.info("insertUserI OK");
        return R.ok();
    }

    @GetMapping("/selectUserByIdI")
    public R selectUserByIdI(@RequestBody QueryUserId queryUserId){
        User user = userService.selectUserById(queryUserId.getId());
        log.info("selectUserByIdI :" user.toString());
        return R.ok().data(user);
    }

过程及结果截图展示

调用insertUserI创建新用户

在这里插入图片描述

日志打印

在这里插入图片描述

数据库中数据

在这里插入图片描述

调用selectUserByIdI进行查询

在这里插入图片描述

日志查看

在这里插入图片描述

关于xml调用Java方法时是否能获取对应参数

这里在getUserTableName方法下输出了一下id

在这里插入图片描述

在这里插入图片描述

 到此这篇关于mybatis水平分表实现动态表名的项目实例的文章就介绍到这了,更多相关mybatis 动态表名内容请搜索Devmax以前的文章或继续浏览下面的相关文章希望大家以后多多支持Devmax!

mybatis水平分表实现动态表名的项目实例的更多相关文章

  1. Mybatis分页插件PageHelper手写实现示例

    这篇文章主要为大家介绍了Mybatis分页插件PageHelper手写实现示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  2. Springboot集成mybatis实现多数据源配置详解流程

    在日常开发中,若遇到多个数据源的需求,怎么办呢?通过springboot集成mybatis实现多数据源配置,简单尝试一下,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  3. Mybatis结果集映射与生命周期详细介绍

    结果集映射指的是将数据表中的字段与实体类中的属性关联起来,这样 MyBatis 就可以根据查询到的数据来填充实体对象的属性,帮助我们完成赋值操作

  4. Spring Boot 整合持久层之MyBatis

    在实际开发中不仅仅是要展示数据,还要构成数据模型添加数据,这篇文章主要介绍了SpringBoot集成Mybatis操作数据库,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  5. Mybatis详解动态SQL以及单表多表查询的应用

    MyBatis的动态SQL是基于OGNL表达式的,它可以帮助我们方便的在SQL语句中实现某些逻辑,下面这篇文章主要给大家介绍了关于Mybatis超级强大的动态SQL语句的相关资料,需要的朋友可以参考下

  6. mybatis水平分表实现动态表名的项目实例

    本文主要介绍了mybatis水平分表实现动态表名的项目实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  7. Mybatis中${param}与#{param}的区别说明

    这篇文章主要介绍了Mybatis中${param}与#{param}的区别说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  8. MyBatis流式查询的项目实践

    本文主要介绍了MyBatis流式查询的项目实践,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  9. Mybatis图文并茂讲解分页插件

    使用过mybatis的人都知道,mybatis本身就很小且简单,sql写在xml里,统一管理和优化。缺点当然也有,比如我们使用过程中,要使用到分页,如果用最原始的方式的话,1.查询分页数据,2.获取分页长度,也就是说要使用到两个方法才能完成分页

  10. Spring Boot 利用注解方式整合 MyBatis

    这篇文章主要介绍了Spring Boot 利用注解方式整合 MyBatis,文章围绕主主题的相关资料展开详细的内容介绍,需要的小伙伴可以参考一下

随机推荐

  1. Java利用POI实现导入导出Excel表格

    这篇文章主要为大家详细介绍了Java利用POI实现导入导出Excel表格,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  2. Mybatis分页插件PageHelper手写实现示例

    这篇文章主要为大家介绍了Mybatis分页插件PageHelper手写实现示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  3. (jsp/html)网页上嵌入播放器(常用播放器代码整理)

    网页上嵌入播放器,只要在HTML上添加以上代码就OK了,下面整理了一些常用的播放器代码,总有一款适合你,感兴趣的朋友可以参考下哈,希望对你有所帮助

  4. Java 阻塞队列BlockingQueue详解

    本文详细介绍了BlockingQueue家庭中的所有成员,包括他们各自的功能以及常见使用场景,通过实例代码介绍了Java 阻塞队列BlockingQueue的相关知识,需要的朋友可以参考下

  5. Java异常Exception详细讲解

    异常就是不正常,比如当我们身体出现了异常我们会根据身体情况选择喝开水、吃药、看病、等 异常处理方法。 java异常处理机制是我们java语言使用异常处理机制为程序提供了错误处理的能力,程序出现的错误,程序可以安全的退出,以保证程序正常的运行等

  6. Java Bean 作用域及它的几种类型介绍

    这篇文章主要介绍了Java Bean作用域及它的几种类型介绍,Spring框架作为一个管理Bean的IoC容器,那么Bean自然是Spring中的重要资源了,那Bean的作用域又是什么,接下来我们一起进入文章详细学习吧

  7. 面试突击之跨域问题的解决方案详解

    跨域问题本质是浏览器的一种保护机制,它的初衷是为了保证用户的安全,防止恶意网站窃取数据。那怎么解决这个问题呢?接下来我们一起来看

  8. Mybatis-Plus接口BaseMapper与Services使用详解

    这篇文章主要为大家介绍了Mybatis-Plus接口BaseMapper与Services使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  9. mybatis-plus雪花算法增强idworker的实现

    今天聊聊在mybatis-plus中引入分布式ID生成框架idworker,进一步增强实现生成分布式唯一ID,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. Spring JdbcTemplate执行数据库操作详解

    JdbcTemplate是Spring框架自带的对JDBC操作的封装,目的是提供统一的模板方法使对数据库的操作更加方便、友好,效率也不错,这篇文章主要介绍了Spring JdbcTemplate执行数据库操作,需要的朋友可以参考下

返回
顶部