390 Star 1.6K Fork 483

闲.大赋(李家智) / BeetlSQL

 / 详情

请允许我谈一下BeetlSql项目的缺点

已完成
创建于  
2017-03-12 03:46

您好,我是jSqlBox ( https://git.oschina.net/drinkjava2/jSqlBox ) 项目的作者,能看一下这个项目吗?
其中有我个人对BeetlSql和其它一些常见持久化工具的横向评分比较,BeetlSql总体上做的还是不错的,但主要有以下问题:1)Sql不支持重
构,如果字段名修改将不得不手一个个更正; 2)引入模板语言也是一种侵入性,为了写Sql还要加载模板语言,而且后者也没有IDE重构和语法检查支持; 3)表和字段的配置是固定的,不能在运行期动态生成和修改,这个是目前所有采用XML或Annotation配置的持久化项目的通病。4)总体来说没有特别出彩的创新,相比于"Hibernate退化用法+原生SQL"这种组合,BeetlSql没有什么给人印象特别深刻的创新。"避开了实体容器的复杂性"、"DAO类自动生成"等,也不是什么特别的发明。
作为Java持久化工具的作者,我深知开发的难度和作者时间和精力上的巨大付出,但是作为追求完美的程序员来说,永远向往着一个更简单更好用的持久化工具,一千个人有一千种设计思路,BeetlSql是一种尝试,jSqlBox也是一种尝试,也许将来它们互相取长补短,会促生出更完美的持久化工具。

评论 (40)

drinkjava2 创建了任务

看了你对beetlsql的评价,总体评价还是很高,有些地方我有不同意见,第一,beetlsql支持orm查询而不像JAP那样全支持,但就orm查询来说,甚至比jpa,hibernate更强,你将beetlsql 和 不支持orm的dbutil 一个分数,这不对。在跨数据一样,比如统计分析函数,不同的数据库是不一样的,只能写不同的sql,JPA是无法完美做到的,beetlsql,则可以按照数据库类型来寻找特定sql

另外,你说sql重构,但就你的例子来说,你也并不支持sql重构,表明修改,列名修改无法通过重构工具完成,这点大家都是一样的

第三,你认为模板是侵入性,那你看看你实例代码里:


List users = Dao.queryForEntityList(User.class, select(), u.all(), ",", e.all(), from(), u.table(), ",",
e.table(), " where ", oneToMany(), u.ID(), "=", e.UID(), bind(u.EMAILS(), e.USER()));

谁对sql更有倾入性一目了然呢

另外,无论是mybatis还是beetsql,模板不是关键,关键是将sql单独在文件里维护,如beetlsql在md文件里维护,这样上百行的,带有复杂判断的 sql也一目了然。模板方式还为重构提供了可能,只是现在我没有精力去做一个sql文件的重构功能插件而已。

最后,我跟mybatis 是一派别,认为sql非常重要,我推测你和jooq,jpa是一派别,认为java很重要

补充一点,你所谓的重构简直是毁了sql,你官网呈现的例子更是无法支持重构,你这种

 select(), u.all(), ",", e.all(), from(), u.table(), ",",
e.table(), " where ", oneToMany(), u.ID(), "=", e.UID(), bind(u.EMAILS(), e.USER())

看着不烦吗?我打赌你的人生里没有写过超过5行的sql语句

如果我全部时间做一个sql重构的核心(不包含插件部分),我只需要最多俩周,就能吧beetlsql 在md的重构做出来,像java重构一样

满城风雨近重阳,城脚谁家菊自黄?

面对各种强大,我更喜欢在开发效率以及SQL控制上选择一个拆中,beetlsql正是我的选择。
像动态修改sql或者动态修改字段名这种应用,需要具体的场景。但我们更不愿意把这种复杂性交给ORM工具去完成。

