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

其实,我更喜欢写 SQL

  •  
  •   Joker123456789 · 34 天前 · 7740 次点击
    这是一个创建于 34 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    其实,我更喜欢写 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 条附言  ·  34 天前
    补充一句非常主观的话,我真的非常不喜欢去整合其他框架,就喜欢用 JdbcTemplate 一把梭,但它也有它不方便的地方,所以我就稍微增强了一下,仅此而已。

    大家也不要把他去当做一个持久层框架来看待,他就是一个 JdbcTemplate 的扩展包,而且是非常轻微的扩展。
    67 条回复    2024-11-26 12:22:51 +08:00
    mumbler
        1
    mumbler  
       34 天前
    现在都让 AI 写了,我指挥监督一下就行了,才不去关心那些技术细节
    Belmode
        2
    Belmode  
       34 天前
    ```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  
       34 天前 via Android
    跟 Mybatis 的 @Provider 写法比有什么优势?
    Joker123456789
        4
    Joker123456789  
    OP
       34 天前
    @Belmode 谢谢分享,不过我真的没卷,你仔细看一下就知道了,除了单表我几乎就没玩任何花样,就像标题写的那样《其实,我更喜欢写 SQL 》,这绝不是一句引流的话,而是真心话
    Joker123456789
        5
    Joker123456789  
    OP
       34 天前
    @COW 这个工具本身就不是为了打 mybatis 或者其他任何现有的框架,而是给 SQL 党一个非常小型的选择而已。他其实就是对 Spring 的 JdbcTemplate 的扩展,就做了两件事,一个是单表操作的无 sql 化,一个是 sql 里面支持{}占位符,其他也没了。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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