MyBatis 是一个流行的 Java 持久层框架,它提供了灵活的 SQL 映射和执行功能。有时候我们可能需要在运行时动态地修改 SQL 语句,例如添加一些条件、排序、分页等。MyBatis 提供了一个强大的机制来实现这个需求,那就是拦截器(Interceptor)。
推荐博主开源的 H5 商城项目waynboot-mall,这是一套全部开源的微商城项目,包含三个项目:运营后台、H5 商城前台和服务端接口。实现了商城所需的首页展示、商品分类、商品详情、商品 sku、分词搜索、购物车、结算下单、支付宝/微信支付、收单评论以及完善的后台管理等一系列功能。 技术上基于最新得 Springboot3.0、jdk17,整合了 MySql、Redis、RabbitMQ、ElasticSearch 等常用中间件。分模块设计、简洁易维护,欢迎大家点个 star、关注博主。
github 地址:GitHub - wayn111/waynboot-mall: 这是一套全部开源的微商城项目,包含一个运营后台、H5商城前台和服务端接口。 实现了商城所需的首页展示、商品分类、商品详情、sku详情、商品搜索、购物车、结算下单、商品评论等一系列功能。 技术上基于最新得Springboot3.0、jdk17,整合了MySql、Redis、RabbitMQ、ElasticSearch等常用中间件,代码简单易维护,避免过度封装,欢迎大家点个star、关注博主。
拦截器是一种基于 AOP(面向切面编程)的技术,它可以在目标对象的方法执行前后插入自定义的逻辑。MyBatis 定义了四种类型的拦截器,分别是:
@Intercepts({@Signature(type = StatementHandler.class, method = “prepare”, args = {Connection.class, Integer.class})})
,表示在 SQL 执行之前进行拦截处理。Spring Boot 项目中集成了 Mybatis Plus 后要让拦截器生效很简单,Mybatis Plus 的自动配置类会读取项目中所有注册到 Spring 容器的拦截器并进行自动注册。如下图,
所以我们只需要定义一个 DynamicSqlInterceptor 拦截器并加上 @Component 注解就行,代码如下,
指定 xml 文件中需要替换的占位符标识:@dynamicSql
以及待替换日期条件。
# 动态sql配置
dynamicSql:placeholder: "@dynamicSql"date: "2023-07-10 20:10:30"
在需要进行 SQL 占位符替换的方法上加 @DynamicSql 注解。
public interface DynamicSqlMapper {@DynamicSqlLong count();
}
将日期条件改成占位符 where create_time > @dynamicSql
。
<mapper namespace=dao.DynamicSqlMapper"><select id="count" resultType="java.lang.Long">select count(1) from memberwhere create_time > @dynamicSql</select>
</mapper>
@Component
@Slf4j
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
public class DynamicSqlInterceptor implements Interceptor {@Value("${dynamicSql.placeholder}")private String placeholder;@Value("${dynamicSql.date}")private String dynamicDate;@Overridepublic Object intercept(Invocation invocation) throws Throwable {// 1. 获取 StatementHandler 对象也就是执行语句StatementHandler statementHandler = (StatementHandler) Target();// 2. MetaObject 是 MyBatis 提供的一个反射帮助类,可以优雅访问对象的属性,这里是对 statementHandler 对象进行反射处理,MetaObject metaObject = MetaObject.forObject(statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY,SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY,new DefaultReflectorFactory());// 3. 通过 metaObject 反射获取 statementHandler 对象的成员变量 mappedStatementMappedStatement mappedStatement = (MappedStatement) Value("delegate.mappedStatement");// mappedStatement 对象的 id 方法返回执行的 mapper 方法的全路径名,如dao.UserMapper.insertUserString id = Id();// 4. 通过 id 获取到 Dao 层类的全限定名称,然后反射获取 Class 对象Class<?> classType = Class.forName(id.substring(0, id.lastIndexOf(".")));// 5. 获取包含原始 sql 语句的 BoundSql 对象BoundSql boundSql = BoundSql();String sql = Sql();log.info("替换前---sql:{}", sql);// 拦截方法String mSql = null;// 6. 遍历 Dao 层类的方法for (Method method : Methods()) {// 7. 判断方法上是否有 DynamicSql 注解,有的话,就认为需要进行 sql 替换if (method.isAnnotationPresent(DynamicSql.class)) {mSql = placeAll(placeholder, String.format("'%s'", dynamicDate));break;}}if (StringUtils.isNotBlank(mSql)) {log.info("替换后---mSql:{}", mSql);// 8. 对 BoundSql 对象通过反射修改 SQL 语句。Field field = Class().getDeclaredField("sql");field.setAccessible(true);field.set(boundSql, mSql);}// 9. 执行修改后的 SQL 语句。return invocation.proceed();}@Overridepublic Object plugin(Object target) {// 使用 Plugin.wrap 方法生成代理对象return Plugin.wrap(target, this);}@Overridepublic void setProperties(Properties properties) {// 获取配置文件中的属性值}
}
现在我们对拦截器核心代码逻辑进行讲解:
// 测试类
@SpringBootTest
@RunWith(SpringRunner.class)
public class DynamicTest {@Autowiredprivate DynamicSqlMapper dynamicSqlMapper;@Testpublic void test() {Long count = unt();Null(count, "count不能为null");}
}
执行结果:
2023-07-11 22:13:33.375 [main] INFO fig.DynamicSqlInterceptor - [intercept,52] - 替换前---sql:select count(1) from memberwhere create_time > @dynamicSql
2023-07-11 22:13:33.376 [main] INFO fig.DynamicSqlInterceptor - [intercept,62] - 替换后---mSql:select count(1) from memberwhere create_time > '2023-07-10 20:10:30'
到此本文讲解的 MyBatis 实现动态 SQL 内容就讲解完毕了,希望大家喜欢。
本文发布于:2024-01-28 11:06:47,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/17064112106983.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |