我一直都是一个 SQL 派,所以我开发了这个框架

2022-03-04 17:35:38 +08:00
 Joker123456789

项目简介:

我一直都是一个 SQL 派,所以我开发了这个框架, 但我也不是愚昧的排斥无 sql 操作,所以在项目中,单表的操作不需要写 sql ,但是多表 或者 复杂的操作 还是以 sql 为准。

自从 springboot 出来以后,war 包就进入历史了,现在流行的方式是 打 jar 包,这就有点意思了,一旦打了 jar 包 就意味着 mybatis 的 xml 文件也会被打进 jar 包,这种情况下 如果要改 sql 就必须去找源码; 就算是 war 包年代,也不可能直接改线上,肯定还是从源码开始修改,经过正规的一套流程后 才能上线。

所以,把 sql 写在文件里的这种形式,我个人认为 意义已经非常弱了,基于这样的考虑,所以我没支持 xml ,而是直接在代码里写 sql 。

这里大家肯会有疑问,那就是如何解决 用 StringBuilder 时,append 太多的情况,其实这个问题 JDK 已经帮解决了,从 JDK14 开始,就支持了这种语法:

String sql = """
    可以把多行的字符串写在这里
    可以把多行的字符串写在这里
    可以把多行的字符串写在这里
    可以把多行的字符串写在这里
    """

现在 虽然 jdk8 的钉子户还很多,但是 未来肯定会用上 14 ,甚至是 17,18,19,20 ,只是早晚的问题。

废话不多说,直接上示例

单表操作

插入数据

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

int result = JDBCTemplate.get().insert("表名", paramPO);

修改数据

// 构建修改条件
List<Condition> conditionList = ConditionBuilder.createCondition()
        .add("id = ?", 10)
        .add("and name = ?", "bee"))
        .build();

// 构建修改数据
ParamPO paramPO = new ParamPO();
paramPO.setUserName("a");
paramPO.setUserEmail("test@qq.com");

// 执行修改
int result = JDBCTemplate.get().update("表名", paramPO, conditionList);

删除数据

// 构建删除条件
List<Condition> conditionList = ConditionBuilder.createCondition()
        .add("id = ?", 10)
        .build();

// 执行删除
int result = JDBCTemplate.get().delete("表名", conditionList);

查询数据

// 构建查询条件
List<Condition> conditionList = ConditionBuilder.createCondition()
            .add("id > ?", 10)
            .add("and (name = ? or age > ?)", "bee", 10))
            .add("order by create_time", Condition.NOT_WHERE))
            .build();

// 执行查询
List<ParamPO> result = JDBCTemplate.get().select("表名", conditionList, ParamPO.class);

自定义 SQL 操作

增删改

ParamPO paramPO = new ParamPO();
paramPO.setUserName("testTx222");
paramPO.setUserEmail("testTx222@qq.com");
paramPO.setId(4);

// 采用{}占位符的写法
int result = JDBCTemplate.get().exec("update xt_message_board set user_name = {user_name} , user_email = {user_email} where id = {id}", paramPO);

// 采用 ? 占位符的写法
int result = JDBCTemplate.get().exec("update xt_message_board set user_name = ? , user_email = ? where id = ?", new Object[]{"testTx222","testTx222@qq.com", 4});

查询数据

ParamPO paramPO = new ParamPO();
paramPO.setId(5);
paramPO.setUserName("a");

// 采用{}占位符的写法
List<ParamPO> result = JDBCTemplate.get("dataSource").selectList("select * from xt_message_board where id > {id} and user_name != {user_name}", paramPO, ParamPO.class);

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

分页查询

/ 查询条件
ParamPO paramPO = new ParamPO();
paramPO.setId(5);
paramPO.setUserName("a");

// 查询参数
PageParamModel pageParamModel = new PageParamModel();
pageParamModel.setCurrentPage(1);
pageParamModel.setPageSize(10);
pageParamModel.setParam(paramPO);

// 使用默认 countSql 查询
PageModel<ParamPO> pageModel =  JDBCTemplate.get().selectPage("select * from xt_message_board where id > {id} and user_name != {user_name}", pageParamModel, ParamPO.class);

// 使用自定义 countSql 查询
String countSql = "自己定义 countSql";

PageModel<ParamPO> pageModel =  JDBCTemplate.get().selectPageCustomCountSql("select * from xt_message_board where id > {id} and user_name != {user_name}", countSql, pageParamModel, ParamPO.class);

事务管理

// 开启事务
TransactionManager.beginTraction();

try {
    ParamPO paramPO = new ParamPO();
    paramPO.setUserName("testTx222");
    paramPO.setUserEmail("testTx222@qq.com");
    paramPO.setId(4);
    int result = JDBCTemplate.get().exec("update xt_message_board set user_name = {user_name} , user_email = {user_email} where id = {id}", paramPO);

    // 提交
    TransactionManager.commit();
} catch(Execption e){
    // 回滚
    TransactionManager.rollback();
}

访问官网了解更多

https://magician-io.com/cn/

2936 次点击
所在节点    Java
20 条回复
brust
2022-03-04 18:01:52 +08:00
就我觉得很奇怪吗
Saurichthys
2022-03-04 18:04:05 +08:00
你这是实现一个类似 mybatis 的 querywrapper 功能,说实话没有什么特别之处啊
liprais
2022-03-04 18:05:58 +08:00
prepared statement 都不用......
py2ex
2022-03-04 18:09:40 +08:00
8 的钉子户报到
9c04C5dO01Sw5DNL
2022-03-04 18:21:06 +08:00
“一旦打了 jar 包 就意味着 mybatis 的 xml 文件也会被打进 jar 包”

但凡用 maven assembly 等插件配置一下,都不至于把 xml 也打进 jar 包。
pocketz
2022-03-05 09:51:19 +08:00
是我孤陋寡闻了。。。现在嵌入式 tomcat 用的这么普遍吗
ldyisbest
2022-03-05 11:49:20 +08:00
看着感觉和 mybatis 的 example 差不多
msg7086
2022-03-05 11:53:30 +08:00
唔,恭喜你重新发明了半个 ORM ?
msg7086
2022-03-05 12:18:20 +08:00
最理想的做法应该是根据输入数据构建 AST 然后把 AST 变形转换成对应的 SQL 语句,这是一般 ORM 的做法。
你这个只是做了一个 StringBuffer 在那拼字符串。
而且这么核心的组件没有基本的测试覆盖,是不是太草率了一些……

(我十几年前倒是在 PHP4.4 上做过类似的项目,在 ADOdb 上面包一层字符串处理。可这都已经 2022 年了……)
a0210077
2022-03-05 17:58:23 +08:00
“自从 springboot 出来以后,war 包就进入历史了,现在流行的方式是 打 jar 包,这就有点意思了,一旦打了 jar 包 就意味着 mybatis 的 xml 文件也会被打进 jar 包,这种情况下 如果要改 sql 就必须去找源码; 就算是 war 包年代,也不可能直接改线上,肯定还是从源码开始修改,经过正规的一套流程后 才能上线。”
a0210077
2022-03-05 18:03:30 +08:00
这里不用 xml 的理由非常牵强,同意我 @giiiiiithub #5 说的,用 maven ,想怎么打包都可以
Joker123456789
2022-03-07 15:08:06 +08:00
@liprais 你确定不是你眼花了?
Joker123456789
2022-03-07 15:11:11 +08:00
@msg7086 ast 确实高端一点,但是 stringBuffer 也没什么 致命的弱点吧。
Joker123456789
2022-03-07 15:15:22 +08:00
@a0210077 确实....... , 但是 我最后还有一句:线上不可能让你直接改的,你还是要回去改源码,然后 经过一套流程 才能上线。

我个人认为,配置文件 是 外包界的产物, 去客户现场 安装 调试等,改起来方便。 对于自研的公司,配置文件存在的意义 只是一个归纳整理。 按照环境拆分,统一配置中心等 都不是非文件不可的。
Joker123456789
2022-03-07 15:19:39 +08:00
@pocketz 是的,非常普遍,因为 几乎都在用 springboot 。 然后.... 我用的是嵌入式 netty
Joker123456789
2022-03-07 15:26:05 +08:00
@Saurichthys 确实是的,用法上没什么特别的。 但是因为我是一个 sql 派,所以

这种写法

// 构建查询条件
List<Condition> conditionList = ConditionBuilder.createCondition()
.add("id > ?", 10)
.add("and (name = ? or age > ?)", "bee", 10)) // 这里是一个条件的组合
.add("order by create_time", Condition.NOT_WHERE))
.build();

我觉得比这种写法更容易上手

QueryWrapper queryWrapper = new QueryWrapper();
queryWrapper.eq("id", 10);

因为前者是 原生 sql ,学习成本几乎为 0 ,而且 组合条件 也很好写。 而后者 需要熟练的记住 eq, lt, ge 等方法的意思。

而且我这个 jar 包非常小,很小很小, 连源码都没几个类。
msg7086
2022-03-07 15:39:18 +08:00
StringBuffer 意味着你只能以真正 SQL 的方式去写查询。
如果用 AST 或者类似的技术,则不需要遵循 SQL 的顺序,也不需要逼着用户去加生硬的「 and 」。

举个简单的例子:

bob = User.where(email: "bob@test.com").where(active: true)
# => SELECT "users".* FROM "users" WHERE "users"."email" = 'bob@test.com' AND "users"."active" = 't'

details = User.select(:id, :email, :first_name).order(id: :desc)
# => SELECT "users"."id", "users"."email", "users"."first_name" FROM "users" ORDER BY "users"."id" DESC

bob.merge(details).first
# => SELECT "users"."id", "users"."email", "users"."first_name" FROM "users"
# WHERE "users"."email" = 'bob@test.com' AND "users"."active" = 't'
# ORDER BY "users"."id" DESC LIMIT 1

这里的这种 bob.merge(details).first 的用法可以让代码变得非常干净且易于维护。
你可以提前列出所有可能的查询条件,然后在最后一步根据输入参数或者具体需求进行拼装。
coderwl
2022-03-07 18:19:52 +08:00
建议直接使用 jooq
lichao
2022-03-08 13:24:06 +08:00
@msg7086 用 ActiveRecord 去怼 Java 写的 ORM 有点不讲武德
msg7086
2022-03-08 13:40:21 +08:00
@lichao 概念是类似的,不是添加字符串而是添加成 ast 然后从 ast 构建语句。我相信就算 Java 应该也能实现出来的。

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

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

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

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

© 2021 V2EX