admin管理员组

文章数量:1665525

 

1.前言

     不夸张的说你可以花上一天时间来看本文. 因为文中的内容给你将来节约的时间或许不止一天.

    我并不喜欢写博客(高质量的). 毕竟花费的时间精力太大了, 也不知道写的东西对阅读者是否有用. 如果没用这样往往浪费阅读者的时间.

    在写完一篇博客我时常需要从头到尾反复的看很多遍. 写的过程中还要考虑用户大概看到哪个时间段开始会疲劳, 当疲劳后又该采用什么样的方法让用户提神.

 

原因

    说实话, 我也没想到还会在写这么一篇博客. 这篇博客的出现是因为之前对项目完工后做的一个总结.

    因为项目过程中与数据库交互使用的是jfinal ORM. 上一篇文中(推荐先看,在读本文)叙述了jfinal ORM开发多表的项目中不便之处, 导致model编写会略乱与维护困难.

    经历过这些事后,打算重新寻找一种方案, 来解决这个在数据交互阶段拖慢开发速度, 且维护困难的问题. 就找到了beetlsql, 做了些demo. 接着就花了一天时间来写一篇项目总结的文章, 并给出了一种优雅的编码方式 java8 + lombok + beetlsql 这种编码方式达到了真正且符合我的极简且极速编码, 并打算用来解决上个项目的痛点. (我当时的出发点很简单. 希望给遇见同样问题的开发人员, 提供我这边将来的解决方案.)

    由于上文总结是使用的当前最新版本(3.0)的上一个版本. 由此引起广大jfinal使用者的诸多不满. (: 以当时博文发布时间计算当前时间). 所以这是我写这博客的原因之一.  

 

上文补充

上文中没有说详细的问题, 这里补充上:

    立项时间是2015.09, 交付时间是 2016.12.表数量超过150+. (上文是发表于2017.04.18, 当时没说明版本以及项目其实与结束时间, 我认为大家能根据表的数量推测出来大概的开发周期与使用版本. 毫无疑问这是我的错.)

    项目使用jfinal的版本是2.2. 上文我在评论中也说明为什么不使用jfinal的代码生成. 这里在说一次.

    因为团队使用的是阿里云的数据库, 习惯了这种开发环境.因为这样无论是在家还是公司,玩代码的时候都可以保持一致的数据.团队也不需要在本地安装数据库. 如果数据库是在云端的, 每次使用代码生成都会很慢. 这样的开发体验我是接受不了的.    ( 当然如果在本机安装有数据库, 生成代码是很快的. 我的是个别特殊的情况)

    上文在评论中我说过, jfinal中的俱乐部给我的感觉就是另外一种闭源. 因为很多功能在文档没有写得很详细,  但是在评论中了解到是因为作者忙和时间不够. 这才解开了我的迷惑. 这里我再次声明这个闭源的仅是我个人的感觉, jfinal是一直开源的.

    写上一篇博客时, jfinal最新版本是3.0, 项目使用的版本是最新版本3.0的上一个版本. 也就是2.2. 也是2016年的版本. 2.1是有问题的版本, 作者推荐升级到2.2. 

    在上一文中推荐多看评论区, 那里都有讨论.  

    在3.x中的jfinal 解决了上篇文章(基于2.x)的所有痛点.

 

讲解阐述

    如果你进来了, 推荐你慢慢品尝. 这是一种特别的讲解方式.越到后面收获也是越大的.

    这种讲解方式会给出示例比较, 图文并茂.

    好了, 到这里我推荐你先泡杯茶,舒缓下情绪. 当然才看到这里也没什么情绪需要舒缓的. 但我依然这么推荐你.

 

题外话一 :

我更喜欢的开源氛围是相互推荐好东西:

    像 t-io 这样作者,  他会与其他相同的框架进行性能PK(良性的). 并且会在PK失利的情况下在群里公然告诉大家结果, 这是让我很难相信的. (当然不是失利就没事了,  而是作者会进一步加强优化)

    像 ActFramework ( MVC全栈框架 ) 这样的作者(几乎把所有业余时间都贡献给了act), 即使拥有自己的模版引擎也会主动集成其他模版引擎的插件, 即使有自己ORM也会与beetlsql作者相互协作完成 act 框架的beetlsql插件.

    act吸引我的地方很多: 如非Servlet架构, 热加载特性. 在开发过程中,即使新增方法也不需要重启(比spring boot提供的快很多. 几乎0.x秒内), 直接刷新页面即可. 这种开发体验真的很棒. 很多优雅的用法(远不止这些. 将来有时间我会另开文来详细解说). act非常注重性能测试,每月都会有新的测试报告 (t-io, beetl, act 都是有性能测试报告的. 而不是全凭一些浮夸的宣传词).

    但是act现阶段文档很少. 因为是开源中的新秀, 所以需要点时间来完善.  (这也是我将来高度关注的MVC框架之一)

 

题外话二 :

    我想知道开源中国的软件更新资讯上头条的因素有哪几个. 为什么有的开源软件可以占据时间长, 为什么有的时间却那么短. 希望能把这些东西量化给所有的开源作者

    作为普通用户的我, 只希望开源中国能公平的对待每一位开源者作者或是每一个开源软件. 而不要特殊照顾某几个, 私下感情好是一回事. 最好是能公事按公事的原则办, 不讲私人情面 .

    还有一个问题是上一篇文章我记得是95条评论, 为什么会变成94条评论了.  我只知道只有博主可以删除评论, 我是没有删除评论行为的习惯的, 也不会去删除任何一条评论. 如果是我误点了请帮忙恢复. (也有可能是我眼花了或许真的是94条. 如果是我眼花了就不需要理会这个问题)

 

感觉跑题了. 正式开始吧.

 

2.正式开始

    好了, 说了一大堆终于可以进入正题了. 上一文中说的是总结,版本不是最新的. 这次是beetlsql与jfinal的对比.(都取各自当前的最新版本 ).

    说实话,我并不喜欢对比. 对比伤感情. 那么既然开始了就会有一说一.

    文中所有示例都是有源码并且可以运行, 读者可以下载源码运行 (当然不需要这么着急的下载源码, 文中最后还会给出下载地址).

 

为什么不是mybatis.

我现在选框架的标准是

  1. 开源
  2. 性能
  3. 文档是否很全 (中文优先)
  4. 能否和作者直接沟通 (如果沟通都没有, 只能等着出什么就用什么)
  5. oschina git上commitsissues是否很多 (数量多表示更新活跃和参与的人多)
  6. 是否接受PR或者是否接受合理的issues (用户使用的开源软件. 如果觉得功能是合理的, 但是框架又没有该功能, 可以在其对应的git上issues)
  7. 更新是否足够快, 有时一个issues真的很着急.

显然这就是我选择了beetlsql的原因之一.

后面也会给出一种较爽的编码方式: java8 + lombok + beetlsql (比上一文中的更优雅. COC原则)

文中最后会给出demo地址.

 

为什么不是jfinal的sql管理

    为什么不直接使用jfinal的sql管理, 而要寻找其他的呢.

    2.2 到 3.0 的这个版本发布. 改进得最好的是SQL的管理模块了, 因为这是个曾经让我头疼的点. 因为以前喜欢所以无条件的坚持着这样的方式编写代码. 但是喜欢归喜欢, 工作是工作. 我现在更倾向往后几步考虑问题.

    之前把sql业务放入Model中看作是一个充血模型, 同时也充当Dao的职责. 项目的表越多,类也越多,SQL也更为复杂的情况下.维护是个头痛的事情. 

    jfinal经过五年的开源也开始认识到了这种不足. 终于jfinal 3.0开始(2017年)引入了sql管理模块(类似mybatis的开发方式), 来解决维护困难的问题.

    既然都是外部管理sql, 所以我得先了解其他框架是如何做的 (避免上个项目受到的教训, 毕竟我已经过了喜欢一个框架就接受它一切的年纪了).

所以当做到beetlsql demo时, 发现dao可以如此的简洁与优雅. 毫无疑问, 这也是我选择beetlsql的原因之一.

 

各自ORM的sql文件管理

这里不讨论ORMsql文件管理格式.

    无论是jfinal的.sql, mybatis的.xml, 还是beetlsql的.md. 万变不离其宗. 对我而言都是管理sql的文件, 文件里面都可以实现逻辑. 唯一不同点就是各自的语法和文件的后缀而已.

    这里不会比较管理sql的文件语法. 因为这个讨论是很难终止且没有结果的. 你说你的好, 他说他的好, 就好比编程语言的争论也是没有结果的. 对我而言我需要关注的是哪个能给我提供更优雅解决方案. 所以对你而言呢?

 

sql文件管理的profile概念

    beetlsql的sql文件管理中有个类似profile的概念. 让你更方便地组织在不同运行环境(mysql postgres oracle 等...)下的配置文件

大概sql目录样子如下:

/sql/bee.md (findA,findB)
/sql/mysql/bee.md (findB)
/sql/oracle/bee.md(findB)

这种情况下,findB可以用在postgres等其他数据库. 如果是mysql,或者oracle,就用各自目录下的.

/sql目录下的 bee.md 这个sql文件定义了两个方法 findA 和 findB.