关于BeetlSQL、MyBatis、jSqlBox这种查强写弱的单向ORM工具,应该不如Hibernate这种全功能ORM,这点应该没有疑问吧? BeetlSQL分数偏低主要是因为这句话:List orders = (List)user.get("productOrder"); 这种用法对重构支持不好,如果写成user.get(ProductOrder.class)就好了。
关于重构,jSqlBox只是提供了这个功能,但不强迫别人去执行。这也是我的示例中有可能出现数据库表字段名的原因,因为我本人也是会是会愉懒的。但是这个示例Dao.queryForEntityList(User.class, select(), u.all(), ",", e.all(), from(), u.table(), ",",
e.table(), " where ", oneToMany(), u.ID(), "=", e.UID(), bind(u.EMAILS(), e.USER()));是100%支持重构的,如果数据库字段id更名为user_id,改一下User类的配置就可以了,如果User属性id更名为userId, 直接通过ID的重构功能就可以了,所有SQL中出现u.ID()的都会改为u.USERIID()而不必用搜索查找功能去手工替换。
我说的侵入性不是你把SQL搞乱了,而是模板语言太重量级了,引入模板语言的学习成本大于它带来的好处。不是嫌抹布把水搞脏了,而是嫌抹布太高级了,用不惯。SQL就是个字符串而已,没什么技术含量,好比个面团,放在代码时你想怎么揉它就怎么揉,放在模板里就揉起来不方便了,必须发明一套新的语法在模板里。本来写十个Java函数就能操纵好SQL,引入模板语言后要写十个函数还要重新发明一遍Java语法,而且失去了重构的可能性。想一想如果User和Customer里都有一个username字段,放在模板里怎么用查找替换功能重构它?
把sql单独在文件里好维护,对DBA友好是不错,但换句话说就是对我们程序员自己残忍。Sql是有意义的,它在说一句话,它和我们的代码是同级别的,应该出现在程序员最方便获得的地方,最差也要集中放在一个Java类里,用IDE的Ctrl+左键一点就能找到,而不是用搜索功能去文本里面去搜。不支持重构的SQl写起来爽,维护起来就是恶梦,尤其是不分行的SQL。
以上为个人看法,公说公有理,编程风格这个事,各人可以保留自己的偏好,无须一定要说服别人。
顺便说一下,我写SQL和人家不一样,从来都是竖着写的,通常都是超过5行的,你打赌输了:http://blog.csdn.net/zhangrex/article/details/79474

@drinkjava2 queryForEntityList 这个方法如果是链式就好了,这样写起来,看上去真像拼凑

@drinkjava2 在国内我想mybatis的用户远大于 hibernate 用户量,肯定不是hibernate 比不上mybatis,而是mybatis更知道开发者的痛点和g点,而你这个设计,对于我个人而言阿,你这个方式将g点变成了痛点。 :smile:

@李嘉图 对,jSqlBox基本原理就是通过拼字符串返回一个完整的原生SQL,如果有参数先暂存在Threadlocol中。只要满足上述两个条件,可以用逗号分隔,也可以用+号连(见示例7),也可以用s.append().append().toString()这种链式写法的,也可以用过程来包装,如 test.function_test.query_method.ListQueryTest类中的comma方法:
select(), comma(u1.all(), u2.ID(), u2.PHONENUMBER(), u2.ADDRESS(), u2.USERNAME()), from()..., 甚至可以将模板语言或XML的解析结果嵌入到SQL中,只要模板语言或XML在解析过程中将参数按正确顺序暂存到Threadlocal中,使用了jSqlBox并不排斥模板语言,它们不是非此即彼的关系。但是个人更偏向于写在程序里,而且通常写成多行以提高可维护性, 而且也方便根据条件判断来动态拼接SQL。写在程序里看起来丑了一点,但是有IDE拼写检查和语法提示,不用担心打错字。

spring-data-jpa用做简单查询还是很好用的。
我们现在的项目是spring-data-jpa + spring-mybatis,
spring-data-jpa 用作单表简单查询 和 数据保存使用,
spring-mybatis 用作复杂查询,
这两者结合还有一个好处,jpa的实体类可以与spring-mybatis返回结果所需要的实体类共用,目前感觉是非常棒的方法吧。

@nibilly Hibernate(JPA)+myBatis倒也可行,但是要掌握两个知识点,要配置两遍,有点累,从长久看,最终还是会统一成用一个工具,或者是BeetlSQL或jSqlBox这种ORM+SQL双管齐下的工具,或者是Hibernate加强动态配置和SQL查询,或是Mybatis加强CRUD自动生成功能。让时间来选择吧。

对于sql,我一般都是在数据库工具验证完毕了才放到beetlsql md文件里,不会出现打错的情况

