Spring Boot集成MyBatis-Plus 自定义拦截器实现动态表名切换功能
Spring Boot集成MyBatis-Plus:自定义拦截器实现动态表名切换
一、引言
介绍动态表名的场景需求,比如多租户系统、分表分库,或者不同业务模块共用一套代码但操作不同表。说明 MyBatis-Plus 默认绑定固定表名的问题。
二、项目配置
1. 集成 MyBatis-Plus
简单说明如何在 Spring Boot 中引入 MyBatis-Plus 并配置。
2. 依赖添加
<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>最新版本号</version> </dependency>
三、自定义拦截器实现动态表名
1. 拦截器原理
解释 MyBatis 拦截器的核心概念,介绍 Interceptor
接口和 @Signature
注解。
2. 拦截器实现代码
详细展示拦截器的完整实现:
@Component @Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})}) public class DynamicTableInterceptor implements Interceptor { /** * 使用ThreadLocal来存储线程特有的数据,这里用于存储动态表名 */ private static final ThreadLocal<String> TABLE_NAME_HOLDER = new ThreadLocal<>(); /** * 设置动态表名 * * @param tableName 需要设置的表名,由调用者指定 * 此方法允许在运行时动态地设置数据库表名,以便在多数据源或动态表名的场景下灵活地切换表 */ public static void setDynamicTableName(String tableName) { TABLE_NAME_HOLDER.set(tableName); } /** * 清除当前线程中的动态表名 * 此方法用于清除ThreadLocal中存储的表名信息,以避免内存泄漏 * 它应在每次使用动态表名后调用,确保不会对后续的请求产生影响 */ public static void clearDynamicTableName() { TABLE_NAME_HOLDER.remove(); } /** * 拦截器 */ @Override public Object intercept(Invocation invocation) throws Throwable { StatementHandler handler = (StatementHandler) invocation.getTarget(); BoundSql boundSql = handler.getBoundSql(); String tableName = TABLE_NAME_HOLDER.get(); if (tableName != null) { /** * 从实体类的@tableName中获取表名,必须在实体类上面添加@tableName注解 * 实体类中的@tableName注解中的值,必须是数据库中的表名,否则会报错 * 例如 :@TableName("t_user_"),数据库是根据月份来的,在这里替换则需要根据当前月份进行拼接表名 */ Class<?> entityType = boundSql.getParameterObject().getClass(); String oldTableName = entityType.getAnnotation(TableName.class).value(); String newSql = boundSql.getSql().replace(oldTableName, tableName); Field sqlField = boundSql.getClass().getDeclaredField("sql"); sqlField.setAccessible(true); sqlField.set(boundSql, newSql); } return invocation.proceed(); } }
3. 使用拦截器动态设置表名
DynamicTableInterceptor.setDynamicTableName("your_dynamic_table_name"); try { myService.saveOrUpdateBatch(entities); // MyBatis-Plus 操作 } finally { DynamicTableInterceptor.clearDynamicTableName(); }
四、其他场景
MyBatis 拦截器(Interceptor)是 MyBatis 提供的强大扩展机制,可以拦截执行过程中的不同阶段并进行自定义操作。除了动态修改表名之外,拦截器还可以应用于以下多种场景:
1. SQL 日志记录与审计
- 场景:记录每次执行的 SQL 语句、参数、执行时间等信息。
- 用途:用于 SQL 审计、性能监控、问题排查。
示例:
@Override public Object intercept(Invocation invocation) throws Throwable { StatementHandler handler = (StatementHandler) invocation.getTarget(); BoundSql boundSql = handler.getBoundSql(); long startTime = System.currentTimeMillis(); Object result = invocation.proceed(); long endTime = System.currentTimeMillis(); System.out.println("SQL: " + boundSql.getSql() + " Execution Time: " + (endTime - startTime) + "ms"); return result; }
2. 多租户数据隔离
- 场景:根据当前租户信息自动添加过滤条件,确保数据隔离。
- 用途:实现 SaaS 系统中不同租户的数据访问控制。
示例:
@Override public Object intercept(Invocation invocation) throws Throwable { StatementHandler handler = (StatementHandler) invocation.getTarget(); BoundSql boundSql = handler.getBoundSql(); String originalSql = boundSql.getSql(); String tenantId = TenantContext.getCurrentTenantId(); String modifiedSql = originalSql + " WHERE tenant_id = " + tenantId; Field sqlField = boundSql.getClass().getDeclaredField("sql"); sqlField.setAccessible(true); sqlField.set(boundSql, modifiedSql); return invocation.proceed(); }
3. SQL 参数加密与解密
- 场景:对敏感数据(如身份证号、电话等)在 SQL 操作时进行自动加密或解密。
- 用途:提高数据安全性,确保数据存储符合安全规范。
示例:
@Override public Object intercept(Invocation invocation) throws Throwable { StatementHandler handler = (StatementHandler) invocation.getTarget(); BoundSql boundSql = handler.getBoundSql(); String sql = boundSql.getSql(); // 自定义加密逻辑 // 加密处理参数 return invocation.proceed(); }
4. 自动分页处理
- 场景:在执行查询时自动添加分页逻辑,避免手动分页处理。
- 用途:简化分页查询的代码。
示例:
@Override public Object intercept(Invocation invocation) throws Throwable { StatementHandler handler = (StatementHandler) invocation.getTarget(); BoundSql boundSql = handler.getBoundSql(); String originalSql = boundSql.getSql(); String paginatedSql = originalSql + " LIMIT " + offset + ", " + limit; Field sqlField = boundSql.getClass().getDeclaredField("sql"); sqlField.setAccessible(true); sqlField.set(boundSql, paginatedSql); return invocation.proceed(); }
5. 数据权限控制
- 场景:根据用户角色或权限,自动添加数据过滤条件。
- 用途:确保用户只能访问被授权的数据。
示例:
@Override public Object intercept(Invocation invocation) throws Throwable { StatementHandler handler = (StatementHandler) invocation.getTarget(); BoundSql boundSql = handler.getBoundSql(); String originalSql = boundSql.getSql(); String userRole = SecurityContext.getCurrentUserRole(); String restrictedSql = originalSql + " AND role = '" + userRole + "'"; // 动态修改 SQL return invocation.proceed(); }
6. 缓存增强
- 场景:自定义缓存逻辑,拦截查询请求,先检查缓存是否命中。
- 用途:减少数据库访问次数,提高性能。
示例:
@Override public Object intercept(Invocation invocation) throws Throwable { // 检查缓存 Object cachedResult = CacheManager.get(boundSql.getSql()); if (cachedResult != null) { return cachedResult; } // 如果没有命中,执行查询 Object result = invocation.proceed(); // 存入缓存 return result; }
7. 动态数据源切换
场景:根据不同的业务需求动态选择数据源。
用途:实现读写分离或多数据库支持。
示例:
@Override public Object intercept(Invocation invocation) throws Throwable { String dataSourceKey = DataSourceContextHolder.getDataSourceKey(); DynamicDataSource.setDataSourceKey(dataSourceKey); return invocation.proceed(); }
总结
MyBatis 拦截器为开发者提供了灵活的扩展能力,可以在 SQL 执行的多个阶段中注入自定义逻辑,从而实现多种高级功能。合理使用拦截器不仅能增强系统功能,还能提升性能和安全性。
到此这篇关于Spring Boot集成MyBatis-Plus 自定义拦截器实现动态表名切换功能的文章就介绍到这了,更多相关Spring Boot MyBatis-Plus 动态表名切换内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
springboot中关于classpath:路径使用及说明
这篇文章主要介绍了springboot中关于classpath:路径使用及说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教2022-09-09简单的理解java集合中的HashSet和HashTree几个重写方法
这篇文章主要介绍了简单的理解java集合中的HashSet和HashTree几个重写方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧2020-10-10使用mybatis的typeHandler对clob进行流读写方式
这篇文章主要介绍了使用mybatis的typeHandler对clob进行流读写方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教2022-01-01
最新评论