/sql/mysql 目录下同样有一个bee.md的sql文件, 但是文件只定义了一个 findB 方法.  那么beetlsql的加载方式是先加载/sql/bee.md文件. 这时候你使用的是mysql数据库, 那么会把mysql下的bee.md文件的方法覆盖之前所加载的配置项 (就是把mysql下的bee.mdfindB方法覆盖之前所加载的).

据我所知目前ORM的sql文件管理中, beetlsql是唯一拥有profile概念的.

 

全文内容摘要

这里列出编程中比较常用几个方面来进行比较. 以及后续会引发的问题 (这往往是许多开发者欠缺考虑的, 大部分是沉侵在部分示例代码的美妙中).

  1. 呈现各自的代码 (代码的编写方式)
  2. 打印各自sql的信息 (在开发过程正可以看见是否被执行了)
  3. 各自sql文件写错时的异常信息
  4. 面对各自的耦合问题 (重点)

 

下面各举两个方法执行同样的sql查询做比较 (这次是多表查询)

  • 三个条件参数来查询单个对象 - findOne
  • 两个条件参数来查询一个对象列表 - findAnimals

 

3.极简: jfinal 实体类

    下面会用两个类来演示jfinal, 也是官方推荐的代码存放方式. (原来是全部在model中)

  1. AnimalService (官方推荐之前的dao代码全部写在service层中)
  2. Animal (官方推荐继续让model保持清爽)

 

service 代码示例 :

public class AnimalService {

    private static final Animal dao = new Animal().dao();

    public List<Animal> findAnimals(int animalInfoId, int age) {

        Kv pars = Kv.by("animalInfoId", animalInfoId).set("age", age);

        SqlPara sqlPara = dao.getSqlPara("jfinalAnimal.findAnimals", pars);

        return dao.find(sqlPara);
    }

    public Animal findOne(String name, int animalInfoId, int age) {

        Kv pars = Kv.by("animalInfoId", animalInfoId).set("age", age).set("name", name);

        SqlPara sqlPara = dao.getSqlPara("jfinalAnimal.findOne", pars);

        return dao.findFirst(sqlPara);
    }
}

个人看法:

    业务层同时存放业务逻辑与dao相关的代码, 当然我不知道是好是坏.

    但这样会造成service臃肿 (当然这里只是示例代码, 真正的项目service不会这么点代码), 间接导致service层维护费时. 只是把之前model的dao相关转移到service层中

    我更倾向各层之间的职责分明. 寻找与维护相关代码也方便, 如: (xxxController xxxService xxxDao)

 

model 代码示例 :

    model使用sql管理模块并配合代码生成后(完全无需手动敲击getter setter), 代码更是达到了极简.

    做到了. Model(无.xml、无annotaion、无attribute、无getter、无setter)

public class Animal extends BaseAnimal<Animal> {

    /**
     * 这样的字段是没办法代码生成的
     *
     * @return animal_info表的字段: 分布区域
     */
    public String getRegionName() {
        return this.getStr("regionName");
    }
}

 

正如 jfinal作者 在上一文章在评论所说的:

    完全不需要 mabatis 中 xml、mapper、interface、annotation、handler、interceptor 这些概念,每增加一个概念都是要花时间学习

真的做到了完全站在用户的学习成本上考虑.

当然我相信jfinal不引入这些概念只是暂时的, 任何框架都是有一个不断演进的过程的. 如果用户需求强烈,这些概念是不可避免的.

 

这次的示例中毫无疑问的是比以前简单多了, 也达到了大部分jfinal使用者的极简了.

    没有了大量的getter setter了. 但如果是多表查询,还是需要手动添加某些getter的. 但是这个是能接受的.问题不大.

    在也不用手动敲击逻辑判断添加动态sql条件了. 这样无论sql有多复杂, 也不会造成代码的混乱.

 

提出一个假设 - X

    我向左边的观众提出一个假设X. 如果这个Model是你接手的一个二手项目, 你能告诉我这个表大概有哪些字段吗?  说实话, 你不继续往下看本文就能说出来你真神了.

    如果你接手的二手项目超过20张表或者说开发一个比较耗时的项目, 时间过得比较久远了 (8个 10个月后). 这种隐藏getter setter的方式真的是你能接受的吗. 你可以的,我相信你, 你只需要点击一下父类就能知道大概有哪些字段了.

    如果接受二手项目的哥们是没接触过jfinal的ORM呢? A: 我TM都走了,还需要管那么多吗. B: 有道理.

 

