业务逻辑经常包含较复杂的流程和计算,同时涉及数据库的读写。由于授权麻烦、影响数据库安全、无法迁移、技术要求高、编写困难等原因,很多场景不适合用存储过程实现业务逻辑。因为不擅长复杂的流程处理,SQL也不适合单独实现业务逻辑,必须与JAVA等高级语言配合才行。但SQL和高级语言的语法风格迥然不同,数据结构差异巨大,导致两者难以配合,开发效率始终无法提高。在这种情况下,ORM适时而生。

ORM是一种将结构化数据(表/记录)映射为高级语言的对象的技术,这样就可以用统一的数据结构和语法风格实现业务逻辑其根本目标还是提高开发效率。常见的ORM技术有Hibernate、QueryDSL、JOOQ等。

在数据结构和语法风格的统一方面,ORM表现优秀,已经在大量项目中得到广泛应用。但ORM仍有很多不足,主要表现在:缺乏专业的结构化数据类型,集合运算不够方便,读写数据库时代码繁琐,不支持热部署,库函数不够丰富,复杂计算难以实现。ORM的这些缺点导致业务逻辑的开发效率没有明显提升,有时甚至大幅降低。

作为JAVA下开源的结构化数据处理类库,SPL可以解决ORM期望目标,甚至有更好的表现。

SPL用统一数据结构和语法风格实现业务逻辑

比如一个常见的业务逻辑:根据规则计算出奖金,向数据库插入包含奖金字段的记录。可用下面的SPL代码实现:

A B C
1 =db=connect@e("dbName") /连接数据库,开启事务
2 =db.query@1("select   sum(Amount)  from sales where
  sellerID=? and year(OrderDate)=? and month(OrderDate)=?",
  p_SellerID,year(now()),month(now()))
/查询当月销售额
3 =if(A2>=10000   :200, A2<10000 && A2>=2000 :100, 0) /本月累计奖金
4 =p_Amount*0.05 /本单固定奖金
5 =BONUS=A3+A4 /总奖金
6 =create(ORDERID,CLIENT,SELLERID,AMOUNT,BONUS,ORDERDATE) /创建订单的数据结构
7 =A6.record([p_OrderID,p_Client,p_SellerID,p_Amount,BONUS,
  date(now())])
/生成一条订单记录
8 >db.update@ik(A7,sales;ORDERID) /尝试写入库表
9 =db.error() /入库结果
10 if A9==0 >A1.commit() /成功,则提交事务
11 Else >A1.rollback() /失败,则回滚事务
12 >db.close() /关闭数据库连接
13 return   A9 /返回入库结果

上面的SPL代码使用统一的数据结构、语法、函数,就可以完成整个业务逻辑的开发,包括数据库读写、事务处理、流程处理、数据计算。

JAVA代码本身不包含业务逻辑,通过JDBC驱动引用上面的SPL脚本,就可以实现业务逻辑。

…
//省略参数的获取过程
Class.forName("com.esproc.jdbc.InternalDriver");
Connection conn =DriverManager.getConnection("jdbc:esproc:local://");
CallableStatement statement = conn.prepareCall("{call InsertSales(?, ?,?,?)}");
statement.setObject(1, d_OrderID);
statement.setObject(2, d_Client);
statement.setObject(3, d_SellerID);
statement.setObject(4, d_Amount);
statement.execute();
...

JAVA调用SPL的形式与调用存储过程相同,且无须数据库管理员授权,不影响数据库安全,便于迁移,方便调试,处理能力更强,代码更简单。

SPL提供了专业的结构化数据对象,具备完整的内外数据交换能力和流程控制能力。

结构化数据对象

业务逻辑围绕结构化数据对象展开,SQL、ORM、SPL都内置专业的结构化数据对象,并据此提供了各自的结构化数据计算函数。类似ORM的实体/List<实体>,SPL的结构化数据对象是记录/序表。

取记录的字段值:=r.AMOUNT*0.05

修改记录的字段值:=r.AMOUNT= T.AMOUNT*1.05

序表取一列:T.(AMOUNT)

序表追加记录:T.insert(0,31,"APPL",10,2400.4)

基于序表,SPL提供了丰富的SQL式计算函数。

过滤:T.select(Amount>1000 && Amount<=3000 && like(Client,"*bro*"))

排序:T.sort(-Client,Amount)

去重:T.id(Client)

汇总:T.max(Amount)

分组汇总后过滤:T.groups(year(OrderDate),Client; avg(Amount):amt).select(amt>2000)

关联:join(Orders:o,SellerId ; Employees:e,EId).groups(e.Dept; sum(o.Amount))

交集:T1.id(Client) ^ T2.id(Client)

TopN:T.top(-3;Amount)

