怎样优雅地增删查数据结构就下面几个改(三):业务用户的增删查改

从IR Drop到EM分析:数字IC电源规划中那些没人明说的细节(以MTCMOS配置为例) 本文深入探讨数字IC后端设计中电源规划的关键细节,特别关注MTCMOS配置、电源网络电阻最小化和level shifter供电等实战技巧。通过分析IR Drop和EM问题,提供优化floorplan和powerplan的具体策略,帮助工程师避免常见陷阱,提升芯片设计的稳定性和可靠性。 阅读详情

简介

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 定义了四种类型的拦截器,分别是:

  • Executor:拦截执行器的方法,例如 update、query、commit、rollback 等。可以用来实现缓存、事务、分页等功能。
  • ParameterHandler:拦截参数处理器的方法,例如 setParameters 等。可以用来转换或加密参数等功能。
  • ResultSetHandler:拦截结果集处理器的方法,例如 handleResultSets、handleOutputParameters 等。可以用来转换或过滤结果集等功能。
  • StatementHandler:拦截语句处理器的方法,例如 prepare、parameterize、batch、update、query 等。可以用来修改 SQL 语句、添加参数、记录日志等功能。

实现拦截器

  1. 定义一个实现 org.apache.ibatis.plugin.Interceptor 接口的拦截器类,并重写其中的 intercept、plugin 和 setProperties 方法。
  2. 添加 @Intercepts 注解,写上需要拦截的对象和方法,以及方法参数,例如 @Intercepts({@Signature(type = StatementHandler.class, method = “prepare”, args = {Connection.class, Integer.class})}),表示在 SQL 执行之前进行拦截处理。

注册拦截器

Spring Boot 项目中集成了 Mybatis Plus 后要让拦截器生效很简单,Mybatis Plus 的自动配置类会读取项目中所有注册到 Spring 容器的拦截器并进行自动注册。如下图,

所以我们只需要定义一个 DynamicSqlInterceptor 拦截器并加上 @Component 注解就行,代码如下,

代码示例

yml 配置

指定 xml 文件中需要替换的占位符标识:@dynamicSql 以及待替换日期条件。

# 动态sql配置
dynamicSql:
  placeholder: "@dynamicSql"
  date: "2023-07-10 20:10:30"

Dao 层代码

在需要进行 SQL 占位符替换的方法上加 @DynamicSql 注解。

public interface DynamicSqlMapper {
    @DynamicSql
    Long count();
}

mapper 文件

将日期条件改成占位符 where create_time > @dynamicSql

<mapper namespace="ltd.newbee.mall.core.dao.DynamicSqlMapper">
    <select id="count" resultType="java.lang.Long">
        select count(1) from member
        where 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;

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 1. 获取 StatementHandler 对象也就是执行语句
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        // 2. MetaObject 是 MyBatis 提供的一个反射帮助类,可以优雅访问对象的属性,这里是对 statementHandler 对象进行反射处理,
        MetaObject metaObject = MetaObject.forObject(statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY,
                SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY,
                        new DefaultReflectorFactory());
        // 3. 通过 metaObject 反射获取 statementHandler 对象的成员变量 mappedStatement
        MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
        // mappedStatement 对象的 id 方法返回执行的 mapper 方法的全路径名,如ltd.newbee.mall.core.dao.UserMapper.insertUser
        String id = mappedStatement.getId();
        // 4. 通过 id 获取到 Dao 层类的全限定名称,然后反射获取 Class 对象
        Class<?> classType = Class.forName(id.substring(0, id.lastIndexOf(".")));
        // 5. 获取包含原始 sql 语句的 BoundSql 对象
        BoundSql boundSql = statementHandler.getBoundSql();
        String sql = boundSql.getSql();
        log.info("替换前---sql:{}", sql);
        // 拦截方法
        String mSql = null;
        // 6. 遍历 Dao 层类的方法
        for (Method method : classType.getMethods()) {
            // 7. 判断方法上是否有 DynamicSql 注解,有的话,就认为需要进行 sql 替换
            if (method.isAnnotationPresent(DynamicSql.class)) {
                mSql = sql.replaceAll(placeholder, String.format("'%s'", dynamicDate));
                break;
            }
        }
        if (StringUtils.isNotBlank(mSql)) {
            log.info("替换后---mSql:{}", mSql);
            // 8. 对 BoundSql 对象通过反射修改 SQL 语句。
            Field field = boundSql.getClass().getDeclaredField("sql");
            field.setAccessible(true);
            field.set(boundSql, mSql);
        }
        // 9. 执行修改后的 SQL 语句。
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        // 使用 Plugin.wrap 方法生成代理对象
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
        // 获取配置文件中的属性值
    }
}

现在我们对拦截器核心代码逻辑进行讲解:

  1. 通过 invocation 参数获取 statementHandler 对象,也就是包含拼接后 SQL 语句的对象。
  2. 获取 metaObject 对象, MetaObject 是 MyBatis 提供的一个反射帮助类,可以优雅访问对象的属性,这里是访问 statementHandler 对象进行反射处理。
  3. 通过 metaObject 反射获取 statementHandler 对象的成员变量 mappedStatement。
  4. 通过 mappedStatement 对象的 id 方法获取到 Dao 层类的全限定名称,然后反射获取 Dao 层类的 Class 对象。
  5. 获取包含原始 SQL 语句的 BoundSql 对象。
  6. 遍历 Dao 层类的方法。
  7. 判断方法上是否有 DynamicSql 注解,有的话就进行时间条件替换
  8. 对 BoundSql 对象通过反射修改 SQL 语句。
  9. 执行修改后的 SQL 语句。

代码测试

// 测试类
@SpringBootTest
@RunWith(SpringRunner.class)
public class DynamicTest {

    @Autowired
    private DynamicSqlMapper dynamicSqlMapper;

    @Test
    public void test() {
        Long count = dynamicSqlMapper.count();
        Assert.notNull(count, "count不能为null");
    }
}

执行结果:

2023-07-11 22:13:33.375 [main] INFO  l.n.m.config.DynamicSqlInterceptor - [intercept,52] - 替换前---sql:select count(1) from member
        where create_time > @dynamicSql
2023-07-11 22:13:33.376 [main] INFO  l.n.m.config.DynamicSqlInterceptor - [intercept,62] - 替换后---mSql:select count(1) from member
        where create_time > '2023-07-10 20:10:30'

拦截器应用场景

  • SQL 语句执行监控:可以拦截执行的 SQL 方法,打印执行的 SQL 语句、参数等信息,并且还能够记录执行的总耗时,可供后期的 SQL 分析时使用。
  • SQL 分页查询:MyBatis 中使用的 RowBounds 使用的内存分页,在分页前会查询所有符合条件的数据,在数据量大的情况下性能较差。通过拦截器,可以在查询前修改 SQL 语句,提前加上需要的分页参数。
  • 公共字段的赋值:在数据库中通常会有 createTime , updateTime 等公共字段,这类字段可以通过拦截统一对参数进行的赋值,从而省去手工通过 set 方法赋值的繁琐过程。
  • 数据权限过滤:在很多系统中,不同的用户可能拥有不同的数据访问权限,例如在多租户的系统中,要做到租户间的数据隔离,每个租户只能访问到自己的数据,通过拦截器改写 SQL 语句及参数,能够实现对数据的自动过滤。
  • SQL 语句替换:对 SQL 中条件或者特殊字符进行逻辑替换。(也是本文的应用场景)

总结

到此本文讲解的 MyBatis 实现动态 SQL 内容就讲解完毕了,希望大家喜欢。

AutoSAR系列讲解(深入篇)13.6-闪烁一颗LED灯 6 闪烁一颗LED灯:配置SWC和生成代码 阅读详情

相关推荐

ruoyi-nbcio-plus基于vue3的flowable服务任务的升级修

ruoyi-nbcio-plus基于vue3的flowable服务任务的升级修

宁波阿成的博客 810

怎样优雅增删查改):业务用户增删查数据结构下面几个

可变化、可扩展

hijoppo58745的博客 915

高通平台 Android T 蓝牙技术全解析:A2DP 与 AVRCP 核心代码路径与交互流程

A2DP 接收端 - 连接流程(AVDTP 打开)

专业开发者的博客,一个从事Android WiFi和蓝牙的工程师 265

怎样优雅增删查改数据结构下面几个):业务用户增删查改

可变化、可扩展

KOPL956899的博客 1157

怎样优雅增删查改实现事务的原子性():业务用户增删查改

赋能技术

ioq26666s的博客 611

怎样优雅增删查改数据结构下面几个):业务用户增删查改

传统关系型数据库

uuhj66663322的博客 199

怎样优雅增删查改):业务用户增删查改

区别于身份管理模块(Identity模块)的鉴权用户IdentityUser,业务用户(BusinessUser)是围绕业务系统中“用户”这一定义的领域模型。这些业务用户继承自HealthUser,HealthUser是业务用户的基类,包含了业务用户的基本信息,如姓名,性别,出生日期,身份证号等。以Client为例,ClientLookupService是业务用户的查询服务,其基类UserLookupService定义了关联用户的查询接口,包括按ID查询,按用户名查询,按组织架构查询,按户关系查询等。

jevonsflash的专栏 699

怎样优雅增删查改):业务用户数据结构下面几个增删查改

会随机

HUKI9985633的博客 564

怎样优雅增删查改数据结构下面几个业务用户增删查改

相当于利用泛型函数保存了类型信息

2301_79100267的博客 183

怎样优雅增删查改):业务实现对业务用户用户增删查改

无需重写

2301_79100037的博客 237

怎样优雅增删查改):业务用户的新增数据源增删查改

随鼠标或触摸移动

LPL98785566的博客 1725

怎样优雅地增删数据结构下面几个):业务用户增删查改

数据结构下面几个

2301_78942864的博客 196

怎样优雅增删查改):业务用户增删查改目标场景

同步器将更新业务用户的基本信息

yyuqoq9966333的博客 584

怎样优雅增删查改):业务用户增删查会随机

当鉴权用户更新时

hjk96556661的博客 897

怎样优雅增删查改):业务会随机用户增删查改

无需重写

luohua98555的博客 1100

怎样优雅增删查改):业务用户数据结构下面几个增删查改

随鼠标或触摸移动

plop98998811的博客 153

怎样优雅增删查改):业务用户的增数据结构下面几个删查

实现对业务用户

kolo96542211的博客 195

PostgreSQL笔记48: 全文检索与向量检索的对比与混合检索实践

本文介绍了信息检索技术的演进历程,从关键词匹配、全文检索到语义检索(向量检索),重点分析了PostgreSQL中全文检索和向量检索的实现机制。全文检索通过tsvector/tsquery数据类型、GIN索引和ts_rank函数实现高效检索;向量检索则借助pgvector扩展,使用Embedding模型和相似度计算进行语义搜索。文章对比了两种技术的优缺点,并提出了混合检索策略,结合全文检索的精确性和向量检索的语义理解能力。最后提供了完整的SQL示例,展示了如何在PostgreSQL中实现高效、精准、智能的搜索

Wang的专栏 518

ISO 21498-2-2024.pdf关于电动车辆(EV)和混合动力车辆(HEV)电池系统 功能安全

基于 ISO 26262(道路车辆功能安全标准),规定电池管理系统(BMS)的功能安全要求,涵盖硬件和软件的开发流程,确保电池在故障时仍能安全运行。

MLFviewer 军校数字图书 MLF 浏览器 分包1

MLFviewer 军校数字图书浏览器 比较难找的浏览器 文件较大,分了两个包

上一篇: 【技术积累】Mysql中的SQL语言【技术篇】【6】设置为单例注入
万里归途2
博客等级 码龄3年 26粉丝 20原创
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值