前言:

以前写代码, 关于mysql的分库分表已被中间件服务所支持, 业务代码涉及的sql已规避了这块. 它对扩展友好, 你也不知道到底他分为多少库, 多少表, 一切都是透明的.

不过对于小的团队/工作室而言, 可能就没有那么强大的分布式中间件的基础设施支持了, 而当数据库上去的时候, 分库分表就需要客户端client这边去支持维护了. 如何优雅地使用mybatis支持分表, 这就是本文的主题.

mybatis插件机制:

mybatis支持插件(plugin), 讲得通俗一点就是拦截器(interceptor). 它支持ParameterHandler/StatementHandler/Executor/ResultSetHandler这四个级别进行拦截.

总体概况为:

拦截参数的处理(ParameterHandler)

拦截Sql语法构建的处理(StatementHandler)

拦截执行器的方法(Executor)

拦截结果集的处理(ResultSetHandler)

比如sql rewrite, 它属于StatementHandler的阶段. 以分表实践为例, 它可以简单理解为把table名称替换为分表table名称的过程.

模拟实战:

让我们模拟实战一回, 假定我们有个需求, 就是把重要的业务日志数据, 导入到表tb_record中.

CREATE TABLE `tb_record` (

`id` int(11) NOT NULL AUTO_INCREMENT,

`logs` varchar(128) NOT NULL,

PRIMARY KEY (`id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

但是现在随着业务数据暴增, 单表支撑不了这么多数据. 因此决定把tb_record做水平切分, 按天来做切分tb_record_{yyyyMMdd}, 比如2018/07/26这天的数据, 就导入到表tb_record_20180726中.

之前的mapper接口类如下:

public interface RecordMapper {

@Insert("INSERT INTO tb_record(logs) VALUES(#{logs})")

int addRecord(@Param("logs") String logs);

}

在不改变代码的前提下, 如何支持分表的无感知实现.

代码编写:

由于mybatis的拦截器是全局的, 因此这边引入特定的注解用于区分目标/非目标对象(数据库表).

定义分表策略接口和具体的实现类:

// 分表的策略类

public interface ITableShardStrategy {

String tableShard(String tableName);

}

// 按天切分的分表策略类

public class DateTableShardStrategy implements ITableShardStrategy {

private static final String DATE_PATTERN = "yyyyMMdd";

@Override

public String tableShard(String tableName) {

SimpleDateFormat sdf = new SimpleDateFormat(DATE_PATTERN);

return tableName + "_" + sdf.format(new Date());

}

}

定义注解:

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

public @interface TableShard {

// 要替换的表名

String tableName();

// 对应的分表策略类

Class extends ITableShardStrategy> shardStrategy();

}

编写具体的mybatis拦截器实现:

@Intercepts({

@Signature(

type = StatementHandler.class,

method = "prepare",

args = { Connection.class, Integer.class }

)

})

public class TableShardInterceptor implements Interceptor {

private static final ReflectorFactory defaultReflectorFactory = new DefaultReflectorFactory();

@Override

public Object intercept(Invocation invocation) throws Throwable {

StatementHandler statementHandler = (StatementHandler) invocation.getTarget();

MetaObject metaObject = MetaObject.forObject(statementHandler,

SystemMetaObject.DEFAULT_OBJECT_FACTORY,

SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY,

defaultReflectorFactory

);

MappedStatement mappedStatement = (MappedStatement)

metaObject.getValue("delegate.mappedStatement");

String id = mappedStatement.getId();

id = id.substring(0, id.lastIndexOf('.'));

Class clazz = Class.forName(id);

// 获取TableShard注解

TableShard tableShard = (TableShard)clazz.getAnnotation(TableShard.class);

if ( tableShard != null ) {

String tableName = tableShard.tableName();

Class extends ITableShardStrategy> strategyClazz = tableShard.shardStrategy();

ITableShardStrategy strategy = strategyClazz.newInstance();

String newTableName = strategy.tableShard(tableName);

// 获取源sql

String sql = (String)metaObject.getValue("delegate.boundSql.sql");

// 用新sql代替旧sql, 完成所谓的sql rewrite

metaObject.setValue("delegate.boundSql.sql", sql.replaceAll(tableName, newTableName));

}

// 传递给下一个拦截器处理

return invocation.proceed();

}

@Override

public Object plugin(Object target) {

// 当目标类是StatementHandler类型时,才包装目标类,否者直接返回目标本身, 减少目标被代理的次数

if (target instanceof StatementHandler) {

return Plugin.wrap(target, this);

} else {

return target;

}

}

@Override

public void setProperties(Properties properties) {

}

}

注: 不同mybatis的版本, 具体的api略有出入, 当前mybatis版本为(3.4.6).

配置plugin标签, 注意要在mybatis-config.xml(mybatis全局属性配置文件)中进行配置

测试:

对原来的RecordMapper添加@TableShard注解:

@TableShard(tableName = "tb_record", shardStrategy = DateTableShardStrategy.class)

public interface RecordMapper {

@Insert("INSERT INTO tb_record(logs) VALUES(#{logs})")

int addRecord(@Param("logs") String logs);

}

编写简单的测试代码:

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration({"classpath:application-context.xml"})

public class RecordMapperTest {

@Resource

private RecordMapper recordMapper;

@Test

public void testAddRecord() {

String logs = "hello lilei";

recordMapper.addRecord(logs);

}

}

查看数据库进行数据验证:

后记:

总的来说, mybatis的拦截器给开发者很大的自由度, 像这边的分表实践是很好的例子. 但分表的策略有很多, 很多都是基于特定的维度进行散列, 总觉得在拦截器中实现, 多少有些侵入性, 要做到无感透明, 其实还是挺难的.

mybatis mysql 分表_Mybatis的分表实战相关推荐

  1. mybatis mysql 中文乱码_Mybatis + Mysql 插入数据时中文乱码问题

    近日跟朋友一起建立一个项目,用的是spring+mybatis+mysql. 今天碰到一个mybatis向mysql中插入数据时,中文显示为'???'的问题,拿出来说下. 对于数据库操作中出现的中文乱 ...

  2. mybatis mysql 树形结构_Mybatis查询树形结构数据

    数据表的设计 创建数据表 对于树形结构的数据库设计通常是基于继承关系设计的,也就是通过父ID关联来实现的. 一张树形结构的数据表基本的三个字段:id(自己).name(名称)和parentid(父类编 ...

  3. mybatis mysql 模糊查询语句_mybatis模糊查询语句及注意事项

    select count(*) from t_user loginacct like concat("%",#{queryText},"%") 1.动态查询语句 ...

  4. mybatis mysql 模糊查询_MyBatis Oracle、MySQL、DB2、SQLServer的like模糊查询

    Oracle数据库: select * from t_user where name like '%'||#{search_name}||'%' 或者 select * from t_user whe ...

  5. mybatis mysql 模糊查询语句_mybatis+Spring mysql的模糊查询问题

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 查询的mapping.xml语法 resultType="java.lang.Integer"> SELECT count(yy ...

  6. mybatis mysql 中文乱码_mybatis连接mysql数据库插入中文乱码

    对于mysql数据库的乱码问题,有两中情况: 1. mysql数据库编码问题(建库时设定). 2. 连接mysql数据库的url编码设置问题. 对于第一个问题,目前个人发现只能通过重新建库解决,建库的 ...

  7. mysql mybatis分表查询_mybatis 自动分表

    参考: 相关源码已上传至我的 github 欢迎转载,转载请注明出处,尊重作者劳动成果:https://www.cnblogs.com/li-mzx/p/9963312.html 前言 小弟才疏学浅, ...

  8. SSM框架(spring+springmvc+mybatis)+Mysql实现的社区物业管理系统(功能包含分角色,报修管理、抄表管理、车位管理、房屋管理、业主管理、投诉管理、报修统计分析等)

    博客目录 SSM框架(spring+springmvc+mybatis)+Mysql实现的社区物业管理系统 实现功能截图 系统功能 使用技术 代码 完整源码 SSM框架(spring+springmv ...

  9. ShardingSphere分库分表(SpringBoot+mybatis+mysql)配置

    一.什么是ShardingSphere 定位为轻量级 Java 框架,在 Java 的 JDBC 层提供的额外服务. 它使用客户端直连数据库,以 jar 包形式提供服务,无需额外部署和依赖,可理解为增 ...

最新文章

  1. python画布位置_如何调整tkinter画布的位置
  2. Vant Weapp的dialog组件在mpvue小程序中使用注意事项
  3. 【2018.9.26】K-D Tree详解
  4. apk系统签名和发布软件签名的理解
  5. 直通车怎么能不推广计算机设备,直通车智能推广具体怎么设置?如何操作?
  6. 计算机桌面设置上时间表,如何在电脑桌面设置显示星期
  7. VFP全面控制EXCEL(转自十豆三老师)
  8. [LOJ2339][虚树][边分治][树形DP]WC2018:通道
  9. android 基带版本,手机基带是什么?手机的基带版本是什么意思?
  10. 这种高逼格的图片效果,居然也可以用PPT制作!
  11. 【TS】泛型 Generics
  12. 安徽省毫州市谷歌卫星地图下载(百度网盘离线包下载)
  13. html datatable修改行,DataTable根据条件修改行颜色和特定格颜色
  14. C语言期末设计不在是千篇一律的学生管理系统,新题设值教师工作量管理系统(数组)。
  15. Python合并PDF、操作图片以及生成验证码
  16. 阿里云 mysql tps_MySQL_tps
  17. 一朵云开启智慧交通新未来
  18. div添加阴影(盒子阴影)box-shadow各参数含义
  19. web端 刷卡器_web工程集成身份证读卡器,高拍仪
  20. 如何下载张家口市卫星地图高清版大图

热门文章

  1. Linux 常用命令汇总及解释。
  2. 优化SQL查询:如何写出高性能SQL语句
  3. EmberJS路由详解
  4. 10个最好的 jQuery 视频插件(转)
  5. C#实现对指定文件夹中文件按修改时间排序
  6. sql语句or与union all的执行效率比较
  7. 日志插件 log4net 的使用
  8. php多线程安装pthreads步骤详解
  9. PHP的单引号双引号
  10. JQUERY的location.href.split