其实,我更喜欢写 SQL

4 天前
 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

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

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

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

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

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

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

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

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