4.匠的追求, 极简之上 : 

    使用Beetlsql替换JFinal的Model

    Effective Java 第18条:接口优于抽象类

    java8 + lombok + beetlsql   (无.xml、无.sql、无getter、无setter、Dao无注解、无继承、无框架的强耦合).

@Data                                       // 提供getter setter toString 等方法
@FieldDefaults(level = AccessLevel.PRIVATE) // 属性默认都是private
@Table(name = "animal")                     // 实体类与表映射
public class BeeAnimal implements $Sql { // 实现$Sql接口可以在对象上使用save,update,delete方法(不是必须的)
    int id;
    int age;
    String name;
    String description;

    /** animal_info表的字段: 分布区域 */
    String regionName;

    /**
     * 匠的追求, 极简之上.
     * 无注解 无实现类 无框架的强耦合. 直接可以使用.
     * 自定义sqlDao处理类. (如果没有自定义sql就没必要定义这个接口).
     */
    public interface Dao extends $Mapper<BeeAnimal> {
        Dao $ = mapper(Dao.class);

        List<BeeAnimal> findAnimals(int animalInfoId, int age);

        BeeAnimal findOne(String name, int animalInfoId, int age);
    }
}

    Dao无注解 无实现类 无框架的强耦合

    细心的朋友会发现, 比起 上个版本 注解都没了 (COC原则).

是的,你没有看错. 虽然注解没了,但依然可以正确的运行代码. 是不是感觉清爽了很多.

    这样连beetlsql的注解都不需要学习了, 虽然学习成本不大. 但是那个注解在我眼里,就是重复的代码.所以我想办法把这个注解给消灭掉, 让dao仍然能正常的运行.

    方法也没有实现体 : 也不需要记忆更多的调用方法名与方法体内各种花哨的使用方式. (对我而言这就是有氧运动).

    我突然听见有哥们在问这个是怎么做到的, 你用了第三方库吗? 我想说的是没有用第三方库, 而且实现很简单.

    当然, 不要以为这样就是本文的重点. 文的开头就说了, 越到后面收获是越大的. (先让我们跳过这段, 后面回过头来在讨论这个).

    也别着急的问我无框架的强耦合是什么意思. 给我点时间,后面会为你一一道来.

 

提出一个假设 - Y

    好了. 让我们再次进入一个假设.

    我向右边的观众提出一个假设Y. 如果这个实体是你接手的一个二手项目(或者说项目时间长了你回过头来看这个类), 你能告诉我这个表大概有哪些字段吗? 

    这就是一览众山小的感觉. 我相信你可以分析得出这个表有哪些字段.

 

5.各自打印sql的内容 (编程的效率)

jfinal 打印情况

当jfinal执行findAnimals方法时, 简单的打印了执行的sql语句. (细心的朋友会发现打印的toString中仅打印了字段, 而比较重要的信息类名都没有. 当然这并不会影响什么)

 

beetlsql打印情况

    打印了执行的sql

    打印了sql语句中使用的参数值

    打印了调用该接口的地方, 只需要在控制台点击一下, 就能跳转到调用点.

    打印了执行时间  

    打印了返回的结果数量

    打印了sql所在.md文件的对应方法名.

 

jfinal与beetlsql相比, 只是简单的打印了一下执行的sql.

如果一个业务类(service)的某个方法中调用了多个Dao的方法. 在beetlsql可以快速的定位到Dao的调用点, 以及使用的是哪个.md文件的sql一目了然.

也不需要log或者debug查看传入的参数是否是理想的, 以及返回的结果数量.. 直接在控制台查看. 这点对于使用者来说方便了很多.

在这点上beetlsql做得很细腻.

 

6.各自异常信息的报告 (编程的效率)

    异常信息对开发者来说是定位与分析题问最重要的信息之一. 也会间接影响到编程的效率.

    毕竟谁都不愿意让系统把程序引发bug信息给吃掉.你试想一下如果在编写一个程序, 某部分引发内存溢出而导致奔溃.假设java就不给你抛出内存溢出的异常信息. 我相信你很难分析出是什么原因.

    如果没有错误信息帮助排除这个错误. 我不知道你需要多久, 但我知道我需要很久.  所以异常信息精准与否会间接影响到编程开发的效率, 效率慢会连锁性的影响项目进度.

    什么? 你不相信这个会影响你的开发效率? 没关系,请时刻注意你所在的技术群. 总会有些小白说程序报错, 接着下面也会有哥们说能不能把你的报错异常信息发一下. 因为他需要这些信息来定位问题的所在.

    让我们特意在sql管理文件中输入一个错误的语句, 就把.md与.sql的变量赋值相互交换一下写法. 就是调换一下各自的变量赋值语法, 把 jfinal中#p(age)beetlsql中#age# 相互调换一下.

 

