动态拼接SQL实践备忘

2025-12-12 0 366

背景介绍

最近工作接到一个荒诞的任务,实际情况比较复杂,总结一下有以下特点

  • 返回的字段如幽灵般游移不定
  • 目标表仿佛在迷雾中随每次请求变化
  • 过滤条件无规则无限制任意搭配

Mybatis的XML模板沉默以对,Mybatis-Plus的Wrapper亦束手无策。这让我想起 Navicat 的“筛选”功能,感觉两者有些相似,只不过Navicat场景更加简单一些。

动态拼接SQL实践备忘
于是,在荒诞的尽头,笔者做出了一个近乎宿命的选择:亲手拼接SQL——不是出于信念,而是因为别无选择。在这片逻辑崩塌的废墟上,唯有将字符串缝合成一句句临时的祷词,交付给数据库那沉默而全能的神谕。一旦接受了这种徒劳的合理性,道路便在虚无中浮现。

JdbcTemplate

那是来自Spring最原始的呼唤,一种近乎本能的回归。在框架层层叠叠的抽象迷宫中迷失之后,我听见了它低沉而直接的声音:没有装饰,没有隐喻,只有queryForMapqueryForList赤裸裸地横亘于代码之中。我们熟悉它,正如熟悉自己的双手。

public class JdbcTemplate extends JdbcAccessor
    implements JdbcOperations {
    
    /* 获取单条记录 */
    @Override
    public Map queryForMap(String sql)
        throws DataAccessException {
        return result(queryForObject(sql, getColumnMapRowMapper()));
    }
    
    /* 获取多条记录 */
    @Override
    public List<Map> queryForList(String sql) 
        throws DataAccessException {
        return query(sql, getColumnMapRowMapper());
    }

}

然而这朴素之中藏着代价。它不够优雅,更谈不上智能。而当多数据源的阴影悄然降临,它便显露出更深的无力:每一次切换,都像在荒原上重新挖一口井,徒增重复、耦合与混乱。于是,这份最初的慰藉,终究在现实的重压下显出裂痕——它能承载希望,却无法驯服复杂。

@SelectProvider

后来,我遇见了 SelectProvider——它披着优雅的外衣,携带着一种近乎诗意的承诺。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Repeatable(SelectProvider.List.class)
/* 该注解允许定制多种选项,平常使用这两个即可 */
public @interface SelectProvider {

    /* 填写生产SQL语句的类 */
    Class type() default void.class;
    
    /* 填写生产SQL语句的方法名 */
    String method() default \"\";

}

只需在方法上轻轻标注,指明谁将为SQL赋形、何处孕育语句,MyBatis 便会如虔诚的信使,自动召唤那段动态生成的咒语,并将其送入数据库的圣殿。代码简洁,结构清晰,仿佛终于在这查询迷局中,找到了一丝理性的秩序。

/**
 * @author Raphael
 * @since 2025-11-19 15:21
 */