我强调的是将sql放到文件里管理,这点用beetlsql,还是myabtis也好,出发点都是一样的。模板当然用模板的好处,比如,条件拼写,重用等,也容易粘贴到数据库工具里运行

你说你写过超过5行的sql,我说的复杂查询这种。。。。

呵呵,开个玩笑而已,我知道你指的是很复杂很长的SQL的意思。这个,是真的很少碰到,我主业不是编程,也不和DBA打交道。不过我相信这种复杂SQL占比是很少的,就算真的碰到了,就把这种SQL原样放在Java源程序或文本文件里也不是大不了的事。支持重构,主要原因是为了以后维护方便,并不是为了快速开发,这属于前人载树后人乘凉,如果开发时间紧张或业务变动不频繁可以不用考虑重构。
条件拼写、重用、粘贴到数据库运行等,用Java也能做到的。条件拼写可以用Java的IF和三元操作符,优点是不用发明新语法,重用可以放在一个公共Java子程序里,优点是Ctrl+左键可以很快定位到子程序,粘贴到数据库可以在单元测试时打开日志获得SQL。

见文章:一个直接利用Java作为SQL模板的方法
https://my.oschina.net/drinkjava2/blog/892309

public int conditionQuery(int condition, Object parameter) {
        User u = new User();
        String sql = "Select count(*) from " + u.table() + " where ";
        if (condition == 1 || condition == 3)
            sql = sql + u.USERNAME() + "=" + q(parameter) + " and " + u.ADDRESS() + "=" + q("Address1");
        if (condition == 2)
            sql = sql + u.USERNAME() + "=" + q(parameter);
        if (condition == 3)
            sql = sql + " or " + u.AGE() + "=" + q(parameter);
        return Dao.queryForInteger(sql);
    }  

:joy: 这都是些啥,SQL的可读性不忍直视,我还是喜欢SQL交由模板管理,看模板我就知道他要做什么,返回了什么;

这是一个根据条件动态拼接SQL的例子,可读性差和它的逻辑复杂有关系,但好在它至少没有发明新的语法,会Java的就能看懂。如果用模板的话,可读性也好不了多少,该有的IF判断语法总要有吧?你可以用模板写一下实现上述同样的逻辑,我们对比一下。

输入图片说明

开始对比吧.

public int conditionQuery(int condition, Object parameter) {
	User u = new User();
	String sql = "Select count(*) from " + u.table() + " where ";
	if (condition == 1 || condition == 3)
		sql = sql + u.USERNAME() + "=" + q(parameter) + " and " + u.ADDRESS() + "=" + q("Address1");
	if (condition == 2)
		sql = sql + u.USERNAME() + "=" + q(parameter);
	if (condition == 3)
		sql = sql + " or " + u.AGE() + "=" + q(parameter);
	return Dao.queryForInteger(sql);
}  

重复一下 @hpboys 的Java demo,
就我个人观察而言,不管从语法着色还是从SQL语句拼接的连贯性等,Beetl模板的可读性完爆了Java拼接的SQL,哈哈哈。

如果不考虑重构,Java也可写成如下:

    public int conditionQuery(int condition, User u) {
        return Dao.queryForInteger("Select count(*) from tb_user where ", 
                (condition == 1 || condition == 3) ? "userName=? and address=?" + k(u.getName(), u.getAddress())
                        : "1=1 ",
                (condition == 2) ? " and userName=" + q(u.getName()) : "",
                (condition == 3) ? " and age=" + q(u.getAge()) : "");
    }

可读性模板略好,但是可维护性并不一定好。对于Java来说,一个函数解决所有问题,对于模板来说,SQL存放在一个文件,接口放在一个文件,(不知道你的实现在哪里),编程时两边跳来跳去,而且没有IDE导航支持(及重构、查错、构式化等),不利于维护。
另外语法着色,上例中的if和and应该是不同的颜色,IDE对模板的着色也是弱项。

demo 地址.

没有实现也不需要实现, 这样就可以用了.

像这段代码, 用户还需要知道q方法是什么, k方法是什么. 如果返回值是int还需要调用指定的 Dao.queryForInteger方法. 如果是对象,我相信还需要调用返回对象的那个方法.

sql放在java文件中只适合小项目, 一旦项目变大你就会知道编写代码有多吃力. (可以参考)

