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

作者:先养只猫 时间:2023-04-02 22:11:42 

一、水平分表

  • 当业务需求的数据量过大时,一个表格存储数据会非常之多,故时长采用水平分表的方式来减少每张表的数据量即是提升查询数据库时的效率。

  • 水平分表时,各表的结构完全一样,表名不同。

  • 例如:这里我们建了10张user表,每张表的结构完全一致,表名由0~9。

  • 表中包含有id和name属性且都采用varchar的存储类型。

  • 为什么id没有采用int自增的形式?

  • 大型项目极有可能采用分布式数据库,若采用自增的方式,会导致id重复。且id也不一定只是由纯数字组成,id不由数据库自主生成后,可在后台代码中是使用工具类进行id生成。

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

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

二、项目实现

目录结构

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

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

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调用接口

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

创建用户User

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

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

&hellip;多输入些数据&hellip;查看数据库中数据

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

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

&hellip; id查询用户

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

测试成功

三、扩展

还可以在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创建新用户

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

日志打印

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

数据库中数据

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

调用selectUserByIdI进行查询

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

日志查看

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

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

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

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

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

来源:https://blog.csdn.net/qq_52050769/article/details/125841208

标签:mybatis,动态表名
0
投稿

猜你喜欢

  • 一篇文章告诉你JAVA Mybatis框架的核心原理到底有多重要

    2023-11-13 06:20:10
  • 阿里nacos+springboot+dubbo2.7.3统一处理异常的两种方式

    2022-12-05 03:50:25
  • 关于Java中Json的各种处理

    2022-06-12 02:37:48
  • Android MessageQueue消息队列主要作用详解

    2021-11-11 15:25:39
  • c# 如何用lock解决缓存击穿

    2022-12-03 14:27:55
  • 浅谈Springboot实现拦截器的两种方式

    2023-05-10 05:53:50
  • Android集成Flutter

    2023-07-06 13:07:33
  • Unity3D运行报DllNotFoundException错误的解决方案

    2021-09-13 00:22:35
  • Android微信抢红包功能的实现原理浅析

    2023-11-20 19:14:40
  • Android 消息机制以及handler的内存泄露

    2023-08-01 07:59:44
  • Android实现EditText的富文本编辑

    2022-04-14 21:44:29
  • 给C语言初学者的学习建议

    2023-06-14 17:23:44
  • C# 获取硬盘号,CPU信息,加密解密技术的步骤

    2023-04-23 10:37:50
  • 用Kotlin实现Android点击事件的方法

    2023-01-16 15:56:03
  • java 集合----Map、Collection

    2022-11-09 03:39:01
  • Java微信支付之公众号支付、扫码支付实例

    2022-07-10 10:23:11
  • C# 多线程学习之基础入门

    2022-11-08 05:38:13
  • Spring从@Aspect到Advisor使用演示实例

    2022-11-21 11:42:08
  • Java spring AOP基础

    2022-08-10 12:39:25
  • 浅析JDK12的五大重要新特性(推荐)

    2023-01-28 19:09:24
  • asp之家 软件编程 m.aspxhome.com