mybatis拦截器 | 张扎瓦的博客

mybatis拦截器

使用mybatis自定义sql拦截器实现分表


背景介绍

有这么一个场景,有一套文件上传系统,多个用户上传文件时,会记录用户的上传文件位置和文件的其他信息。

文件上传记录表结构如下:

字段编码 字段说明
id 主键
user_code 用户编码
file_name 文件名
file_path 文件存储路径
…… ……

隐藏的问题

上面的这种表结构,在存数据时不会有什么太大的问题,但是当数据量非常大的时候,查询速度会变慢。

解决思路

通过 用户编码_表名 的方式来分表,将单张表的数据,拆分成多张表。在查询的时候,动态拼接表名。

具体写法

创建一个类,实现org.apache.ibatis.plugin.Interceptor接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class MyBatisInterceptor implements Interceptor {
/**
* 存放需要拦截的表
*/
private Set<String> tables = new HashSet<>();

/**
* 拦截sql方法
*/
@Override
public Object intercept(Invocation invocation) throws Throwable {
StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
BoundSql boundSql = statementHandler.getBoundSql();

// 获取sql请求参数
Object parameterObject = statementHandler.getParameterHandler().getParameterObject();
Map<String, Object> parameter = MapUtils.objectToMap(parameterObject);

/*MetaObject metaObject = MetaObject.forObject(statementHandler,
SystemMetaObject.DEFAULT_OBJECT_FACTORY,
SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY,
new DefaultReflectorFactory());

MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
//id为执行的mapper方法的全路径名,如com.zhangjava.UserDao.insertUser
String id = mappedStatement.getId();
//sql语句类型 select、delete、insert、update
String sqlCommandType = mappedStatement.getSqlCommandType().toString();
System.out.println(sqlCommandType);*/

//获取到原始sql语句
String sql = boundSql.getSql();

// 获取用户编码
String userCode = getUserCode(parameter);
for (String table : tables) {
// 替换表名
sql = sql.replaceAll(table, userCode + "_" + table);
}

//通过反射修改sql语句
Field field = boundSql.getClass().getDeclaredField("sql");
field.setAccessible(true);
field.set(boundSql, sql);
return invocation.proceed();
}

@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}

/**
* 获取配置文件配置的属性
*
* @param properties
*/
@Override
public void setProperties(Properties properties) {
String tableStr = properties.getProperty("tables");
if (StringUtils.isNotBlank(tableStr)) {
String[] split = tableStr.split(",");
for (String s : split) {
s = s.trim();
if (StringUtils.isNotBlank(s)) {
tables.add(s);
}
}
}
}
}

修改mybatis的配置文件,增加如下配置

1
2
3
4
5
6
<!-- sql拦截器 按照用户编码切表,表名小写,多个表使用英文,分割 -->
<plugins>
<plugin interceptor="com.zhangjava.conf.interceptor.MyBatisInterceptor">
<property name="tables" value="upload_audio"/>
</plugin>
</plugins>

这样在写sql时,不需要传入参数去拼接表,直接通过拦截器就可以动态拼接表名。

如果我的文章对您有所帮助,不妨打赏一杯豆浆以资鼓励(○` 3′○)