6.1.jfinal异常相关

1.jfinal .sql文件内容

文件的倒数第八行特意写错. and a.animalInfoId = #animalInfoId# 写成beetlsql的赋值语法

#namespace("jfinalAnimal")

#sql("findAnimals")
select a.*, info.regionName from animal a left join animal_info info on a.animalInfoId = info.id where 1=1
#if (age != 0)
and a.age = #p(age)
#end
#if (animalInfoId != 0)
and a.animalInfoId = #p(animalInfoId)
#end
#end

#sql("findOne")
select a.*, info.regionName from animal a left join animal_info info on a.animalInfoId = info.id where 1=1
#if (age != 0)
and a.age = #p(age)
#end
#if (animalInfoId != 0)
and a.animalInfoId = #animalInfoId#
#end
#if (name != null)
and a.name = #p(name)
#end
#end

#end

 

2.jfinal 的测试用例
@Log4j
public class JfinalAnimalTest {
    @Test
    public void fidnOne() {
        String name = "海豚";
        int animalInfoId = 2;
        int age = 9;
        Animal animal = Animal.dao.findOne(name, animalInfoId, age);
        log.info(animal);
        log.info(animal.getRegionName());
    }
}
3.jfinal 的异常信息

控制台的信息

com.jfinal.plugin.activerecord.ActiveRecordException: 
com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: 
You have an error in your SQL syntax; check the manual that 
corresponds to your MySQL server version for the right 
syntax to use near 'and a.name = '海豚'' at line 4

    jfinal的异常信息出来了. 很不精确并且误导了使用者 (错误并不是在文件的第4行, 也没有明确的说是在哪个.sql文件里面). 说实话, 这里只列举了一个.sql文件. 如果你的项目大于30个.sql文件, 并且.sql文件的语句很多. 我想知道你怎么查找.

 

6.2.beetlsql异常相关

1.beetlsql .md文件内容

文件的倒数第五行特意写错. and a.animalInfoId = #p(animalInfoId) 写成jfinal的赋值语法

findAnimals
===
select a.*, info.regionName from animal a left join animal_info info on a.animalInfoId = info.id where 1=1
@if (age != 0) {
and a.age = #age#
@}
@if (animalInfoId != 0) {
and a.animalInfoId = #animalInfoId#
@}

findOne
===
select a.*, info.regionName from animal a left join animal_info info on a.animalInfoId = info.id where 1=1
@if (age != 0) {
and a.age = #age#
@}
@if (animalInfoId != 0) {
and a.animalInfoId = #p(animalInfoId)
@}
@if (name != null) {
and a.name = #name#
@}

 

2.beetlsql 测试用例
@Log4j
public class BeeAnimalTest {
    @Test
    public void findOne() {
        String name = "海豚";
        int animalInfoId = 2;
        int age = 9;
        BeeAnimal animal = BeeAnimal.Dao.$.findOne(name, animalInfoId, age);
        log.info(animal);
        log.info(animal.getRegionName());
    }
}

 

3.beetlsql的异常信息

控制台的信息

