mybatis水平分表實(shí)現(xiàn)動(dòng)態(tài)表名的項(xiàng)目實(shí)例
一、水平分表
- 當(dāng)業(yè)務(wù)需求的數(shù)據(jù)量過(guò)大時(shí),一個(gè)表格存儲(chǔ)數(shù)據(jù)會(huì)非常之多,故時(shí)長(zhǎng)采用水平分表的方式來(lái)減少每張表的數(shù)據(jù)量即是提升查詢數(shù)據(jù)庫(kù)時(shí)的效率。
- 水平分表時(shí),各表的結(jié)構(gòu)完全一樣,表名不同。
- 例如:這里我們建了10張user表,每張表的結(jié)構(gòu)完全一致,表名由0~9。
- 表中包含有id和name屬性且都采用varchar的存儲(chǔ)類型。
- 為什么id沒(méi)有采用int自增的形式?
- 大型項(xiàng)目極有可能采用分布式數(shù)據(jù)庫(kù),若采用自增的方式,會(huì)導(dǎo)致id重復(fù)。且id也不一定只是由純數(shù)字組成,id不由數(shù)據(jù)庫(kù)自主生成后,可在后臺(tái)代碼中是使用工具類進(jìn)行id生成。
二、項(xiàng)目實(shí)現(xiàn)
目錄結(jié)構(gòu)
不需要的文件我已經(jīng)手動(dòng)馬賽克,避免干擾。
簡(jiǎn)單以一個(gè)用戶信息的新增和查詢為例子。
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 #設(shè)置別名,類名的小寫
工具類
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; } }
通過(guò)封裝的返回類來(lái)實(shí)現(xiàn)數(shù)據(jù)的返回,同時(shí)可以自定義返回code(各種錯(cuò)誤碼),便于項(xiàng)目規(guī)范即管理。
ResultCode:返回碼
package com.example.demo.utils.inter; public interface ResultCode { Integer SUCCESS = 2000; Integer ERROR = 3000; }
TableNameUtil:根據(jù)自己定義的規(guī)則獲取表名
這里可以通過(guò)字符串拼接,實(shí)現(xiàn)user_0~user_9的返回。
數(shù)據(jù)存儲(chǔ)規(guī)則: id最后一個(gè)數(shù)字為0即對(duì)應(yīng)user_0,同理9對(duì)應(yīng)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:根據(jù)自己定義的規(guī)則生成唯一ID
這里的生成規(guī)則是通過(guò)UUID+隨機(jī)0~ 9的數(shù)字來(lái)實(shí)現(xiàn)唯一ID的生成。大型項(xiàng)目一般會(huì)有一個(gè)專門由于生成唯一序列號(hào)的服務(wù)器~。
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); } }
實(shí)體類
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匹配一張表即可,因?yàn)樗斜淼慕Y(jié)構(gòu)完全一致。 public class User implements Serializable { @TableId private String id; private String name; }
dto類(Data Transfer Object)
注: 用例比較簡(jiǎn)單,沒(méi)有幾個(gè)參數(shù),實(shí)際時(shí)新增和查詢可能都會(huì)封裝許多參數(shù)。
UserIn: 新增User時(shí)用于傳遞數(shù)據(jù),id由后臺(tái)生成,前端傳輸數(shù)據(jù)時(shí)就不必存在id字段。
package com.example.demo.po.dto; import lombok.Data; @Data public class UserIn { private String name; }
QueryUserId:查詢時(shí)使用
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> { //這里只是簡(jiǎn)單定義了兩個(gè)接口,本打算寫個(gè)批量插入的, //但是這必須要求批量插入時(shí)的所有表的id最后一位數(shù)字是相同的(即是同一張表), //這里不符合,因?yàn)檫@里是在插入時(shí)隨機(jī)生成的,就和循環(huán)單獨(dú)插入一樣了~ void insertUser(@Param("tableName") String tableName, @Param("user") User user); User selectUserById(@Param("tableName") String tableName, @Param("id") String id); }
UserMapper.xml
與UserMapper相綁定,通過(guò)@Param指定傳參后xml中進(jìn)行調(diào)用時(shí)的名稱。
動(dòng)態(tài)表名的實(shí)現(xiàn)也是通過(guò)傳參實(shí)現(xiàn)的,這里直接傳遞參數(shù)"tableName"讓xml進(jìn)行獲取。
注: 這里使用${tableName}而不使用#{tableNaem}是因?yàn)樵谑褂?進(jìn)行預(yù)加載時(shí),由于此時(shí)未能獲取tableName,即無(wú)法確定具體的表格,預(yù)加載會(huì)報(bào)錯(cuò)。
不錯(cuò)的博客,詳細(xì)介紹
<?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實(shí)現(xiàn)類
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);//根據(jù)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); } }
過(guò)程及結(jié)果截圖展示
通過(guò)Apifox調(diào)用接口
創(chuàng)建用戶User
使用自動(dòng)生成數(shù)據(jù),結(jié)果返回成功
…多輸入些數(shù)據(jù)…查看數(shù)據(jù)庫(kù)中數(shù)據(jù)
… id查詢用戶
測(cè)試成功
三、擴(kuò)展
還可以在xml中通過(guò)調(diào)用Java方法來(lái)實(shí)現(xiàn)生成表名的操作,這里就不需要mapper傳遞tableName參數(shù)了,但是需要用于獲取tableName的參數(shù)。在學(xué)習(xí)中遇到這種方式,做個(gè)記錄。
這種方式比較特殊,一般很少使用。
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> 或通過(guò)bind標(biāo)簽進(jìn)行綁定 <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); }
過(guò)程及結(jié)果截圖展示
調(diào)用insertUserI創(chuàng)建新用戶
日志打印
數(shù)據(jù)庫(kù)中數(shù)據(jù)
調(diào)用selectUserByIdI進(jìn)行查詢
日志查看
關(guān)于xml調(diào)用Java方法時(shí)是否能獲取對(duì)應(yīng)參數(shù)
這里在getUserTableName方法下輸出了一下id
到此這篇關(guān)于mybatis水平分表實(shí)現(xiàn)動(dòng)態(tài)表名的項(xiàng)目實(shí)例的文章就介紹到這了,更多相關(guān)mybatis 動(dòng)態(tài)表名內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring RestTemplate簡(jiǎn)化HTTP通信實(shí)現(xiàn)功能探究
這篇文章主要為大家介紹了Spring框架中的RestTemplate,如果你是個(gè)Java程序員,那么你肯定知道Spring框架的重要性,在Spring的眾多工具中,RestTemplate是用來(lái)簡(jiǎn)化HTTP通信的一個(gè)強(qiáng)大工具2024-01-01MyBatis Plus之實(shí)現(xiàn)動(dòng)態(tài)排序方式
這篇文章主要介紹了MyBatis Plus之實(shí)現(xiàn)動(dòng)態(tài)排序方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-02-02Java中常見(jiàn)的XML解析方法與應(yīng)用詳解
XML(eXtensible Markup Language)是一種用于存儲(chǔ)和傳輸數(shù)據(jù)的標(biāo)記語(yǔ)言,被廣泛應(yīng)用于表示和交換獨(dú)立于應(yīng)用程序和硬件平臺(tái)的結(jié)構(gòu)化信息,下面我們就來(lái)看看它的常見(jiàn)解析方法有哪些吧2024-01-01Idea中maven項(xiàng)目實(shí)現(xiàn)登錄驗(yàn)證碼功能
這篇文章主要介紹了Idea中maven項(xiàng)目實(shí)現(xiàn)登錄驗(yàn)證碼功能,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12JAVA如何獲取jvm和操作系統(tǒng)相關(guān)信息
這篇文章主要介紹了JAVA獲取jvm和操作系統(tǒng)相關(guān)信息,使用Java自帶的類進(jìn)行獲取系統(tǒng)運(yùn)行的相關(guān)信息,在這整理記錄分享一下,需要的朋友可以參考下2022-10-10springboot整合mybatis-plus逆向工程的實(shí)現(xiàn)
這篇文章主要介紹了springboot整合mybatis-plus逆向工程的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08java使用google身份驗(yàn)證器實(shí)現(xiàn)動(dòng)態(tài)口令驗(yàn)證的示例
本篇文章主要介紹了java使用google身份驗(yàn)證器實(shí)現(xiàn)動(dòng)態(tài)口令驗(yàn)證的示例,具有一定的參考價(jià)值,有興趣的可以了解一下2017-08-08找出鏈表倒數(shù)第n個(gè)節(jié)點(diǎn)元素的二個(gè)方法
本文提供了找出鏈表倒數(shù)第n個(gè)節(jié)點(diǎn)元素的二個(gè)方法,其中一個(gè)方法是JAVA代碼實(shí)現(xiàn)2013-11-11SpringDataJPA實(shí)體類關(guān)系映射配置方式
這篇文章主要介紹了SpringDataJPA實(shí)體類關(guān)系映射配置方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12