V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Joker123456789
V2EX  ›  Java

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

  •  
  •   Joker123456789 · 125 天前 · 2126 次点击
    这是一个创建于 125 天前的主题,其中的信息可能已经有所发展或是发生改变。

    项目简介:

    • 项目名: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]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("[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  
       125 天前
    就我觉得很奇怪吗
    Saurichthys
        2
    Saurichthys  
       125 天前
    你这是实现一个类似 mybatis 的 querywrapper 功能,说实话没有什么特别之处啊
    liprais
        3
    liprais  
       125 天前
    prepared statement 都不用......
    py2ex
        4
    py2ex  
       125 天前
    8 的钉子户报到
    giiiiiithub
        5
    giiiiiithub  
       125 天前
    “一旦打了 jar 包 就意味着 mybatis 的 xml 文件也会被打进 jar 包”

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

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

    我个人认为,配置文件 是 外包界的产物, 去客户现场 安装 调试等,改起来方便。 对于自研的公司,配置文件存在的意义 只是一个归纳整理。 按照环境拆分,统一配置中心等 都不是非文件不可的。
    Joker123456789
        15
    Joker123456789  
    OP
       122 天前
    @pocketz 是的,非常普遍,因为 几乎都在用 springboot 。 然后.... 我用的是嵌入式 netty
    Joker123456789
        16
    Joker123456789  
    OP
       122 天前
    @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  
       122 天前
    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  
       122 天前
    建议直接使用 jooq
    lichao
        19
    lichao  
       121 天前
    @msg7086 用 ActiveRecord 去怼 Java 写的 ORM 有点不讲武德
    msg7086
        20
    msg7086  
       121 天前 via Android
    @lichao 概念是类似的,不是添加字符串而是添加成 ast 然后从 ast 构建语句。我相信就算 Java 应该也能实现出来的。
    关于   ·   帮助文档   ·   API   ·   FAQ   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   2897 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 14:18 · PVG 22:18 · LAX 07:18 · JFK 10:18
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.