分组topN:T.groups(Client;top(3,Amount))

此外,SPL还提供了字符串、日期、数学等多种函数,充分满足业务逻辑中的计算需求。

内外数据交换能力

将外部数据库的记录读为内部结构化数据对象,ORM主要通过主键查询、HQL查询、SQL查询、链式编程等方式。类似地,SPL提供了query函数执行SQL的方式。

取单条记录:

=r=db.query("select * from sales where orderid=?",201)

取序表(记录集合):

=T=db.query("select * from salesR where SellerID=?",10)

为了增强可移植性,SPL提供了通用SQL,使用sqltranslate函数可将通用SQL转为主流方言SQL,仍然通过query函数执行。这不是本文重点,不展开说了。

将内部结构化数据对象持久化到数据库,ORM主要通过save(新增的实体)、update(修改的实体)、delete(删除的实体)等方式。类似地,SPL使用update函数。

比如,原序表为 T,经过增删改之后的序表为 NT, 将变化结果持久化到数据库:

=db.update(NT:T,sales;ORDERID)

流程控制能力

业务逻辑的难点在于复杂的流程控制(判断和循环)。SQL缺乏流程控制能力,ORM把数据库表映射为JAVA对象,主要目的就是为了更方便地利用JAVA的流程控制能力。类似JAVA,SPL具备完整的流程控制能力。

分支判断语句:

A B
2
3 if T.AMOUNT>10000 =T.BONUS=T.AMOUNT*0.05
4 else if   T.AMOUNT>=5000 && T.AMOUNT<10000 =T.BONUS=T.AMOUNT*0.03
5 else if   T.AMOUNT>=2000 && T.AMOUNT<5000 =T.BONUS=T.AMOUNT*0.02

循环语句:

A B
1 =db=connect("db")
2 =T=db.query@x("select   * from sales where SellerID=? order by OrderDate",9)
3 for T =A3.BONUS=A3.BONUS+A3.AMOUNT*0.01
4 =A3.CLIENT=CONCAT(LEFT(A3.CLIENT,4),   "co.,ltd.")
5  …

与Java的循环类似,SPL还可用break关键字跳出(中断)当前循环体,或用next关键字跳过(忽略)本轮循环,不展开说了。

SPL超越ORM获得更高的开发效率

SPL不仅能用统一的数据结构和语法风格实现业务逻辑,还能够达到ORM的根本目标,显著提高业务逻辑的开发效率。

更多计算函数

ORM的计算函数不足,很多功能都无法实现,或需要编写冗长的代码。SPL提供了更多的计算函数,可直接实现这些功能,代码量大幅缩短。

比如,时间类函数,日期增减:elapse("2020-02-27",5)  //返回2020-03-03

星期几:day@w("2020-02-27")   //返回5,即星期6

N个工作日之后的日期:workday(date("2022-01-01"),25)   //返回2022-02-04

字符串类函数,判断是否全为数字:isdigit("12345")    //返回true

取子串前面的字符串:substr@l("abCDcdef","cd")      //返回abCD

按竖线拆成字符串数组:"aa|bb|cc".split("|")     //返回["aa","bb","cc"]

SPL还支持年份增减、求年中第几天、求季度、按正则表达式拆分字符串、拆出SQL的where或select部分、拆出单词、按标记拆HTML等功能,数量较多,不再赘述。

更方便的集合运算

对结构化数据对象(实体集合)进行集合运算时,ORM有两种方法:循环语句+硬编码,Stream等Lambda表达式+集合函数。前者代码量显然巨大,后者似乎可以少写代码,但因为编译型Lambda表达式繁琐、集合运算函数少且功能弱等原因,导致大多数运算都要辅以硬编码。比如简单的分组汇总,就要用到groupingBy、Collectors、summarizingDouble、DoubleSummaryStatistics等多个类和方法。

SPL提供了简洁易懂的解释型Lambda语法,以及数量众多的集合函数,可以方便地进行集合运算。

比如,批量修改记录:T.run(BONUS+AMOUNT*0.01: AMOUNT, concat(left(CLIENT,4), "co.,ltd."): CLIENT)

过滤:T.select(Amount>1000 && Amount<=3000)

对有序序表按二分法进行过滤:T.select@b(Amount>1000 && Amount<=3000)

分组汇总:T.groups(Client;sum(Amount))

有序分组(相邻且字段值相同的记录分为一组):T.groups@b(Client;sum(Amount))

涉及跨行的集合运算,通常都有一定的难度,比如比上期和同期比。ORM没有为跨行运算做优化,代码通常很繁琐。SPL使用"字段[相对位置]"引用跨行的数据,可显著简化代码,还可以自动处理数组越界等特殊情况。比如,追加一个计算列rate,计算每条订单的金额增长率:

