使用Mybatis拦截器实现数据分表
文章目录
- 使用Mybatis拦截器实现数据分表
- 分表注解
- Mybaits的拦截器实现
- 参考
使用Mybatis拦截器实现数据分表
在项目中我们是用Mybatis + TKMapper + MYSQL存储了一些消息日志,但是现在随着业务数据暴增, 单表支撑不了这么多数据. 因此决定把表做水平切分, 按照月份来给表进行切分。这样当我们需要housekeep数据的时候,就可以直接drop掉表了,不论是备份还是删除效率都会比较高
那我们就会是用到Mybaits的拦截器
Mybatis插件机制:
Mybatis支持插件(plugin), 讲得通俗一点就是拦截器(interceptor). > 它支持ParameterHandler/StatementHandler/Executor/ResultSetHandler这四个级> 别进行拦截.
总体概况为:
- 拦截参数的处理(ParameterHandler)
- 拦截Sql语法构建的处理(StatementHandler)
- 拦截执行器的方法(Executor)
- 拦截结果集的处理(ResultSetHandler)
分表注解
首先我们想要的效果是只有指定的表才需要分表,并不是全部表都需要分表,因为拦截器是全局的,所以我们需要做特殊处理,当只有指定的表我们才进行拦截和处理。
第二点,我们的分表策略目前是按照年月份来进行分表,以后可能会按照天来分表,也就是分表策略是可变的,所以我们的代码是可扩展的,能够自定义分表策略的。
基于以上两点,我们会构造一个注解,作用于表上,为我们指定分表策略,以及标记当前表是需要做分表的。
@Target({TYPE})
@Retention(RUNTIME)
public @interface TableShard {Class<? extends TableShardStrategy> shardStrategy();
}
分表策略接口
public interface TableShardStrategy {String getTableShardName(String tableName);
}
从方法名就可以看出来,返回的是一个分表后的表名
目前只有一个实现类,也就是按照年月份分表
public class DateTableShardStrategy implements TableShardStrategy {public static final String PATTERN = "yyyyMM";@Overridepublic String getTableShardName(String tableName) {YearMonth yearMonth = Optional.ofNullable(ProcessContextHolder.get()).orElse(ProcessInfo.builder().yearMonth(TimeUtils.getYearMonth()).build()).getYearMonth();String date = DateTimeFormatter.ofPattern(PATTERN).format(yearMonth);return tableName + "_" + date;}
}
具体的实现其实就是根据当前实现获取月份,然后在当前的表名后面加上年月。 至于为什么中间从ProcessContextHolder.get()
获取时间呢,其实是当有消息进来的时候,我们会在ThreadLocal存一个当前时间作为当前消息的全局时间。为什么需要这么做呢,就是担心一条消息在处理的过程中,出现跨月的情况,所以导致同一条消息的数据,存储在了两个不同的表。所以需要维护一个全局的时间。
之后我们就可以在表的实体类上加上该注解了@TableShard(shardStrategy = DateTableShardStrategy.class)
Mybaits的拦截器实现
最后就是Mybatis的拦截器实现了。
@Intercepts({@Signature(type = StatementHandler.class,method = "prepare",args = {Connection.class, Integer.class})
})
@Slf4j
@Component
@ConditionalOnProperty(value = "table.shard.enabled", havingValue = "true") //加上了table.shard.enabled 该配置才会生效
public class MybatisStatementInterceptor implements Interceptor {private static final ReflectorFactory defaultReflectorFactory = new DefaultReflectorFactory();public static final String DELEGATE_BOUND_SQL_SQL = "delegate.boundSql.sql";public static final String DELEGATE_MAPPED_STATEMENT = "delegate.mappedStatement";@Overridepublic 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_MAPPED_STATEMENT);Class<?> clazz = getTableClass(mappedStatement);if (clazz == null) {return invocation.proceed();}TableShard tableShard = clazz.getAnnotation(TableShard.class); //获取表实体类上的注解Table table = clazz.getAnnotation(Table.class);if (tableShard != null && table != null) { //如果注解存在就执行分表策略String tableName = table.name();Class<? extends TableShardStrategy> strategyClazz = tableShard.shardStrategy();TableShardStrategy strategy = strategyClazz.getDeclaredConstructor().newInstance();String tableShardName = strategy.getTableShardName(tableName);String sql = (String) metaObject.getValue(DELEGATE_BOUND_SQL_SQL);metaObject.setValue(DELEGATE_BOUND_SQL_SQL, sql.replaceAll(tableName, tableShardName.toUpperCase())); //替换表名}return invocation.proceed();}private Class<?> getTableClass(MappedStatement mappedStatement) throws ClassNotFoundException {String className = mappedStatement.getId();className = className.substring(0, className.lastIndexOf('.')); //获取到BaseMapper的实现类Class<?> clazz = Class.forName(className);if (BaseMapper.class.isAssignableFrom(clazz)) {return (Class<?>) ((ParameterizedType) (clazz.getGenericInterfaces()[0])).getActualTypeArguments()[0]; //获取表实体类//public interface XXXMapper extends BaseMapper<XXX> 其实就是获取到泛型中的具体表实体类return null;}@Overridepublic Object plugin(Object target) {if (target instanceof StatementHandler) {return Plugin.wrap(target, this);} else {return target;}}}
好了,这样就实现了使用Mybatis拦截器进行数据分表
参考
通过MyBatis拦截器实现增删改查参数的加/解密(已上线项目)
mybatis @Intercepts的用法
MyBatis拦截器原理探究
mybatis自定义拦截器拦截sql,处理createTime,updateTime,createBy,updateBy等问题
Mybatis的分表实战
基于mybatis拦截器分表实现
mybatis拦截器实现数据库表水平切分
使用Mybatis拦截器实现数据分表相关推荐
- 基于mybatis拦截器实现数据权限
数据权限是很多系统常见的功能,实现的方式也是很多的,最近在做项目的时候,自己基于mybatis拦截器做了一个数据权限的功能. **功能设计 a) 需要做数据权限功能的表加上一个权限id字段. 权限i ...
- mybatis拦截器实现数据脱敏拦截器使用
目录 1.主要代码如下: 1.注解 2.定义枚举脱敏规则 3.增加mybatis拦截器 4.Javabean中增加注解,如果是查询返回的map,会根据desensitionMap的规则进行脱敏 2.原 ...
- mybatis拦截器实现权限管理
框架设计 基于Spring Security+JWT技术实现登录认证和访问授权,基于Mybatis 拦截器实现数据权限的控制.由于已有前文介绍Spring Security如有兴趣可以查看,在此将重点 ...
- mybatis拦截器开发-分表插件
相关源码已上传至我的github,对应的插件代码在src/main/java/net/dwade/plugins/mybatis目录 https://github.com/huangxfchn/dwa ...
- Mybatis拦截器安全加解密MySQL数据实战
需求背景 公司为了通过一些金融安全指标(政策问题)和防止数据泄漏,需要对用户敏感数据进行加密,所以在公司项目中所有存储了用户信息的数据库都需要进行数据加密改造.包括Mysql.redis.mongod ...
- 使用mybatis plus自定义拦截器,实现数据权限
需求 为了增强程序的安全性,需要在用户访问数据库的时候进行权限判断后选择性进行判断是否需要增强sql,来达到限制低级别权限用户访问数据的目的. 根据业务需要,这里将角色按照数据范围做权限限定.比如,角 ...
- list mybatis 接收 类型_基于mybatis拦截器实现的一款简易影子表自动切换插件
近期因工作需要,小编基于mybatis拦截器开发了一款简易影子表自动切换插件,可以根据配置实现动态修改表名,即将对原source table表的操作自动切换到对target table表的操作.该插件 ...
- 定义Mybatis拦截器动态切换postgre数据库schema
背景 随着业务的发展和合规要求,产品数据库将切换到Postgres.之前不同技术域,不同交付工程的数据分库管理的方式切换到PG数据库后将通过分schema管理. ORM继续使用Mybatis,为使用迁 ...
- MySQL拦截器获取xml id_关于mybatis拦截器,有谁知道怎么对结果集进行拦截,将指定字段查询结果进行格式化...
用MyBatis结果集拦截器做过这样一个需求: 由于项目需求经常变动,项目MySQL数据库都是存放JSON字符串,例如:用户的基本信息随着版本升级可能会有变动 数据表 CREATE TABLE `ac ...
- Mybatis拦截器 mysql load data local 内存流处理
Mybatis 拦截器不做解释了,用过的基本都知道,这里用load data local主要是应对大批量数据的处理,提高性能,也支持事务回滚,且不影响其他的DML操作,当然这个操作不要涉及到当前所lo ...
最新文章
- 解决报错OMP: Error #15: Initializing libiomp5md.dll, but found libiomp5md.dll already initialized.
- 今日代码(200727)--全局空间自相关性
- linux系统下tar/gz/7z/xz/bz2/zip等各种格式的打包压缩解压
- Sitemesh3的使用及配置
- Linux下Shell文件内容替换(sed)(转)
- Leetcode--713. 乘积小于k的子数组
- iOS如何检测app从后台调回前台
- LintCode 820. 矩形
- wpf使用入式mysql_c#之wpf:从mysql数据库中数据绑定到页面上
- Web前端-HTTP Cache-control
- 树莓派 opencv 调用摄像头
- 质因数分解的一些讨论(Pollard-Rho算法)
- Springboot统一异常处理并保存到数据库
- Redis之listpack、rax
- java 穷举_java循环穷举
- python顺时针旋转_python中的绕点旋转(矩阵)
- 如何在openlayers中使用iconfont或font Awesome字体图标
- Python教程(十)--if 实例运用(棒子老虎鸡游戏)
- 取消UL和OL符号以及padding和margin后恢复默认值的CSS
- 头条校招(今日头条2017秋招真题)1——python解法