其实,我更喜欢写 SQL

37 天前
 Joker123456789

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

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

对于单表的增删改查

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

ParamPO paramPO = new ParamPO();
paramPO.setUserName("a");
paramPO.setUserEmail("test@qq.com");

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("testTx222@qq.com");
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","testTx222@qq.com", 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

7787 次点击
所在节点    Java
67 条回复
mumbler
37 天前
现在都让 AI 写了,我指挥监督一下就行了,才不去关心那些技术细节
Belmode
37 天前
```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
37 天前
跟 Mybatis 的 @Provider 写法比有什么优势?
Joker123456789
37 天前
@Belmode 谢谢分享,不过我真的没卷,你仔细看一下就知道了,除了单表我几乎就没玩任何花样,就像标题写的那样《其实,我更喜欢写 SQL 》,这绝不是一句引流的话,而是真心话
Joker123456789
37 天前
@COW 这个工具本身就不是为了打 mybatis 或者其他任何现有的框架,而是给 SQL 党一个非常小型的选择而已。他其实就是对 Spring 的 JdbcTemplate 的扩展,就做了两件事,一个是单表操作的无 sql 化,一个是 sql 里面支持{}占位符,其他也没了。

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

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

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

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

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

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

所以用 ORM 框架,更多的是为了约束开发者,别乱写。(当然,真乱写,也没法。)
YUyu101
37 天前
idea 自带的数据库插件有良好的自动补全和错误提示写 sql 还行,但碰到条件拼接就不行了,在字符串里拼条件编译器也不知道你错没错,要做到编译器也能提示的 sql builder ,我记得 jooq 勉强可以,但总之 java 的类型系统实现起来比较蛋疼。
typescript 这方面比较有潜力,这边类似的有 kysely 。如果不需要对 sql 如此精确的控制力,目前体验最好的是 prisma 。
如果既要又要呢,想用 kysely 达到 prisma 的效果,就要多写一堆子查询还要给子查询包一层 to_json 函数,所以只能说没有完美的解决方案,都是各有所长,勉强来说两全其美的方案是 prisma-extension-kysely ,没有复杂需求的时候用 prisma ,有复杂 sql 时,用 kysely 弥补 prisma 写 raw sql 两眼一抹黑的缺点。
XuHuan1025
37 天前
现在直接把表结构 需求打包给 ai 结果大差不差
vultr
37 天前
```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
37 天前
最近看油管的视频时也有老外推荐 sqlc 这种,我觉得还挺直观的。
james122333
37 天前
较真来看其实都不好
sql 本质上就不是为了程序员而设计的东西
自然搭配语言处理上就不是无缝的
现在解法都是怎么尽可能处理那堆烂东西
本身的功能也过于複杂 以语言观念来看 sql 是不合格的 只是对技术门外汉来讲语法显的更直观一些 而且他们不用处理技术差异问题
一劳永逸的东西才是好货 然而现实面考量不会公开出现的
289396212
37 天前
你有用过.net 里的 Linq 吗?
kivmi
37 天前
直接存储过程走起
kongkongye
37 天前
@mumbler 用的哪个工具?
LowBi
37 天前
确实直接写 sql 更直观,现在的 orm 框架整合太花哨了,什么 eq 、ge 、and...我要用还得去看文档,还得把传统 sql 语句思维转为代码,头疼
129duckflew
37 天前
更喜欢简单查询用 Jpa 内置的方法,复杂查询用 QueryDSL
kinkin666
37 天前
看过几十年前.pcpp 格式的 c++代码,那个真酷炫,c++代码里直接能写 sql ,select 可以直接把结果赋到变量里,where 里面也能直接用变量

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

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1092231

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX