我一直都是一个 SQL 派,所以我开发了这个框架 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Joker123456789
V2EX    Java

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

  •  
  •   Joker123456789 2022-03-04 17:35:38 +08:00 3286 次点击
    这是一个创建于 1324 天前的主题,其中的信息可能已经有所发展或是发生改变。

    项目简介:

    • 项目名:Magician-JDBC
    • 开发语言:Java
    • 多数据源:支持
    • 实体映射:支持

    我一直都是一个 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("[email protected]"); 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("[email protected]"); // 执行修改 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("[email protected]"); 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","[email protected]", 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("[email protected]"); 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/

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

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

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

    我个人认为,配置文件 是 外包界的产物, 去客户现场 安装 调试等,改起来方便。 对于自研的公司,配置文件存在的意义 只是一个归纳整理。 按照环境拆分,统一配置中心等 都不是非文件不可的。
    Joker123456789
        15
    Joker123456789  
    OP
       2022-03-07 15:19:39 +08:00
    @pocketz 是的,非常普遍,因为 几乎都在用 springboot 。 然后.... 我用的是嵌入式 netty
    Joker123456789
        16
    Joker123456789  
    OP
       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
        17
    msg7086  
       2022-03-07 15:39:18 +08:00
    StringBuffer 意味着你只能以真正 SQL 的方式去写查询。
    如果用 AST 或者类似的技术,则不需要遵循 SQL 的顺序,也不需要逼着用户去加生硬的「 and 」。

    举个简单的例子:

    bob = User.where(email: "[email protected]").where(active: true)
    # => SELECT "users".* FROM "users" WHERE "users"."email" = '[email protected]' 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" = '[email protected]' AND "users"."active" = 't'
    # ORDER BY "users"."id" DESC LIMIT 1

    这里的这种 bob.merge(details).first 的用法可以让代码变得非常干净且易于维护。
    你可以提前列出所有可能的查询条件,然后在最后一步根据输入参数或者具体需求进行拼装。
    coderwl
        18
    coderwl  
       2022-03-07 18:19:52 +08:00
    建议直接使用 jooq
    lichao
        19
    lichao  
       2022-03-08 13:24:06 +08:00
    @msg7086 用 ActiveRecord 去怼 Java 写的 ORM 有点不讲武德
    msg7086
        20
    msg7086  
       2022-03-08 13:40:21 +08:00 via Android
    @lichao 概念是类似的,不是添加字符串而是添加成 ast 然后从 ast 构建语句。我相信就算 Java 应该也能实现出来的。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2658 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 39ms UTC 03:15 PVG 11:15 LAX 20:15 JFK 23:15
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86