前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >分表:没有Sharding-JDBC,你还有Mybatis!

分表:没有Sharding-JDBC,你还有Mybatis!

作者头像
品茗IT
发布2019-09-12 09:43:07
7880
发布2019-09-12 09:43:07
举报
文章被收录于专栏:品茗IT品茗IT

分表:没有Sharding-JDBC,你还有Mybatis!

Sharding-JDBC作为时下流行的分库分表工具,自有其魅力之处;

这里,我也写过两篇文章关于《SpringBoot入门建站全系列(二十四)使用Sharding-JDBC进行分库分表》《Spring整合Sharding-JDBC分库分表详情》

阿里的Java开发手册的 MySQL 数据库--》建表规约--》第14条提到:单表行数超过 500 万行或者单表容量超过 2GB,才推荐进行分库分表,当然,这不是绝对的,如果我们的数据呈现一定的规律,就是历史数据使用不频繁或基本不使用,也可以考虑分表存储几个月前的数据。所以,我这地方想说的是,当我们考虑分库分表时,应该先考虑是否需要分库,如果不需要分库,是否需要分页查询,如果不需要分页(如果自己实现归并分页另说),可以不使用Sharding-JDBC了;Mybatis就可以帮你实现!

注:网上也有人用mybatis分库,建议不要这样搞,mybatis分库要求当前用户可以操作其他数据库,这样看运维不打死你。

阿里的Java开发手册: https://www.pomit.cn/develops/aliJava2.html 我这里就有一份可以在线阅读。

**如果大家正在寻找一个java的学习环境,或者在开发中遇到困难,可以<a

href="https://jq.qq.com/?_wv=1027&k=52sgH1J"

target="_blank">

加入我们的java学习圈,点击即可加入

</a>

,共同学习,节约学习时间,减少很多在学习中遇到的难题。**

一、Mybatis分表的演化

3.1 $符号提供分表

当年看到Mybatis的$符号,第一反应就是,肯定又是说$和#的区别了,没错,这两个符号的区别特别重要,也经常被当作面试题来刁难初学者。

但是,它真的是太普通的一个概念,$就是字符串替换,#是变量替换。也会有长者告诉你,不要用$,会导致SQL注入的。其实,这只是使用场景的问题。

  1. 如果$用来替换前端传过来的参数值,那就有很大可能性被sql注入,建议使用#,最大限度避免sql注入。
  2. 如果$用户替换表名,那就没啥问题了,表名又不会从前台传入。。

所以,我们可以使用$来进行表名的动态设置。

比如,我们打算对聊天记录做分表操作,最近一个月的聊天记录存储在t_chat_info表中,历史数据使用房间ID取余的形式分散在其他10个表中,此时,我们可以定义实体ChatInfo(对应表t_chat_info),中有一个字段叫liveId(房间ID对应数据库的live_id),是一个数字类型,可以作为分表字段。

3.1.1 确定表名

根据liveId取余,获取到表名t_chat_info_x。

代码语言:javascript
复制
public ChatInfo findByChatNoAndLiveId(Integer liveId, Long id) {
	String tableName = "t_chat_info_" + liveId % 10;
	return chatInfoMapper.findByCondition(id, tableName);
}
3.1.2 Mybatis查询

使用${tableName}替换tableName参数,实现不同表查询数据。

代码语言:javascript
复制
@Select({
   "<script>",
		"SELECT ",
		"chat_no as chatNo,user_id as userId,live_id as liveId,",
		"nick_name as nickName,create_time as createTime,",
		"delete_flag as deleteFlag,read_flag as readFlag",
		"FROM ${tableName} where chat_no = #{id}",
   "</script>"})
ChatInfo findByCondition(@Param("id") Long id, @Param("tableName") String tableName);
3.2 bind使分表更简单

bind:可以将OGNL表达式的值绑定到一个变量中,方便后来引用这个变量的值。

OGNL是一种功能强大的表达式语言,通过它简单一致的表达式语法,可以存取对象的任意属性,调用对象的方法,遍历整个对象的结构图,实现字段类型转化等功能。

OGNL是通常要结合Struts 2的标志一起使用。主要是#、%和$这三个符号的使用。

Mybatis使用OGNL,可以动态替换sql语句中的变量。