@DS(\"report\")
@Mapper
public interface TestMapper {

    @SelectProvider(type = SqlProvider.class, method = \"findById\")
    Map findById(String tbl, String id);
  
    /* SQL语句提供类 */
    static class SqlProvider {

        /* 提供语句的方法 */
        public static String findById(String tbl, String id) {
            return \"select * from \" + tbl + \" where id = \" + id;
        }
        
    }
    
}

然而,这优雅之下潜伏着古老的危险:若对输入不加审视,任其流入拼接的缝隙,SQL 注入便如幽灵般悄然附体——一句恶意的字符串,足以撕裂整个系统的防线。因此,我不得不低声警告:凡使用此道者,必先以转义为盾,以校验为矛,方可在自由与安全之间行走。

/**
 * @author Raphael
 * @since 2025-11-19 15:21
 */
@DS(\"report\")
@Mapper
public interface TestMapper {

    @SelectProvider(type = SqlProvider.class, method = \"getById\")
    Map getById(String tbl, String id);
  
    /* SQL语句提供类 */
    static class SqlProvider {

        /* 提供语句的方法 */
        public static String getById(@Param(\"tbl\")String tbl, @Param(\"id\") String id) {
            return new SQL()
                .SELECT(\"*\")
                .FROM(\"tbl = #{tbl}\")
                .WHERE(\"id = #{id}\")
                .toString();
        }
        
    }
    
}

更令人踟蹰的是性能之问。有人提议缓存生成的 SQL,以避重复构造之耗。可这念头本身便陷入悖论:若 SQL 真能被缓存,那意味着其形式已然趋于稳定;

private static final String SQL =
    new SQL()
        .SELECT(\"*\")
        .FROM(\"#{tbl}\")
        .WHERE(\"id = #{id}\")
        .toString();

public static String getById(@Param(\"tbl\")String tbl, @Param(\"id\") String id) {
    return SQL;
}

而若形式稳定,又何须动用SelectProvider这般为混沌而生的机制?这就像试图为风塑形,再宣称它的轮廓可以复用。

总结

回望整个探索过程,从 JdbcTemplate 的直白拼接到 SelectProvider 的动态封装,每一种方案都像是在不确定性的迷宫中点亮一盏临时的灯——足够照亮脚下的路,却照不透整座建筑的结构。
工具没有对错,只有适配与否;而所谓“优雅”或“原始”,往往只是我们面对复杂时情绪的投射。真正关键的,是在开放性与安全性、灵活性与性能之间,做出清醒的权衡,并为自己的选择负责。
最终,代码不只是逻辑的堆砌,更是判断的痕迹。我选择接受它的不完美,并用克制的拼接、谨慎的校验和明确的边界,为系统保留最后一道理性的防线。

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

申明:本文由第三方发布,内容仅代表作者观点,与本网站无关。对本文以及其中全部或者部分内容的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。本网发布或转载文章出于传递更多信息之目的,并不意味着赞同其观点或证实其描述,也不代表本网对其真实性负责。

左子网 编程相关 动态拼接SQL实践备忘 https://www.zuozi.net/36042.html

常见问题
  • 1、自动:拍下后,点击(下载)链接即可下载;2、手动:拍下后,联系卖家发放即可或者联系官方找开发者发货。
查看详情
  • 1、源码默认交易周期:手动发货商品为1-3天,并且用户付款金额将会进入平台担保直到交易完成或者3-7天即可发放,如遇纠纷无限期延长收款金额直至纠纷解决或者退款!;
查看详情
  • 1、描述:源码描述(含标题)与实际源码不一致的(例:货不对板); 2、演示:有演示站时,与实际源码小于95%一致的(但描述中有”不保证完全一样、有变化的可能性”类似显著声明的除外); 3、发货:不发货可无理由退款; 4、安装:免费提供安装服务的源码但卖家不履行的; 5、收费:价格虚标,额外收取其他费用的(但描述中有显著声明或双方交易前有商定的除外); 6、其他:如质量方面的硬性常规问题BUG等。 注:经核实符合上述任一,均支持退款,但卖家予以积极解决问题则除外。
查看详情
  • 1、左子会对双方交易的过程及交易商品的快照进行永久存档,以确保交易的真实、有效、安全! 2、左子无法对如“永久包更新”、“永久技术支持”等类似交易之后的商家承诺做担保,请买家自行鉴别; 3、在源码同时有网站演示与图片演示,且站演与图演不一致时,默认按图演作为纠纷评判依据(特别声明或有商定除外); 4、在没有”无任何正当退款依据”的前提下,商品写有”一旦售出,概不支持退款”等类似的声明,视为无效声明; 5、在未拍下前,双方在QQ上所商定的交易内容,亦可成为纠纷评判依据(商定与描述冲突时,商定为准); 6、因聊天记录可作为纠纷评判依据,故双方联系时,只与对方在左子上所留的QQ、手机号沟通,以防对方不承认自我承诺。 7、虽然交易产生纠纷的几率很小,但一定要保留如聊天记录、手机短信等这样的重要信息,以防产生纠纷时便于左子介入快速处理。
查看详情

相关文章

猜你喜欢
发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务