=T.derive(AMOUNT/AMOUNT[-1]-1: rate)

灵活运用SPL的Lambda语法和集合函数,可大幅简化复杂的集合计算。比如,在各部门找出比本部门平均年龄小的员工:

A
1 …//省略序表Employees的生成过程
2 =Employees.group(DEPT;   (a=~.avg(age(BIRTHDAY)),~.select(age(BIRTHDAY)<a)):YOUNG)
3 =A2.conj(YOUNG)

计算某支股票最长的连续上涨天数:

A
1 …//省略序表AAPL的生成过程
2 =a=0,AAPL.max(a=if(price>price[-1],a+1,0))

更专业的结构化数据类型

ORM的结构化数据类型是实体/List<实体>,通用性较强,但专业性不足,很多常用的访问方法都不支持,比如按字段名取某一个或某几个列。SPL的结构化数据类型是记录和序表,更加专业,功能也更强,常见的访问方法都支持。

比如,在序表T的基础上,按字段名取一列,返回简单集合:T.(AMOUNT)。

取几列,返回集合的集合:T.([CLIENT,AMOUNT])

取几列,返回新序表:T.new(CLIENT,AMOUNT)

按序号访问通常难度较大,序表天然有序,很容易处理此类问题。比如,按列号取几列,返回新序表:T.new(#2,#4)

按序号倒数取记录:T.m(-2)

按序号取某几条记录形成序表:T([3,4,5])

按范围取记录形成序表:T(to(3,5))

先按字段取再按记录序号取:T.(AMOUNT)(2);等价于先按记录序号取再按字段取:T(2).AMOUNT

除此之外,SPL还有很多基于序表的高级功能,如TopN、蛇形取值、有序关联等。

解释执行和热部署

业务逻辑数量多,复杂度高,变化是常态。良好的系统构架,应该有能力应对变化的业务逻辑。ORM的本质是JAVA代码,需要先编译再执行,一般都要停机才能部署,应对变化的业务逻辑时非常繁琐。

SPL是基于JAVA的解释型语言,无须编译就能执行,脚本修改后立即生效,支持不停机的热部署,适合应对变化的业务逻辑。

更方便的数据库读写方法

大量的业务逻辑要读写批量记录,这种情况下ORM只能循环ArrayList,并单独处理每条记录,代码冗长繁琐。遇到既有新增,又有修改和删除的批量写库的情况,ORM的代码就更复杂了。

基于序表,SPL提供了更方便的方法读写批量记录,可大幅简化代码。比如批量修改记录:

A B
1
2 =T=db.query("select   * from salesR where SellerID=?",10) /批量查询,序表 T
3 =NT=T.derive() /复制出新序表 NT
4 =NT.field("SELLERID",9) /批量修改
5 =db.update(NT:T,sales;ORDERID) /持久化到数据库

上面代码中,函数update实现批量修改,无须繁琐的循环语句。函数update经过精心设计,可以统一处理多种批量写库方法,遇到既有新增,又有修改和删除的批量写库的情况,SPL的优势更加明显。

A B
1
2 =T=db.query("select   * from salesR where SellerID=?",10) /查出一批记录
3 =NT=T.derive() /复制出新序表
4 =NT.delete(NT.select(ORDERID==209   || ORDERID==208)) /批量删除
5 =NT.field("SELLERID",9) /批量修改
6 =NT.record([220,"BTCH",9,5200,100,date("2022-01-02"),
  221,"BTCH",9,4700,200,date("2022-01-03")])
/批量追加
7 =db.update(NT:T,salesR;ORDERID) /持久化到数据库

与ORM相比,SPL是专业的结构化数据处理语言,数据对象更强大,语法更灵活,函数更丰富,可以更容易地进行数据库读写,可以简化复杂的流程处理和数据计算,可以真正提高业务逻辑的开发效率。

重磅!开源SPL交流群成立了

简单好用的SPL开源啦!

为了给感兴趣的小伙伴们提供一个相互交流的平台,

特地开通了交流群(群完全免费,不广告不卖课)

需要进群的朋友,可长按扫描下方二维码

本文感兴趣的朋友,请转到阅读原文去收藏 ^_^

开源SPL,ORM的终结者?相关推荐

  1. 开源 SPL 优化报表应用应对没完没了

    现在应用中的报表大都使用报表工具开发,成熟的报表工具提供了丰富的显示设置.图表类型.导出打印等功能可以简化报表开发,非常方便.但是,实际报表开发中还是经常碰到一些非常棘手的深层次问题,即使是已经熟练使 ...

  2. 哪吒杯开源SPL答题竞赛,火爆来袭

    目录 一.竞赛规则 二.奖励机制 1.每个题目设置三个优胜奖 2.对于所有参赛但没得奖的选手,会抽取30名幸运奖,各奖励罗技蓝牙无线鼠标一个: 三.数据说明 1.SPL集文件格式的数据 2.在集算器( ...

  3. 开源 SPL 消灭数以万计的数据库中间表

    中间表是数据库中专门存放中间计算结果的数据表,往往是为了前端查询统计更快或更方便而在数据库中建立的汇总表,由于是由原始数据加工而成的中间结果,因此被称为中间表.在某些大型机构中,多年积累出来中间表的数 ...

  4. 开源SPL消灭数以万计的数据库中间表

    中间表是数据库中专门存放中间计算结果的数据表,往往是为了前端查询统计更快或更方便而在数据库中建立的汇总表,由于是由原始数据加工而成的中间结果,因此被称为中间表.在某些大型机构中,多年积累出来中间表的数 ...

  5. .Net开源SqlServer ORM框架SqlSugar整理

    一.链接整理 官方Git源代码地址: https://github.com/sunkaixuan/SqlSugar 最新发布版更新地址:当前版本Release 3.5.2.1 https://gith ...

  6. [开源] .Net ORM FreeSql 1.10.0 稳步向前

    写在开头 FreeSql 是 .NET 开源生态下的 ORM 轮子,转眼快两年了,说真的开源不容易(只有经历过才明白).今天带点干货和湿货给大家,先说下湿货. 认识我的人,知道 CSRedisCore ...

  7. [开源] .Net ORM FreeSql 1.8.0-preview 最新动态播报

    FreeSql 是 .NET 开源生态下的 ORM 轮子,在一些人眼里属于重复造轮子:不看也罢.就像昨天有位朋友截图某培训直播发给我看,内容为:"FreeSQL(个人产品),自己玩可以,不要 ...

  8. [开源] .Net orm FreeSql 1.5.0 最新版本(番号:好久不见)

    废话开头 这篇文章是我有史以来编辑最长时间的,历时 4小时!!!原本我可以利用这 4小时编写一堆胶水代码,真心希望善良的您点个赞,谢谢了!! 很久很久没有写文章了,上一次还是在元旦发布 1.0 版本的 ...

  9. 开源XDesigner ORM 框架设计

    XDesigner ORM 框架设计 袁永福 2011-01-20 最新版本源代码下载地址 http://files.cnblogs.com/xdesigner/XDesignerORM.zip . ...

最新文章

  1. 【干货书】图、网络与算法
  2. 【Java 并发编程】线程池机制 ( 线程池阻塞队列 | 线程池拒绝策略 | 使用 ThreadPoolExecutor 自定义线程池参数 )
  3. 关于面试,我也有说的
  4. python图片二进制流转换成图片_python将图片二进制数据转换成Django file对象
  5. PHP 连接 Rabbitmq 实例代码(亲测通过)
  6. python输错了怎么办_python怎么实现输错三次密码之后锁定
  7. set集合判断集合中是否有无元素_Python入门教程笔记(五)集合(set)及函数
  8. C++中的内联函数和C中的宏定义的区别
  9. linux中可以使用-af含义,关于Windows中的linux:AF_UNIX
  10. f12获取网页文本_【教程】如何抓取动态网页内容
  11. weblogic启动项目失败查看_weblogic 部署项目成功,但是再界面上取不到数据
  12. 串联型直流稳压电源制作(800个电子设计案例)
  13. PHP+H5全栈工程师培训视频教程
  14. Learning ImageMagick 1: Lomo效果
  15. 认知升级是令我们变得优秀的重要基石,没有之一
  16. 牛逼!100多个常用 API 接口整理大全,常用的都有。。
  17. 2021软科计算机科学与技术,2021软科排名,NTU 十学科跻身世界前十!
  18. 人工智能(机器学习、深度学习等)专业名词、代码参数解释(持续更新)
  19. ping命令的基本格式
  20. HCIP之路IPV6

热门文章

  1. 华为官方推特直接开骂@苹果,负责人称:被盗号了
  2. 雷军发布会罕见爆粗口:这XXX绝对是来捣乱的!
  3. 蔚来汽车澄清“4年亏损57亿美元”说法:只有200亿人民币
  4. 后置“浴霸”六摄!疑似索尼全新旗舰Xperia 2曝光
  5. Facebook再次被曝通过至少11款应用收集用户私密信息
  6. Python+Selenium自动化测试:Page Object模式
  7. c语言边界条件的设置,求解能不能用c或c++语言实现下面的约束条件
  8. redis清理缓存_大话Redis问题
  9. 【Elasticsearch】ElasticSearch里面的偏好查询
  10. 【Elasticsearch】Elasticsearch 7.3 的 offheap 原理