>>10:43:39:缺少符号(PARSER_MISS_ERROR):@ 位于19行 资源:beeAnimal.findOne
缺少输入 '模板的占位结束符号' 在 '@'
16|}
17|@if (animalInfoId != 0) {
18|and a.animalInfoId = #p(animalInfoId)
19|@}
20|@if (name != null) {
21|and a.name = #name#

    beetlsql的异常信息出来了. 误差是正负1行.  在可接受范围内.

    信息打印出.md文件的行数及其内容, 通过异常信息查找bug简单多了. 连错误所在的文件名beeAnimal及使用的方法名都打印了.

beetlsql的异常信息定位不得不说比jfinal的异常信息,强大与精准了很多!

 

7.各自所在编程工具中的提示 (编程效率)

    工具的强大之一在于可以提示对象的方法. 从而不需要你手动敲击方法全名, 只需要 对象. 工具就可以列出当前对象的所有方法, 从而加速我们的编程速度.

    这里之所以拿出来说,是因为编程中几乎每天都需要面对的.

 

jfinal中的方法提示 (对象.).

   

 

    细心的朋友会发现滚动条的长度还可以继续往下翻, 因为一个屏幕已经装不下了. 虽然提示过多问题并不大, 但是无形之中就是给开发人员增加了干扰项(大部分是业务无相关). 开发人员每次只想使用自己在dao中定义的方法, 却蹦出这么多提示.

 

beetlsql的方法提示 (对象.).

    

    滚动条都没有? 是的, 你没有看错, 的确就这么少. 没有一寸是多余的. 你可以看见接口中只定义了两个方法. 多余的提示都是Object对象的(不是接口本身定义的).

    没有了那些多余的干扰项, 还你一片精准的业务提示.

        我仿佛又听见有哥们在问 : 你不继承Beetlsql的BaseMapper吗? 怎么可能,你是怎么做到的? BaseMapper不是内置了很多方法吗. 怎么在这里会没有提示, 你是不是作弊了.

    没有作弊. 本文中的示例都是开源的, 大家都可以下载下来运行. 地址: demo

    当然, 不要又以为这样就是本文的重点. 文的开头就说了, 越到后面收获是越大的. (先让我们跳过这段, 后面回过头来在讨论这个).

  

8.md文件来管理sql (视图部分)

    Markdown的介绍在这里不说了.md文件管理sql有什么好处. 在beetlsql官网上有部分说明. 下面我还会介绍一些额外扩展. (所有.md文件, 都可以被md相关工具打开).

现在很多开源的md工具. 如果基于开源的md工具基础上添加几种beetlsql视图预览.mdsql文件会发生什么呢?

 

MD工具中的: 正常的视图

    sql关键字都高亮,是不是看起来你的sql更加的好维护与直观了!

 

MD工具中的: sql视图

    帮助你去掉beetlsql中逻辑语句变成下面这种预览方式呢. 是不是看起来比上面的正常md视图更加的精简了!

 

MD工具中的: sql美化视图

    在预览界面的时候给你直接美化sql并帮助你去掉beetlsql中的逻辑语句

是不是看起来又比上面的sql视图更加的好维护与直观漂亮了, 方便你直接复制语句到sql工具中运行!

9 阅文过半 听歌一曲

    看到这里. 我们不妨喝上一口茶, 舒缓下情绪. 并且我打算听上一曲:

歌名: 老司机带带我 (奔驰车版)

:老司机带带我我要上昆明啊, 老司机带带我我要去省城
:要上昆明车子多,半路拦我为什么
:阿里里~阿里里~阿里阿里里

:老司机听我说我会唱山歌啊, 老司机听我说小妹嘴皮薄啊
:管你嘴皮薄不薄我的老婆等着我
:阿里里~阿里里~阿里阿里里~~阿里里~阿里里~阿里阿里里

:老司机你瞧瞧小妹桃花色啊,老司机你看看小妹生的白啊
:管你两个白不白大哥不想玩素格
:阿里里~阿里里~阿里阿里里

:老司机你望望小妹牡丹样啊, 老司机你望望小妹胖不胖啊
:管你两个胖不胖野味大哥不想尝
:阿里里~阿里里~阿里阿里里~~阿里里~阿里里~阿里阿里里

:老司机身体差不会贪野花, 老司机身体差不会放野马
:小哥从来不贪花家有老婆和娃娃
:阿里里~阿里里~阿里阿里里

:老司机有这土小妹会跳舞啊,老司机大老粗我会时装步啊
:我们陪你唱山歌你的车子给我坐你想想你说说哪个划得着
: 阿里里~阿里里~阿里阿里里

:小妹子听我说我也爱玩乐~小妹子听我说人品也不错哦
:我的车子给你坐你们陪我唱山歌我想想我说说我也划得着

:小妹子听我说我也爱玩乐~小妹子听我说人品也不错哦
:我的车子给你坐你们陪我唱山歌我想想我说说我也划得着
:老司机真善良我心多宽畅啊
:小妹子陪你逛城市好风光
:昆明城市好漂亮盛世歌声传四方
:阿里里~阿里里~阿里阿里里~~阿里里~阿里里~阿里阿里里

 

    这首歌的特点是第一次听:这是什么啊. 第二次听:什么垃圾, 呵呵. 第三次听: 阿里里~阿里里~阿里阿里里, 你会不由自主的跟着旋律走!  (有些歌就是要听多次才能领略其中的经典. 就像是本文一样看多次才能领略其中的精髓)

    好了, 小休过后我们继续. 这时候你的情绪也该舒缓得差不多了.

 

10.无框架的强耦合

    在本文中上面提到了: 无框架的强耦合.

    现在为你一一道来.

    在beetlsql之前的版本中, dao接口必须继承BaseMapper接口才能工作(让dao与数据库交互). 显然我认为这是一个不好的行为. 

 

直接使用BaseMapper的缺点:

  • 强行给你的dao添加内置方法
  • 内置的方法过, 或者过 (对某些用户而言)
  • 必须使用内置的方法名 (如果有用户不喜欢这个名字怎么办, 我就有不喜欢的方法名. 所以一定还有其他用户不喜欢这些方法名的)
  • 用户代码与框架耦合性略强, 将来移植不方便.

 

提交PR, 解决上述问题.

    在经过一系列的beetlsql源码查看后, 发现这些问题都是可以解决的.

所以我在04.27号提交了一个 PR , 值得高兴的是 beetlsql的作者接受了这个PR.

    @闲大赋 在查看了PR后. 同意这样做. 觉得这个想法不错. 不强绑用户. 让用更自由.

这个PR的大概内容是:

  • 用户可以定制属于自己的 BaseMapper (基接口), 例如这个定义的基接口的名字叫: MyMapper. (这样业务代码就能跟beetlsql完全解耦).
  • 定制你喜欢的内置方法名.
  • 你所有的Dao都继承你自定义的这个 MyMapper (基接口), 不在强制依赖BaseMapper .

 

文字说得再多, 不如上码清晰.

自定义基接口

自定义一个基接口 MyMapper 的代码:

如果你还是喜欢BaseMapper接口提供的方法, 只需要从BaseMapper复制方法到这个接口中, 就直接可以使用. 直接 (复制 复制 复制)

/**
 * 不继承beetlsql的 {@link org.beetl.sql.core.mapper.BaseMapper} 也能拥有内置实现 <BR>
 * 只需要从BaseMapper复制方法到这个接口中, 就直接可以使用. 无需配置. 直接 (复制 复制 复制)
 * create time : 2017-04-27 18:12
 *
 * @author 朱洛毅
 */
public interface MyMapper<T> {
    long selectCount();
    List<T> selectAll();
}

 

设置 MyMapper 为基接口, 可以添加多个基接口.

public class AmiInnerProxyMapperInvokeTest {
    @Autowired
    protected SQLManager sqlManager;

    @Test
    public void testAmi() {
        /*
         * 松耦合编程. 不强制用户与beetlsql耦合, 站在用户的角度是为了让用户无伤切换到其他orm框架.
         * 设置一个自定义基接口, 并获取基接口配置构建器
         */
        MapperConfigBuilder builder = this.sqlManager.setBaseMapper(MyMapper.class).getBuilder();

        /*
          这两个方法名与 MyMapper接口保持一致.
          为了告诉beetlsql. 遇见这个方法名, 帮我用对应的实现类来处理.
          这样扩展性更高, 更自由.不必等着开源作者来提供实现.(解决你不能在基接口定义方法的烦恼)
          */
        builder.addAmi("selectAll", new AllAmi());
        builder.addAmi("selectCount", new AllCountAmi());

    }

}

在设置 MyMapper 为基接口后, 并定制了两个内置方法. 是不是感觉为你的程序与框架解耦就这么简单.

这样你会发现在你的业务代码中, 没有任何的beetlsql的代码.

    真是改动一小步, 解耦一大步. 这就是为什么推荐你实现自己的MyMapper不要使用BaseMapper的原因. 所以请制定属于你自己的基接口. (当然你一定要使用BaseMapper也是可以的)

    在这里希望beetlsql作者心中对我的这种, 推荐用户不要使用框架的BaseMapper行为有不满. 毕竟我也是beetlsql的用户之一, 所以我一定是优先站在用户的角度上考虑问题的.

 

自定义(基接口)的好处:

  1. 可以定制属于你自己基接口的方法名 (解决你的强迫症)
  2. 可以扩展你的基接口方法, 扩展非常简单. (解决你不能在基接口定义方法的烦恼)
  3. 与框架强耦合说再见.
  4. 自定义的基接口可以直接使用beetlsql提供的BaseMapper方法, 只需要定义与BaseMapper一样的方法名 (直接从BaseMapper中复制方法到你的基接口即可)
  5. 无伤切换到其他类mybatis的ORM框架.
  6. 更像是一场无限制格斗.


11.脑海中的众山小图

    由于大部分的人在脑中是很难绘制大概图的, 这里给大家画出了我脑中的图.

  • 图左边是你的dao接口部分
  • 图右边框架提供的服务
  • 图中间五边形就是beetlsql提供的基接口注册 (解决与框架耦合的关键点)

由于你的Dao部分没有任何的beetlsql的代码, 切换到mybatis或者说将来有更优秀的ORM框架是没有任何压力的.

正由于是基于接口编程, 所以可移植性超高.

 

 

12.框架编程细节篇 (重点)

站在用户的角度考虑事情,我们是认真的.

    除了代码编写量少之外, 还考虑到了用户编写代码时的细节(没有多余的提示, 精准定位提示, 无干扰项. 这种精准提示配合IDEA编程,简直是一绝.).

甚至还考虑了用户代码与框架的强耦合问题. (这样做为了用户在编写同样代码的情况下, 可以随时切换mybatis, 或者说将来有更优秀的orm框架代替beetlsql, 你们也可以随时切换过去).

 

 

回头讨论之前遗留的两个问题

问题一 : 干扰项少

    前面在工具中(对象.)提示为什么这么少, 就能说得通了. 因为我自定义了一个基接口. (名字: $Mapper)

问题二 : Dao无注解

    遵循COC原则. Dao无注解参数与你之前使用注解的名字一致. 处理方式就是java8的api.

不过我还在想要不要新开一篇文来介绍这些. 如果超过800个赞我就新开文章来写个详细的. 否则就是都能读懂示例中的源码了 (也顺便省了我的笔墨和时间).

 

13.jfinal的ORM目前对你的限制有哪些:

  1. 必须使用代码生成器(也是jfinal官方推荐的编码方式), 否则就会引发 上一篇文中 的问题.
  2. 每当有新增的业务需要添加新表, 或表字段的修改,增加,删除. 需要运行一次代码生成.
  3. 强制多层次继承 Animal extends BaseAnimal,  BaseAnimal extends Model. 这样也变相的把你的代码捆绑在框架上. (继承层次只会多不会少)
  4. 所有生成代码的成员属性都会强制生成getter, setter.(如果你不想提供某些属性的setter方法, 对不起目前无法做到.)
  5. 代码生成的父类Model必须放在同一个目录下 (如果你没有强迫症, 这条可以忽略).
  6. 手动添加配置写好的.sql文件 (与早期版本的model手动添加有异曲同工之妙).
  7. 理解Model与传统java bean的区别概念

不过这些种种的限制换个角度来看, 也可以算是给用户的一种别有风味且独特的 编程习惯.

 

对第5条的额外补充 如果你的module结构不是类似下面这种方式的, 那么就不算限制.

org.test.module.account.controller

org.test.module.account.service

org.test.module.account.model

org.test.module.home.controller

org.test.module.home.service

org.test.module.home.modle

 

 

14.推荐的编程方式

上面介绍的 java8 + lombok + beetlsql 你也许可以尝试一下.

    因为这样做到了:  (无.xml、无.sql、无getter、无setter、Dao无方法体、Dao无注解、无继承、无框架的强耦合). 并且不引入更多的概念 (屠龙宝刀点击就送).

    方法也没有实现体 : 也不需要记忆更多的调用方法名与方法体内各种花哨的使用方式. {{ 无招胜有招 }}. (对我而言这就是有氧运动).

    无方法体. 这个名字让我想起了中学语文老师曾经说过的这么一句话: 多做多错, 少做少错, 不做就大错特错.

  1. 在这里少做指的就是只需要定义方法名与方法参数, 既是做最少的事情. - 少错.
  2. 不做指的就是什么都不想做(一种懒政或啃老的行为).  - 大错特错
  3. 多做 ... 有氧运动, 可以锻炼身体.

    一定要离开jfinal的ORM吗? 答案不是的. 如果你一定要坚持我一定也是支持的. 就像是 Effective Java 这本书给了我很多建议, 但也不是每一条我都会在实际中用上. 所以喜欢jfinal ORM这种编程方式我推荐你们继续保持, 因为相比3.0之前的版本已经很极简了.

    如果你使用过jfinal的ORM但感觉到有不满意的或者对本文推荐的编程方式有心动的感觉. 不防尝试这种编程方式. 你会发现你根本不需要学这么多概念, 就能开始工作了. (项目越大这种编程方式的优势就越能体现. ).

    这里我想问各位一个问题, 如果把jfinal的ORM替换成这种方式. 会轻松很多吗? 或者说jfinal MVC + (java8 + lombok + beetlsql) 这种编程方式呢? (不用回答我, 在心里回答自己就可以了. 毕竟每个人心中的答案都是不同的)

 

 

 

赞的鼓励

    最后如果觉得文章对你有用的或者说的确有所收获的就点个赞鼓励一下.

    或者说将来你使用上了这种方式, 并且觉得这样编码的确很爽在回过头来点赞也可以. 好的原创文章离不开你的鼓励.

请不要吝啬你的赞.

    你点赞只需要动一下鼠标, 而我可以得到你的鼓励, 从而出更多的原创文.

    解决dao层与框架之间的强耦合, 才是本文的重点

 

    大家请在评论区留言, 我们相互讨论,相互学习. (这里不删除任何一条评论)

                        -- 渔泯小镇

示例源码: demo

转载于:https://my.oschina/iohao/blog/894137

本文标签: 优点缺点jfinalbeetlsqlORM