此文章充满了个人的主观色彩,如果引起了大家的不适,那我也没办法。
其实,我更喜欢写 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 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 result = MagicDBUtils.get(jdbcTemplate).selectList("select * from xt_message_board where id > {id} and user_name != {user_name}", paramPO, ParamPO.class);

// 采用 ? 占位符的写法
List 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&#160;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&#160;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 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 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;

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

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

大家也不要把他去当做一个持久层框架来看待,他就是一个 JdbcTemplate 的扩展包,而且是非常轻微的扩展。

现在都让 AI 写了,我指挥监督一下就行了,才不去关心那些技术细节

// 新增 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);

别人的花样更多。 mybatis-flex.com/zh/base/db-row.html
java ORM 这块就别再卷了,已经到了极限了,java 语法限制了没办法做那些函数式语言做 DSL 的便利性。

跟 Mybatis 的 写法比有什么优势?

谢谢分享,不过我真的没卷,你仔细看一下就知道了,除了单表我几乎就没玩任何花样,就像标题写的那样《其实,我更喜欢写 SQL 》,这绝不是一句引流的话,而是真心话

这个工具本身就不是为了打 mybatis 或者其他任何现有的框架,而是给 SQL 党一个非常小型的选择而已。他其实就是对 Spring 的 JdbcTemplate 的扩展,就做了两件事,一个是单表操作的无 sql 化,一个是 sql 里面支持{}占位符,其他也没了。

我再说一句很主观的话,我真的非常不喜欢去整合其他框架,就喜欢用 JdbcTemplate 一把梭,但它也有它不方便的地方,所以我就稍微增强了一下,仅此而已。

我更喜欢 Doma2

我也喜欢直接写 SQL 因为本质上就是和数据库打交道,SQL 才是最直观的 “官方语言”
数据库调试好,稍微修改一下到程序就能用,看代码也流畅
个人是比较反对使用程序框架包一层的,最主要的问题是会影响或者打断思路,大量的语法糖会导致开发脱离真实的世界,框架生成的代码质量也不一定可控。数据库关系都搞不清楚,后期维护越来越迷糊。

#7 我理解你的意思,但是这本就是多种不同的数据库设计范式。

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

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

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

你说迷糊,那是因为一开始,数据模型就没建立好,而且不理解框架的设计思路,熟悉这些就事半功倍,不熟悉,就很困惑。当然这些都是有学习成本的代价的,我提这个还是想强调 ORM 的不同或者用不用 ORM ,都是设计思路的区别。

#4 其实自己用,用哪个都行,都无所谓的。你设计的这个类库也好,别的类库也好,都没什么关系,只要能实现最基本的功能,都可以。

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

所以用 ORM 框架,更多的是为了约束开发者,别乱写。(当然,真乱写,也没法。)

idea 自带的数据库插件有良好的自动补全和错误提示写 sql 还行,但碰到条件拼接就不行了,在字符串里拼条件编译器也不知道你错没错,要做到编译器也能提示的 sql builder ,我记得 jooq 勉强可以,但总之 java 的类型系统实现起来比较蛋疼。
typescript 这方面比较有潜力,这边类似的有 kysely 。如果不需要对 sql 如此精确的控制力,目前体验最好的是 prisma 。
如果既要又要呢,想用 kysely 达到 prisma 的效果,就要多写一堆子查询还要给子查询包一层 to_json 函数,所以只能说没有完美的解决方案,都是各有所长,勉强来说两全其美的方案是 prisma-extension-kysely ,没有复杂需求的时候用 prisma ,有复杂 sql 时,用 kysely 弥补 prisma 写 raw sql 两眼一抹黑的缺点。

现在直接把表结构 需求打包给 ai 结果大差不差

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

最近看油管的视频时也有老外推荐 sqlc 这种,我觉得还挺直观的。

较真来看其实都不好
sql 本质上就不是为了程序员而设计的东西
自然搭配语言处理上就不是无缝的
现在解法都是怎么尽可能处理那堆烂东西
本身的功能也过于複杂 以语言观念来看 sql 是不合格的 只是对技术门外汉来讲语法显的更直观一些 而且他们不用处理技术差异问题
一劳永逸的东西才是好货 然而现实面考量不会公开出现的

你有用过.net 里的 Linq 吗?

直接存储过程走起

用的哪个工具?

确实直接写 sql 更直观,现在的 orm 框架整合太花哨了,什么 eq 、ge 、and...我要用还得去看文档,还得把传统 sql 语句思维转为代码,头疼

更喜欢简单查询用 Jpa 内置的方法,复杂查询用 QueryDSL

看过几十年前.pcpp 格式的 c++代码,那个真酷炫,c++代码里直接能写 sql ,select 可以直接把结果赋到变量里,where 里面也能直接用变量

以前这样子的才算数据库支持能力强吧,不过那都是 IBM 全家桶里面才有的吧?
现在可能考虑可移植性,只能用 JDBC 这样的最大公约数似的类库了,在这个基础上也开不出什么花了

写在 xml 里是真的很难看,xml 的判断逻辑还有坑

写 SQL 确实不错

andIccidEqualTo andEndTimeLessThanOrEqualTo 这两个方法的槽点竟然在 equal/less/greater 上??

这就是我喜欢 kt 的 ORM 的地方,比如 ktorm ,非常直观

www.ktorm.org/images/ktorm-example.png

orm 确实蠢逼 出问题了 debug 先到控制台找 sql 然后解决 sql 的问题 在回到 orm 里面反推代码怎么改
然而他们号称解决的代码复用 数据库不相关的好处 实际上又能提现多少呢 一个项目天天来回引用 orm 层?还是天天换数据库玩。。

终极 ORM 还是 C#的 EntityFramework 配合 LINQ+DataSet 方便快捷

当 JAVA 还在为这些烦恼的时候,dotnet 的 Linq 已经普及多年了

但是直接 sql 会存在不同数据库的兼容问题

很多年前,师兄去给移动解决一个联合查询问题,咨询了 oracle 的工程师之后,搞了一个 3000 多行的 sql 出来,伙伴们只觉得震撼,后来工作了才知道这东西有多麻烦;
oracle 的咨询电话太贵了,当年一小时 1800 ,现在不知道还有哪些行业在用

我也喜欢写楼主这种简单需求

现在是都是 cursor 帮我写全栈代码,我只是个测试和提问者。

简单 多写几个不同数据库的实现类 根据读取配置文件动态加载对应的实现类注入就行

我也喜欢直接写 sql ,于是我也搞了个类似的, github.com/fantasy0v0/swift
平时工作就是 jooq + swift-jdbc ,配合使用。

我也喜欢直接写 sql ,简洁高效。

我相反,orm 重度患者。

无论写什么,只要写的规范就行。特别是复杂语句,适当的格式化,而不是一把梭

#2 可以了解一下 Jimmer ,这个是我认为目前的极限 babyfish-ct.github.io/jimmer-doc/zh/

ConditionBuilder.createCondition() 这个 API 我觉得封装的太丑了。

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

单说这个 ConditionBuilder 我觉得这种混写 SQL 和 Java 代码无论是美观易用性还是后期维护的成本都稍微有点大了。

我自己的 node 后端的 orm 也是自己写的,主打一个简洁直白。
主要设计就是一个模型类系统(定义类/字段/索引,并可自动生成表创建 sql,我可不想手动同步创建表 sql 和代码模型类), 一套查询系统,查询出来的数据 row 直接是对应的模型类的实例。
不搞复杂的自动多表关联系统,因为用处很少还复杂,真有需要时手动处理就行了。
查询系统有覆盖 95%需要的简单的对象方式查询,有需要再直接提供 sql ,毕竟 sql 缺点是没有类型约束这。
整个系统除了直接提供 sql 部分其它都有类型约束,orm 整个只需要 1000 行。
用了很多年,简直太舒服了,以前用那些复杂的 orm 时老是要查 api ,有时还要打印 sql 出来看看到底干了什么,现在因为 orm 太简单到写代码完全不会有任何疑问的地方。

java 可以试试 idea+mybatis+MybatisCodeHelperPro 插件,在写条件 sql 的时候也有代码提示 brucege.com/doc/#/typeSafe

#17 cursor

首先就要夸赞一下 OP 帖子的排版 很优秀

个人不太喜欢裸 sql 的方式,因为喜欢改字段名,再就是参数类型 时间字符串数值要不停的记住。裸 sql 对测试代码的覆盖率的要求更高。

虽然我也不喜欢用 ORM ,但是举的例子有点牵强

真的有程序员看不懂 LessThanOrEqualTo 么,小学英语都可以重修了

哥们,我看过你好几次了 推广你的项目

简单的 crud 丢给 orm 框架很省事,复杂一点的查询本来就要和负责数据的同事去沟通、验证、调整,得到一个符合业务要求的 sql ,这时候再把 sql 翻译回 orm 框架的代码属实没必要

我非常喜欢楼主的这种写法。。

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

ConditionBuilder conditionBuilder = ConditionBuilder.createCondition()
 .add(ParamPO::id).then(">").(10);
``

 重度 ORM 患者
非必要不连表
复杂报表抽数据

 把功能齐全的关系数据库仅仅当一堆表用,做单表 crud ,或者少量几个表之间做一层直接匹配的关联查询,其实用 ORM 和直接写 SQL 也没啥区别。用 MySQL 这种残疾货跟用 Oracle 也没啥区别。

ORM 搞不定的是那种,很多老式的业务系统,业务逻辑有很大一部分写在 SQL 里,子查询、窗口函数、触发器、存储过程、CTE 甚至递归 CTE……老式系统里,可能没有 B/S 结构,是 C/S 的,本机运行的 exe 客户端直连数据库,主要是起 UI 的作用……当然,也可以 B/S 方式用 WebUI ,但跟现在互联网流派的架构还是有很大区别的。虽然从互联网生态出来的架构师鄙视和抵制这种架构,但……其实没有绝对的谁先进谁落后,根本上还是有锤皆钉吧。

 为什么不用 Linq 或者 efcore?完美满足 sql 党和非 sql 党的需求

 你的这个插件确实是我体验过最好的 mybatis 插件。

 原生 sql 的写法,在旧的项目很难维护,而且还有 sql 注入风险

哪有什么注入... 如果 connector 实现正常不会有问题

 还记得当年在老项目查 bug 的时候第一次见到 linq, 让我这个没学过 linq 的新手看着几百行代码四层嵌套的语句找一个小问题, 当时我真的想还不如让我看 SQL🐶🐶🐶(当然, 到现在还是不咋会 linq

Prepare statement 也都是 sql 内建功能
就算没有该功能还是有方法一劳永逸
真有问题是通杀

 我更喜欢写存储过程

 因为 java 没有

 那就换掉啊, 别在一种语言上吊死, 写复杂业务用 java 哪有 C#来的方便

 groovy 有..但 java8 后 groovy 存在的意义就不怎么大了

 golang 的 gorm 也可以这样 db.Table("A").Where("name = ? AND age > ?",name,age).Find()