如果团队的项目中配有一个DBA, 放在单独sql文件DBA可以直接帮助你优化与写这些,但是如果直接把sql写在java文件中, DBA怎么能更好的利用上.

一旦sql单独出来, java程序员关注的只有业务. DBA关注的只有sql文件.分工明确.

demo 地址

运行: com.vvvsss.beetlsql.BeeAnimalTest.java

java里放sql,我只能运行后才知道sql的样子,也是就是说,如果运行出错或者sql要更改,这会非常费劲。

sql现在才几行,实际情况,我遇到项目40%的sql都非常长,50行算正常的,这在java里没有维护的,尤其是你还不容易改了一个sql,要重新放回到java里,那太折腾人了

对于未知返回对象类型,上面Java例中可以改成返回一个preparedSQL对象。极简编程并不是少打几个字就行了,$字符的使用显得很怪异,lombok的使用也使得程序非标准化。不多说了,以下是我的一些其它想法,随便说一说仅供参考:
1.当第一眼看到beetlsql,除了引入模板外,基本架构与hibernate没有太大区别,问题是它还在不断向Hibernate方向靠拢,也很复杂,看一看文档有多少知识点就知道了。与其这样,还不如把Hibernate源码拿过来改一下,加上模板功能就完了,又简单又可靠。当然大赋没有这么做,而是从头发明轮子。即使重新发明轮子,关于注释这块我的意见是要么完全不用或少用,保证项目微型化,要么尽量实现JPA注解,不要重新发明新的注解。
2.我感觉一个开源项目的好坏,可以从它的类的数量来判断,大约在30~50个类左右就是一个好项目,太少了没有内容,太多了就太复杂,学习和维护困难。Hibernate、jFinal、SpringBoot等看起来高大上的项目,因为功能太多、源码复杂而让人生厌。大的项目可以将功能点分离,将各个功能做成子模块。这点上jFinal尤其失败,明明是IOC/ORM/MVC三个模块,非要捆绑在一起硬塞给你。
3.我正在做的jDialects项目,是试图将方言部分从Hibernate中分离出来,从而任何一个ORM框架都可以拿来使用,不必每个ORM框架都要重新发明轮子开发自已的方言实现。话说回来,如果当年Hibernate直接将方言部分做成一个独立的小项目,就没我现在要做的事了。
4.应该去除sqlManager,引入ActiveRecord模式,但是不要用怪异的$字符,要同时提供基类方式(照顾Java7用户)和接口方式(仅适用于Java8),这一点可以参考我的jSqlBox项目。jFinal引入了模板,BeetlSql也可以引入它的ActiveRecord模式,ActiveRecord可以极大简化编程,开源的好处就是可以光明正大地抄窃,包括概念上的和源码上的(在符合开源协议的基础上),互相促进。
5.对于模板类ORM框架,也必须提供直接在Java里运行SQL并注入参数的功能,Java有它简单明了的特点,SQl和代码分离只是一种理想情况,很多时候尤其在快速开发时,事接将SQl放在源码中更简洁。我不反对引入模板,但是ORM框架不能过分依赖于模板,模板只只适用于存放一些长SQL。
6.另外关于beetl模板的导航,可以尝试把beetl模板放在java注释里,见https://my.oschina.net/drinkjava2/blog/892309, 左键加鼠标一点就能定位SQL,不过这个只是个点子而已,我自已都觉得有点怪异。

40%,有这么多? 那用hibernate的不得哭死。可能行业不一样。

1 $字符的使用显得很怪异.

这个是博客的示例代码, 目的是为了突出. 引起看文者的注意.

文章只是推荐了一种做法. 这$Sql $Mapper是自定义的名字. 又不是框架提供的.

2 lombok的使用也使得程序非标准化

依然是推荐的一种做法. 不是每个人都能接受lombok, 读者若是接受不了用工具生成getter setter就是了.

同样的也不是每个人都会拒绝lombok.

3 beetlsql提供了一个注解:Sql. 就是为了不必定义过多的sql模板文件.

4 beetlsql文档有多少

文档多的是示例代码多.

而且并不是需要全部学完才能正常投入到工作.

将来我想我会出一个beetlsql系列的使用博客. 让大家知道实际项目中使用有多简单.

输入图片说明

如果用图来理解框架提供的功能点会清晰.