MyBatis常用OGNL表达式(来源):

  • e1 or e2
  • e1 and e2
  • e1 == e2,e1 eq e2
  • e1 != e2,e1 neq e2
  • e1 lt e2:小于
  • e1 lte e2:小于等于,其他gt(大于),gte(大于等于)
  • e1 in e2
  • e1 not in e2
  • e1 + e2,e1 * e2,e1/e2,e1 - e2,e1%e2
  • !e,not e:非,求反
  • e.method(args)调用对象方法
  • e.property对象属性值
  • e1 e2 按索引取值,List,数组和Map
  • @class@method(args)调用类的静态方法
  • @class@field调用类的静态字段值

上述内容只是合适在MyBatis中使用的OGNL表达式.

有了bind,我们就可以将分表策略固定写在类中的静态方法,或者对象方法。

3.2.1 确定表名

比如,我们打算对聊天记录做分表操作,最近一个月的聊天记录存储在t_chat_info表中,历史数据使用房间ID取余的形式分散在其他10个表中,此时,我们可以定义实体ChatInfo(对应表t_chat_info),中有一个字段叫liveId(房间ID对应数据库的live_id),是一个数字类型,可以作为分表字段。

我们在ChatInfo实体中定义findTable静态方法:

代码语言:javascript
复制
public static String findTable(Integer liveId) {
	int index = liveId % 10;
	String tableName = "t_chat_info_" + index;
	return tableName;
}
3.2.2 Mybatis查询

使用@class@method(args)调用类的静态方法方式调用ChatInfo中的findTable方法,获取表名.

代码语言:javascript
复制
@Select({
   "<script>",
		"<bind name=\"patternTable\" value=\"@com.cff.springbootwork.sharding.jdbc.domain.ChatInfo@findTable(liveId)",
		"SELECT ",
		"chat_no as chatNo,user_id as userId,live_id as liveId,",
		"nick_name as nickName,create_time as createTime,",
		"delete_flag as deleteFlag,read_flag as readFlag",
		"FROM ${patternTable} where chat_no = #{id}",
   "</script>"})
ChatInfo findByCondition(@Param("id") Long id, @Param("liveId") Integer liveId);

二、Mybatis分表的增删改查写法

首先,我们还是使用实体ChatInfo。在实体ChatInfo中,定义两个静态方法:

根据分表字段找到单个表:

代码语言:javascript
复制
public static String findTable(Integer liveId) {
	int index = liveId % 10;
	String tableName = "t_chat_info_" + index;
	return tableName;
}

根据分表字段的起始限制找到多个表:

代码语言:javascript
复制
public static String[] findTables(Integer start, Integer end) {
	List<String> tables = new ArrayList<>(); 
	for(int i = start; i <= end; i++){
		tables.add("t_chat_info_" + i);
	}
	String[] arr = new String[0];
	return tables.toArray(arr);
}
2.1 Insert写法

${tableName}为静态方法findTable指定的唯一表名。切记要带分表字段,且能根据分表字段计算出分表。

代码语言:javascript
复制
@Insert({
   "<script> ",
	   "<bind name=\"tableName\" value=\"@com.cff.springbootwork.sharding.jdbc.domain.ChatInfo@findTable(liveId)\" />",
	   "INSERT INTO ${tableName}",
		"( chat_no,user_id,live_id,nick_name,create_time,delete_flag,read_flag ) ",
		" values ",
		"( #{chatNo},#{userId},#{liveId},#{nickName},#{createTime},#{deleteFlag},#{readFlag} ) ",
   "</script>"})
int save(ChatInfo item);
2.2 Delete写法

${tableName}为静态方法findTable指定的唯一表名。切记要带分表字段,且能根据分表字段计算出分表。

代码语言:javascript
复制
@Delete({
   "<script>",
		"<bind name=\"tableName\" value=\"@com.cff.springbootwork.sharding.jdbc.domain.ChatInfo@findTable(paramIn.liveId)\" />",
		"delete from ${tableName}",
		"<trim prefix=\" where \" prefixOverrides=\"AND\">",
		"<if test='paramIn.chatNo != null '> and chat_no = #{paramIn.chatNo} </if>",
		"<if test='paramIn.userId != null '> and user_id = #{paramIn.userId} </if>",
		"<if test='paramIn.liveId != null '> and live_id = #{paramIn.liveId} </if>",
		"<if test='paramIn.nickName != null '> and nick_name = #{paramIn.nickName} </if>",
		"<if test='paramIn.createTime != null '> and create_time = #{paramIn.createTime} </if>",
		"<if test='paramIn.deleteFlag != null '> and delete_flag = #{paramIn.deleteFlag} </if>",
		"<if test='paramIn.readFlag != null '> and read_flag = #{paramIn.readFlag} </if>",
		"</trim>",
   "</script>"
})
int delete(@Param("paramIn") ChatInfo paramIn);
2.3 Update写法

