V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Joker123456789
V2EX  ›  Java

其实,我更喜欢写 SQL

  •  
  •   Joker123456789 · 6 天前 · 6889 次点击

    此文章充满了个人的主观色彩,如果引起了大家的不适,那我也没办法。

    其实,我更喜欢写 SQL ,如果在此基础上再稍微方便一些就更好了,所以,我理想中的持久层应该是这样的。

    对于单表的增删改查

    由于它不需要各种 join ,所以我们关心的只不过是字段,参数和条件而已,所以必须要有一种方式让我们只需要关注这三点,不需要去写那些固定模式的 SQL ,比如这样。

    ParamPO paramPO = new ParamPO();
    paramPO.setUserName("a");
    paramPO.setUserEmail("[email protected]");
    
    int result = MagicDBUtils.get(jdbcTemplate).insert("表名", paramPO);
    

    又或者这样

    / 构建查询条件
    ConditionBuilder conditionBuilder = ConditionBuilder.createCondition()
                .add("id > ?", 10)
                .add("and (name = ? or age > ?)", "bee", 10)
                .add("order by create_time", Condition.NOT_WHERE);
    
    // 执行查询
    List<ParamPO> result = MagicDBUtils.get(jdbcTemplate).select("表名", conditionBuilder, ParamPO.class);
    

    注意看上面的代码示例,他跟现有的框架有什么区别?答案就在这行

    .add("and (name = ? or age > ?)", "bee", 10)
    

    现有的框架如果要实现这样的条件是一件很头疼的事,而我们可以直接把查询条件写出来,不需要去 set 一堆对象。

    其他框架只能很方便的实现这种(也许是我孤陋寡闻,如果说错了欢迎大家来拍砖)

    .add("id > ?", 10)
    

    这就是其他框架的写法,不仅没有我们灵活,而且还不够直观,需要能一眼看懂方法名是什么意思。大家可以把这段代码跟上面的那段比一比,哪段更直观简直不言而喻。

    ImsCardGoodsExample.Criteria criteria = cardGoodsExample.createCriteria()
                    .andIccidEqualTo(iccid) // 需要看懂英文 equal
                    .andEndTimeLessThanOrEqualTo(new Date()); // 至于这句是什么意思?到底是>=还是<=,别装了,英文很好的程序员占比真不大
    

    对于其他操作

    由于需要各种统计,函数,join ,这个时候无论代码设计的多么出色都不可能有 SQL 灵活好用,而且我们几乎都会在 navicat 等各种客户端里写一遍 SQL ,验证成功了才会把他应用到程序里去。所以在这个场景下我个人认为没有什么方式比把 SQL 直接拷贝到程序里更方便的方式了,所以他必须能很友好的支持原生 SQL 。

    比如查询

    ParamPO paramPO = new ParamPO();
    paramPO.setId(5);
    paramPO.setUserName("a");
    
    // 采用{}占位符的写法
    List<ParamPO> result = MagicDBUtils.get(jdbcTemplate).selectList("select * from xt_message_board where id > {id} and user_name != {user_name}", paramPO, ParamPO.class);
    
    // 采用 ? 占位符的写法
    List<ParamPO> result = MagicDBUtils.get(jdbcTemplate).selectList("select * from xt_message_board where id > ? and user_name != ?", new Object[]{5, "a"}, ParamPO.class);
    

    又或者增删改

    ParamPO paramPO = new ParamPO();
    paramPO.setUserName("testTx222");
    paramPO.setUserEmail("[email protected]");
    paramPO.setId(4);
    
    // 采用{}占位符的写法
    int result = MagicDBUtils.get(jdbcTemplate).exec("update xt_message_board set user_name = {user_name} , user_email = {user_email} where id = {id}", paramPO);
    
    // 采用 ? 占位符的写法
    int result = MagicDBUtils.get(jdbcTemplate).exec("update xt_message_board set user_name = ? , user_email = ? where id = ?", new Object[]{"testTx222","[email protected]", 4});
    

    分个页而已,有没有必要引入三方插件啊?

    简简单单一句话搞定,当然了,只支持 mysql 哈,为什么不支持别的?因为我懒(其实是大部分公司都不舍得买 oracle ,db2 等数据库,所以我觉得这个懒可以偷)

    // 查询条件
    ParamPO paramPO = new ParamPO();
    paramPO.setId(5);
    paramPO.setUserName("a");
    
    // 查询参数
    PageParamModel pageParamModel = new PageParamModel();
    pageParamModel.setCurrentPage(1);
    pageParamModel.setPageSize(10);
    pageParamModel.setParam(paramPO);// 把查询条件 set 进去
    
    // 使用默认 countSql 查询
    PageModel<ParamPO> pageModel =  MagicDBUtils.get(jdbcTemplate).selectPage("select * from xt_message_board where id > {id} and user_name != {user_name}", pageParamModel, ParamPO.class);
    
    // 使用自定义 countSql 查询
    String countSql = "自己定义 countSql";
    
    PageModel<ParamPO> pageModel =  MagicDBUtils.get(jdbcTemplate).selectPageCustomCountSql("select * from xt_message_board where id > {id} and user_name != {user_name}", countSql, pageParamModel, ParamPO.class);
    

    SQL 写在代码里很难看?

    现在已经 2024 年的年底了,90 年出生的程序员都已经达到 35 岁退休的年龄了,所以不要再守着 JDK8 过日子了,试一试 JDK17

    String sql = """
                 select 
                     id,name,age,create_time
                 from 
                     user_info
                 where 
                     name = {name} and age > {age}
                """;
    

    最重要的是

    它只不过是对 Spring 的 JdbcTemplate 做了一个小小的扩展,也就是这玩意儿

    @Resource
    private JdbcTemplate jdbcTemplate;
    

    所以稳定性,各方面都不用担心,而且使用起来超级方便,也就是说你只需要在 https://start.springboot.io 网站建立一个 springboot 项目,然后再添加一个依赖就好了,不需要去查阅 mybaits 怎么整合,分页插件怎么整合等一堆事。

    所以你们打算尝试一下吗?

    项目官网:https://magician-io.com

    第 1 条附言  ·  6 天前
    补充一句非常主观的话,我真的非常不喜欢去整合其他框架,就喜欢用 JdbcTemplate 一把梭,但它也有它不方便的地方,所以我就稍微增强了一下,仅此而已。

    大家也不要把他去当做一个持久层框架来看待,他就是一个 JdbcTemplate 的扩展包,而且是非常轻微的扩展。
    67 条回复    2024-11-26 12:22:51 +08:00
    mumbler
        1
    mumbler  
       6 天前
    现在都让 AI 写了,我指挥监督一下就行了,才不去关心那些技术细节
    Belmode
        2
    Belmode  
       6 天前
    ```java
    // 新增 Row 构建
    DbChain.table("tb_account")
    .setId(RowKey.AUTO)
    .set("user_name","zhangsan")
    .set("age",18)
    .set("birthday",new Date())
    .save();

    // 查询 QueryWrapper 构建
    DbChain.table("tb_account")
    .select("id","user_name","age","birthday")
    .where("age > ?",18)
    .list()
    .forEach(System.out::println);
    ```
    别人的花样更多。https://mybatis-flex.com/zh/base/db-row.html
    java ORM 这块就别再卷了,已经到了极限了,java 语法限制了没办法做那些函数式语言做 DSL 的便利性。
    COW
        3
    COW  
       6 天前 via Android
    跟 Mybatis 的 @Provider 写法比有什么优势?
    Joker123456789
        4
    Joker123456789  
    OP
       6 天前
    @Belmode 谢谢分享,不过我真的没卷,你仔细看一下就知道了,除了单表我几乎就没玩任何花样,就像标题写的那样《其实,我更喜欢写 SQL 》,这绝不是一句引流的话,而是真心话
    Joker123456789
        5
    Joker123456789  
    OP
       6 天前
    @COW 这个工具本身就不是为了打 mybatis 或者其他任何现有的框架,而是给 SQL 党一个非常小型的选择而已。他其实就是对 Spring 的 JdbcTemplate 的扩展,就做了两件事,一个是单表操作的无 sql 化,一个是 sql 里面支持{}占位符,其他也没了。

    我再说一句很主观的话,我真的非常不喜欢去整合其他框架,就喜欢用 JdbcTemplate 一把梭,但它也有它不方便的地方,所以我就稍微增强了一下,仅此而已。
    yechentide
        6
    yechentide  
       6 天前
    我更喜欢 Doma2
    onion83
        7
    onion83  
       6 天前   ❤️ 1
    我也喜欢直接写 SQL 因为本质上就是和数据库打交道,SQL 才是最直观的 “官方语言”
    数据库调试好,稍微修改一下到程序就能用,看代码也流畅
    个人是比较反对使用程序框架包一层的,最主要的问题是会影响或者打断思路,大量的语法糖会导致开发脱离真实的世界,框架生成的代码质量也不一定可控。数据库关系都搞不清楚,后期维护越来越迷糊。
    Belmode
        8
    Belmode  
       6 天前
    @onion83 #7 我理解你的意思,但是这本就是多种不同的数据库设计范式。

    有些公司习惯直接用表结构来关联业务关系,然后才写 SQL ,来解决业务问题。

    但是也有另外一些公司,更习惯先建模,然后已经依据业务模型,来创建表结构和关联关系,不写 SQL 。

    在 Java 语言上,国内 ORM 流行 MyBatis 系,国外 ORM 多数都是 JPA ,这就是业务设计思路的区别,没什么优劣。

    你说迷糊,那是因为一开始,数据模型就没建立好,而且不理解框架的设计思路,熟悉这些就事半功倍,不熟悉,就很困惑。当然这些都是有学习成本的代价的,我提这个还是想强调 ORM 的不同或者用不用 ORM ,都是设计思路的区别。
    Belmode
        9
    Belmode  
       6 天前   ❤️ 2
    @Joker123456789 #4 其实自己用,用哪个都行,都无所谓的。你设计的这个类库也好,别的类库也好,都没什么关系,只要能实现最基本的功能,都可以。

    但是一旦到企业协作,在 java 中使用字符串模式的,真不行,要是大家都讲规矩,dao 都写一起,没什么问题,就怕到时候到处都是字符串拼 SQL ,这种项目让未来维护人接手了,又是新的 shi 山。

    所以用 ORM 框架,更多的是为了约束开发者,别乱写。(当然,真乱写,也没法。)
    YUyu101
        10
    YUyu101  
       6 天前
    idea 自带的数据库插件有良好的自动补全和错误提示写 sql 还行,但碰到条件拼接就不行了,在字符串里拼条件编译器也不知道你错没错,要做到编译器也能提示的 sql builder ,我记得 jooq 勉强可以,但总之 java 的类型系统实现起来比较蛋疼。
    typescript 这方面比较有潜力,这边类似的有 kysely 。如果不需要对 sql 如此精确的控制力,目前体验最好的是 prisma 。
    如果既要又要呢,想用 kysely 达到 prisma 的效果,就要多写一堆子查询还要给子查询包一层 to_json 函数,所以只能说没有完美的解决方案,都是各有所长,勉强来说两全其美的方案是 prisma-extension-kysely ,没有复杂需求的时候用 prisma ,有复杂 sql 时,用 kysely 弥补 prisma 写 raw sql 两眼一抹黑的缺点。
    XuHuan1025
        11
    XuHuan1025  
       6 天前
    现在直接把表结构 需求打包给 ai 结果大差不差
    vultr
        12
    vultr  
       6 天前
    ```go
    var sqlx strings.Builder
    var args []any

    sqlx.WriteString("SELECT `id`, `ref`, `out_key`, `first_name`, `last_name`, `email_address`, `phone_number`, `avatar_url`, `photo_url`, `status`, `created_at`, `updated_at` ")
    sqlx.WriteString("FROM `users` ")
    sqlx.WriteString("WHERE `status` >= 0 ")
    ```

    我也喜欢直接写 SQL
    falcon05
        13
    falcon05  
       6 天前 via iPhone
    最近看油管的视频时也有老外推荐 sqlc 这种,我觉得还挺直观的。
    james122333
        14
    james122333  
       6 天前 via Android
    较真来看其实都不好
    sql 本质上就不是为了程序员而设计的东西
    自然搭配语言处理上就不是无缝的
    现在解法都是怎么尽可能处理那堆烂东西
    本身的功能也过于複杂 以语言观念来看 sql 是不合格的 只是对技术门外汉来讲语法显的更直观一些 而且他们不用处理技术差异问题
    一劳永逸的东西才是好货 然而现实面考量不会公开出现的
    289396212
        15
    289396212  
       6 天前
    你有用过.net 里的 Linq 吗?
    kivmi
        16
    kivmi  
       6 天前
    直接存储过程走起
    kongkongye
        17
    kongkongye  
       6 天前
    @mumbler 用的哪个工具?
    LowBi
        18
    LowBi  
       6 天前
    确实直接写 sql 更直观,现在的 orm 框架整合太花哨了,什么 eq 、ge 、and...我要用还得去看文档,还得把传统 sql 语句思维转为代码,头疼
    129duckflew
        19
    129duckflew  
       6 天前
    更喜欢简单查询用 Jpa 内置的方法,复杂查询用 QueryDSL
    kinkin666
        20
    kinkin666  
       6 天前
    看过几十年前.pcpp 格式的 c++代码,那个真酷炫,c++代码里直接能写 sql ,select 可以直接把结果赋到变量里,where 里面也能直接用变量

    以前这样子的才算数据库支持能力强吧,不过那都是 IBM 全家桶里面才有的吧?
    现在可能考虑可移植性,只能用 JDBC 这样的最大公约数似的类库了,在这个基础上也开不出什么花了
    spritecn
        21
    spritecn  
       6 天前
    写在 xml 里是真的很难看,xml 的判断逻辑还有坑
    Cbdy
        22
    Cbdy  
       6 天前
    写 SQL 确实不错
    marcong95
        23
    marcong95  
       6 天前   ❤️ 1
    andIccidEqualTo andEndTimeLessThanOrEqualTo 这两个方法的槽点竟然在 equal/less/greater 上??
    Akitora
        24
    Akitora  
       6 天前   ❤️ 1
    这就是我喜欢 kt 的 ORM 的地方,比如 ktorm ,非常直观

    https://www.ktorm.org/images/ktorm-example.png
    AlexHsu
        25
    AlexHsu  
       6 天前   ❤️ 1
    orm 确实蠢逼 出问题了 debug 先到控制台找 sql 然后解决 sql 的问题 在回到 orm 里面反推代码怎么改
    然而他们号称解决的代码复用 数据库不相关的好处 实际上又能提现多少呢 一个项目天天来回引用 orm 层?还是天天换数据库玩。。
    wanguorui123
        26
    wanguorui123  
       6 天前
    终极 ORM 还是 C#的 EntityFramework 配合 LINQ+DataSet 方便快捷
    a194259440
        27
    a194259440  
       6 天前
    当 JAVA 还在为这些烦恼的时候,dotnet 的 Linq 已经普及多年了
    zoyao
        28
    zoyao  
       6 天前
    但是直接 sql 会存在不同数据库的兼容问题
    dododada
        29
    dododada  
       6 天前
    很多年前,师兄去给移动解决一个联合查询问题,咨询了 oracle 的工程师之后,搞了一个 3000 多行的 sql 出来,伙伴们只觉得震撼,后来工作了才知道这东西有多麻烦;
    oracle 的咨询电话太贵了,当年一小时 1800 ,现在不知道还有哪些行业在用
    cheng6563
        30
    cheng6563  
       6 天前
    我也喜欢写楼主这种简单需求
    zysuper
        31
    zysuper  
       6 天前
    现在是都是 cursor 帮我写全栈代码,我只是个测试和提问者。
    WDATM33
        32
    WDATM33  
       6 天前
    @zoyao 简单 多写几个不同数据库的实现类 根据读取配置文件动态加载对应的实现类注入就行
    fantasy0v0
        33
    fantasy0v0  
       6 天前
    我也喜欢直接写 sql ,于是我也搞了个类似的,https://github.com/fantasy0v0/swift
    平时工作就是 jooq + swift-jdbc ,配合使用。
    kangkkk
        34
    kangkkk  
       6 天前
    我也喜欢直接写 sql ,简洁高效。
    lyxxxh2
        35
    lyxxxh2  
       6 天前
    我相反,orm 重度患者。
    altwei
        36
    altwei  
       6 天前
    无论写什么,只要写的规范就行。特别是复杂语句,适当的格式化,而不是一把梭
    bingo084
        37
    bingo084  
       6 天前
    @Belmode #2 可以了解一下 Jimmer ,这个是我认为目前的极限 https://babyfish-ct.github.io/jimmer-doc/zh/
    qq135449773
        38
    qq135449773  
       6 天前
    ConditionBuilder.createCondition() 这个 API 我觉得封装的太丑了。

    可以考虑把算子封装到 Java 操作上(比如 `.le(field, value)` 、 `.gt(field, value)` 这种形式)

    单说这个 ConditionBuilder 我觉得这种混写 SQL 和 Java 代码无论是美观易用性还是后期维护的成本都稍微有点大了。
    june4
        39
    june4  
       6 天前
    我自己的 node 后端的 orm 也是自己写的,主打一个简洁直白。
    主要设计就是一个模型类系统(定义类/字段/索引,并可自动生成表创建 sql,我可不想手动同步创建表 sql 和代码模型类), 一套查询系统,查询出来的数据 row 直接是对应的模型类的实例。
    不搞复杂的自动多表关联系统,因为用处很少还复杂,真有需要时手动处理就行了。
    查询系统有覆盖 95%需要的简单的对象方式查询,有需要再直接提供 sql ,毕竟 sql 缺点是没有类型约束这。
    整个系统除了直接提供 sql 部分其它都有类型约束,orm 整个只需要 1000 行。
    用了很多年,简直太舒服了,以前用那些复杂的 orm 时老是要查 api ,有时还要打印 sql 出来看看到底干了什么,现在因为 orm 太简单到写代码完全不会有任何疑问的地方。
    gejun123456
        40
    gejun123456  
       6 天前
    @YUyu101 java 可以试试 idea+mybatis+MybatisCodeHelperPro 插件,在写条件 sql 的时候也有代码提示 https://brucege.com/doc/#/typeSafe
    mumbler
        41
    mumbler  
       6 天前
    @kongkongye #17 cursor
    mayerer
        42
    mayerer  
       6 天前
    首先就要夸赞一下 OP 帖子的排版 很优秀
    dogfeet
        43
    dogfeet  
       6 天前
    个人不太喜欢裸 sql 的方式,因为喜欢改字段名,再就是参数类型 时间字符串数值要不停的记住。裸 sql 对测试代码的覆盖率的要求更高。
    majula
        44
    majula  
       6 天前
    虽然我也不喜欢用 ORM ,但是举的例子有点牵强

    真的有程序员看不懂 LessThanOrEqualTo 么,小学英语都可以重修了
    EastLord
        45
    EastLord  
       6 天前
    哥们,我看过你好几次了 推广你的项目
    codingmiao
        46
    codingmiao  
       6 天前
    简单的 crud 丢给 orm 框架很省事,复杂一点的查询本来就要和负责数据的同事去沟通、验证、调整,得到一个符合业务要求的 sql ,这时候再把 sql 翻译回 orm 框架的代码属实没必要
    RedBeanIce
        47
    RedBeanIce  
       6 天前
    我非常喜欢楼主的这种写法。。

    如果可以下面这样子,就更好了(很抱歉,可能是一种无聊的想法)

    ```
    ConditionBuilder conditionBuilder = ConditionBuilder.createCondition()
    .add(ParamPO::id).then(">").(10);
    ``
    wu00
        48
    wu00  
       6 天前
    重度 ORM 患者
    非必要不连表
    复杂报表抽数据
    adoal
        49
    adoal  
       6 天前
    把功能齐全的关系数据库仅仅当一堆表用,做单表 crud ,或者少量几个表之间做一层直接匹配的关联查询,其实用 ORM 和直接写 SQL 也没啥区别。用 MySQL 这种残疾货跟用 Oracle 也没啥区别。

    ORM 搞不定的是那种,很多老式的业务系统,业务逻辑有很大一部分写在 SQL 里,子查询、窗口函数、触发器、存储过程、CTE 甚至递归 CTE……老式系统里,可能没有 B/S 结构,是 C/S 的,本机运行的 exe 客户端直连数据库,主要是起 UI 的作用……当然,也可以 B/S 方式用 WebUI ,但跟现在互联网流派的架构还是有很大区别的。虽然从互联网生态出来的架构师鄙视和抵制这种架构,但……其实没有绝对的谁先进谁落后,根本上还是有锤皆钉吧。
    bthulu
        50
    bthulu  
       6 天前
    为什么不用 Linq 或者 efcore?完美满足 sql 党和非 sql 党的需求
    YUyu101
        51
    YUyu101  
       6 天前   ❤️ 1
    @gejun123456 你的这个插件确实是我体验过最好的 mybatis 插件。
    ala2008
        52
    ala2008  
       6 天前
    原生 sql 的写法,在旧的项目很难维护,而且还有 sql 注入风险
    james122333
        53
    james122333  
       6 天前 via Android
    @ala2008

    哪有什么注入... 如果 connector 实现正常不会有问题
    corcre
        54
    corcre  
       6 天前
    @bthulu 还记得当年在老项目查 bug 的时候第一次见到 linq, 让我这个没学过 linq 的新手看着几百行代码四层嵌套的语句找一个小问题, 当时我真的想还不如让我看 SQL🐶🐶🐶(当然, 到现在还是不咋会 linq
    james122333
        55
    james122333  
       6 天前 via Android
    @ala2008

    Prepare statement 也都是 sql 内建功能
    就算没有该功能还是有方法一劳永逸
    真有问题是通杀
    leonhao
        56
    leonhao  
       6 天前
    我更喜欢写存储过程
    ftsland
        57
    ftsland  
       6 天前
    @bthulu 因为 java 没有
    bthulu
        58
    bthulu  
       5 天前
    @ftsland 那就换掉啊, 别在一种语言上吊死, 写复杂业务用 java 哪有 C#来的方便
    spritecn
        59
    spritecn  
       5 天前
    @ftsland groovy 有..但 java8 后 groovy 存在的意义就不怎么大了
    Rehtt
        60
    Rehtt  
       5 天前 via Android
    golang 的 gorm 也可以这样 db.Table("A").Where("name = ? AND age > ?",name,age).Find()
    acorngyl
        61
    acorngyl  
       5 天前
    以前的项目业务逻辑都在 oracle 里,一个 sql 都大几十行起,我们都是 jdbc select sql_content from sql_config where sql_id = #{sql_id}. 业务维护基本不用动代码,直接在库里更新 sql 就行。
    vishun
        62
    vishun  
       5 天前
    @majula #44 不是看不懂,而是要过一遍脑子才懂,没有<=这种符号类的直观,大脑在识别符号和语言貌似是走了两个不同的方法。
    fancy2020
        63
    fancy2020  
       5 天前
    感觉最优雅的是裸 SQL 和 ORM 的中间体,基于 Knex.js 轻度封装的 Objection.js 。
    nicotine
        64
    nicotine  
       5 天前
    mybatis-flex 可以了解一下
    zacard
        65
    zacard  
       5 天前
    mybatis-plus 、querydsl 都能满足 OP 的需求。即可以用纯 sql ,也可以用 dsl ,甚至可以混用
    kran
        66
    kran  
       5 天前 via Android
    挺好,可以绑定数组吗?比如 in 条件
    zjh7890
        67
    zjh7890  
       5 天前
    支持 1 楼,都是 AI 写了,现在,推荐用 IDEA 插件 gpt-tools ,直接帮你把 mybaits mapper 到 xml 全都改好,还有调用的地方
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2593 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 06:18 · PVG 14:18 · LAX 22:18 · JFK 01:18
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.