对方项目不更新,没有issues,可以关闭讨论了

闲.大赋(李家智) 关闭了任务

老实说对比mybatis我更愿意使用beetlsql, 国内开源和国外的很大差别在于持续的维护,赋予软件持久的生命力,希望你们一直持续下去

这坟贴还有人回啊。两年过去了,时间选择的结果看,是MyBatis先嬴一局(MyBatis-Plus插件),看来不能小瞧MyBatis的用户基数啊。它另避奇径,通过IDE插件的形式暂时缓解了XML中SQL文本查找定位的问题。

顽固在有些地方体现的淋漓尽致

@Cande 每个人业务背景不一样导致的,我说的理论,有些人会会心一笑,有些人会骂我什么不懂

这个月校企实训,企业的导师给我们讲SSM,今天我花了半天时间学了beetl :joy: ,真好用

只能对beetlsql的作者表示敬佩,就喜欢用国人的东西。

两年过去了,我对重构的想法已经改变了一些,不再看重(因为Java语法本身的原因,能实现但有点笨拙)。不过现在看看NutzDao(个人更看好NutzDao一些,除了它的实体映射搞的太复杂)和BeetlSql迟迟不加入ActiveRecord功能,简直是迷之自信,让人无语,为此我用关键字"ActiveRecord"搜索了一下issue,居然一个也没发现,真是奇了怪了。
另外问个问题,如果BeetlSql离开了Spring(太庞大了), 实现声明式事务有没有方案? 如果没有的话,实际上用 jfinal+beetlSql也是一种方案,虽然我知道你们两家有点小矛盾,但技术本身是无害的,Apache开源协议是鼓励抄袭的,甚至也可以考虑兼容它的模板引擎,当然这需要放下身段打破成见了。至于jSqlBox自带的IOC/AOP工具+声明式事务,因为只有一个人开发,维护不给力,就不推荐了。

beetlsql 支持多种模型,Bean ,Map 或者混合模式,相当于hibernate或者mybatis来说,支持的模型已经不错了
我个人工作经历无法理解ActiveRecord 模式的重要性,但有人封装了BeetlSQL能实现
BeetlSQL就是一个JDBC封装+SQL管理+内置SQL生成

简洁,你看用你的方法能不能一行写出以下功能:
new User().loadById("张三").setUserAge(13).update();
而且要横向比较,jFinal一直把ActiveRecord作为卖点,但是它那个有巨大问题,内部用map实现,不支持标准Bean模型,个别场合会出问题。你可以参考jSqlBox的ActiveRecord实现,只有三百行源码左右吧,三百行源码换一个卖点,不是太大的负担。
如果你只把BeetlSQL定位于JDBC封装+SQL管理+内置SQL,也可以干脆去掉实体功能。相当于与jSqlBox中的jDbPro模块等同。实体功能另起一个项目或插件。

@闲.大赋(李家智) 固执的人生才容易走上巅峰

@drinkjava2 你这个有人用beetlsql实现过了。

我的意思是代价很小为什么不直接整合到BeetlSQL里提供,即然你没这个打算就算了。

@drinkjava2 beetlsql 提供了一个最基础得sql管理,这个是SQLManager来完成,其他的Query风格,还是BaseMapper风格,还是ActivitiRecord风格,甚至是ORM,都是基于这个来完成的,beetlsql正打算把ORM独立出来,我意思是beetlsql可以扩展自己的风格,因为做好sql管理(自动生成),结果集映射,这就已经很好了

每个人执着的方向不一样,取长补短,才能形成一个更加成熟和完善的工具。期待@渔泯小镇的系列博客

登录 后才可以发表评论

状态
负责人
里程碑
Pull Requests
关联的 Pull Requests 被合并后可能会关闭此 issue
分支
开始日期   -   截止日期
-
置顶选项
优先级
参与者(14)
29 xiandafu 1678706040 388366 david0624 1578922314 123484 linziguan 1578918579 920504 drinkjava2 1578935955 389261 osheep 1578922338 5475 iohao 1653332816 132384 ydq 1671165271 4877085 z8g 1604290883
加载更多
Java
1
https://gitee.com/xiandafu/beetlsql.git
git@gitee.com:xiandafu/beetlsql.git
xiandafu
beetlsql
BeetlSQL

搜索帮助