${tableName}为静态方法findTable指定的唯一表名。切记要带分表字段,且能根据分表字段计算出分表。

代码语言:javascript
复制
@Update({
   "<script>",
		"<bind name=\"tableName\" value=\"@com.cff.springbootwork.sharding.jdbc.domain.ChatInfo@findTable(paramIn.liveId)\" />",
		"update ${tableName}",
		"<trim prefix=\"set\" suffixoverride=\",\"> ",
		"<if test='paramIn.userId != null '> user_id = #{paramIn.userId}, </if>",
		"<if test='paramIn.nickName != null '> nick_name = #{paramIn.nickName}, </if>",
		"<if test='paramIn.createTime != null '> create_time = #{paramIn.createTime}, </if>",
		"<if test='paramIn.deleteFlag != null '> delete_flag = #{paramIn.deleteFlag}, </if>",
		"<if test='paramIn.readFlag != null '> read_flag = #{paramIn.readFlag}, </if>",
		"</trim>",
		"where chat_no = #{paramIn.chatNo}",
   "</script>"
})
int update(@Param("paramIn") ChatInfo updateIn);
2.4 Select写法
2.4.1 查询指定表

${tableName}为静态方法findTable指定的唯一表名。切记要带分表字段,且能根据分表字段计算出分表。

代码语言:javascript
复制
@Select({
   "<script>",
		"<bind name=\"patternTable\" value=\"@com.cff.springbootwork.sharding.jdbc.domain.ChatInfo@findTable(liveId)",
		"SELECT ",
		"chat_no as chatNo,user_id as userId,live_id as liveId,",
		"nick_name as nickName,create_time as createTime,",
		"delete_flag as deleteFlag,read_flag as readFlag",
		"FROM ${patternTable} where chat_no = #{id}",
   "</script>"})
ChatInfo findByCondition(@Param("id") Long id, @Param("liveId") Integer liveId);
2.4.2 查询多个表

使用findTables计算的patternTables来做mybatis的foreach循环。使用union来连接查询结果。

Union:对两个结果集进行并集操作,不包括重复行,同时进行默认规则的排序;

Union All:对两个结果集进行并集操作,包括重复行,不进行排序;

代码语言:javascript
复制
@Select({
   "<script>",
		"<bind name=\"patternTables\" value=\"@com.cff.springbootwork.sharding.jdbc.domain.ChatInfo@findTables(start,end)",
		"<foreach collection =\"patternTables\" item=\"item\" index=\"index\" separator=\" union \">",
		"SELECT ",
		"chat_no as chatNo,user_id as userId,live_id as liveId,",
		"nick_name as nickName,create_time as createTime,",
		"delete_flag as deleteFlag,read_flag as readFlag",
		"FROM ${item} where delete_flag = #{deleteFlag}",
		"</foreach>",
   "</script>"})
ChatInfo findRange(@Param("start") Integer start, @Param("end") Integer end, @Param("deleteFlag") Integer deleteFlag);
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-08-30 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 分表:没有Sharding-JDBC,你还有Mybatis!
  • 一、Mybatis分表的演化
    • 3.1 $符号提供分表
      • 3.1.1 确定表名
      • 3.1.2 Mybatis查询
    • 3.2 bind使分表更简单
      • 3.2.1 确定表名
      • 3.2.2 Mybatis查询
  • 二、Mybatis分表的增删改查写法
    • 2.1 Insert写法
      • 2.2 Delete写法
        • 2.3 Update写法
          • 2.4 Select写法
            • 2.4.1 查询指定表
            • 2.4.2 查询多个表
        相关产品与服务
        云数据库 MySQL
        腾讯云数据库 MySQL(TencentDB for MySQL)为用户提供安全可靠,性能卓越、易于维护的企业级云数据库服务。其具备6大企业级特性,包括企业级定制内核、企业级高可用、企业级高可靠、企业级安全、企业级扩展以及企业级智能运维。通过使用腾讯云数据库 MySQL,可实现分钟级别的数据库部署、弹性扩展以及全自动化的运维管理,不仅经济实惠,而且稳定可靠,易于运维。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档