百度360必应搜狗淘宝本站头条
当前位置:网站首页 > IT知识 > 正文

Java vs SQL:数据业务场景下谁才是最优选项?

liuian 2025-03-12 16:41 23 浏览

应用中的数据业务通常涉及持久化数据的访问、数据计算和流程处理。数据库中的持久化数据可以用 SQL 计算,存储过程的 loop/if 语句可以进行流程处理,JDBC(含 ODBC)可以让 SQL 和应用集成,所以复杂 SQL(含存储过程)常用于数据业务开发。

但是,复杂 SQL 深度绑定数据库,存在架构上的缺陷,不能满足现代应用的要求。

复杂 SQL(及存储过程)的缺陷

难以扩展

用复杂 SQL 或存储过程实现数据业务时,压力会集中在数据库上。而数据库无法被成熟框架集成,无法利用框架实现高可用性和易扩展性,本身扩展时无论横向还是纵向的成本都很高,不符合现代应用的建设理念。

代码可移植性差

简单 SQL 通用性强,容易在数据库间移植,复杂 SQL 则不然。复杂 SQL 经常用到绑定数据库的特殊语法(含函数),代码就很难移植;存储过程甚至没有统一的标准,互相差异更大,移植也更加困难。

耦合性高

数据业务是为应用服务的,最好是处于应用内部,但存储过程存在于数据库,两者耦合性过高。数据库通常是共享的,还会造成应用间的耦合。

为了弥补复杂 SQL 的缺陷,很多应用开始使用 Java+ 简单 SQL 实现数据业务。主要有两类技术可选择:ORM 和 Stream,ORM 技术以 Hibernate 和 JOOQ 为主;Stream 是 Java8 开始提供的类库,在此基础上又发展出 Kotlin。大量的数据计算和处理压力由应用承担,Java 程序很容易被成熟框架集成,进行低成本的扩展。ORM 和 Stream 负责数据计算,基础 Java 语言负责流程处理,同为 Java 代码,移植性非常好。这种方式的耦合性也很低,数据库仅用作存储,数据业务全部集中于应用,可单独维护,不同应用间的数据业务天然隔离。

相比复杂 SQL,Java+ 简单 SQL 可以得到较好的架构优势,但也带来了新的缺陷。

Java+ 简单 SQL 的缺陷

计算能力弱导致开发困难

Hibernate 的计算能力远不如 SQL,很多简单计算都无法用 Hibernate 描述,包括 from 子查询、涉及行号的计算等。比如 SQL 很容易实现的 from 子查询:

select orderId, m from (select orderId, month(orderDate) m from OrdersEntity) t1

复杂些的计算 Hibernate 更加无法描述,比如 Oracle SQL 用窗口函数计算各组前 3 名

select * from (
select *, row_number() over (partition by Client order by Amount) rn from Orders) T where rn<=3

很多基础的日期函数和字符串函数 Hibernate 都不支持,包括日期增减、求年中第几天、求季度数,以及替换、left 截取、求 ASCII 码等。以日期增加为例:

select date_add(OrderDate,INTERVAL 3 DAY) from OrdersEntity

想实现类似的功能,只有两种办法,引入方言 SQL,或者用 Java 硬编码。前者绑定数据库,代码难以移植,偏离了 ORM 的初衷,后者代码量巨大。

JOOQ 需要程序员先设计好 SQL,再把 SQL 翻译成 JOOQ 代码,最后由引擎把 Java 代码解析成 SQL 去执行,想获得接近方言 SQL 的计算能力,就要大量使用绑定数据库的 JOOQ 函数,但这样并没有解决架构上的缺陷;想弥补架构上的缺陷,就要尽量使用通用的 JOOQ 函数,计算能力又将大幅下降。Java 语法不适合表达 SQL,为了正确表达,JOOQ 经常对函数过度封装,代码比 SQL 复杂,实际的计算能力低于 SQL。

比如,各组前 3 名:

//等价的SQL见前文,绑定Oracle的JOOQ如下
WindowDefinition CA = name("CA").as(partitionBy(ORDERS.CLIENT).orderBy(ORDERS.AMOUNT));
context.select().from(select(ORDERS.ORDERID,ORDERS.CLIENT,ORDERS.SELLERID,ORDERS.AMOUNT,ORDERS.ORDERDATE,rowNumber().over(CA).as("rn")).from(ORDERS).window(CA) ).where(field("rn").lessOrEqual(3)).fetch();


明显要复杂很多。

Stream 提供了流式编程风格、Lambda 语法、集合函数,可以对简单类型(数字、字符串、日期)的集合进行简单计算,但 Stream 是通用的底层工具,在记录集合方面还不够专业,计算能力远低于 SQL。很多基本计算 Stream 实现起来都很困难,比如分组汇总:

//等价的SQL:
select year(OrderDate), sellerid, sum(Amount), count(1) from Orders group by year(OrderDate), sellerid
//Stream:
Calendar cal=Calendar.getInstance();
Map c=Orders.collect(Collectors.groupingBy(
        r->{
            cal.setTime(r.OrderDate);
            return cal.get(Calendar.YEAR)+"_"+r.SellerId;
            },
            Collectors.summarizingDouble(r->{
                return r.Amount;
            })
        )
);
    for(Object sellerid:c.keySet()){
        DoubleSummaryStatistics r =c.get(sellerid);
        String year_sellerid[]=((String)sellerid).split("_");
        System.out.println("group is (year):"+year_sellerid[0]+"\t (sellerid):"+year_sellerid[1]+"\t sum is:"+r.getSum()+"\t count is:"+r.getCount());
    


Kotlin 对 Stream 进行了改进,Lambda 表达式更加简洁,集合函数更加丰富,另外增加了热情集合计算(Eager Evaluation,与 Stream 的惰性集合计算 Lazy evaluation 相对)。但 Kotlin 也是通用的底层工具,设计目标是简单集合的计算,在记录集合方面还不够专业,计算能力依然远低于 SQL。比如基本的分组汇总:

data class Grp(var OrderYear:Int,var SellerId:Int)
data class Agg(var sumAmount: Double,var rowCount:Int)
var result=Orders.groupingBy{Grp(it.OrderDate.year+1900,it.SellerId)}
    .fold(Agg(0.0,0),{
        acc, elem -> Agg(acc.sumAmount + elem.Amount,acc.rowCount+1)
    })
.toSortedMap(compareBy { it. OrderYear}.thenBy { it. SellerId})
result.forEach{println("group fields:${it.key.OrderYear}\t${it.key.SellerId}\t aggregate fields:${it.value.sumAmount}\t${it.value.rowCount}") }


Hibernate、JOOQ、Stream、Kotlin 这些类库之所以计算能力不足,根本原因在于它们的宿主语言是静态的编译型语言,很难支持动态数据结构,表达能力受到极大限制。像 JOOQ 这种勉强支持动态数据结构的类库,必须写成静态代码 + 动态代码(字符串)混合的形式,如 T2.field("continuousdays"),业务数据只要稍显复杂,编码难度就会陡增。SQL 之所以计算能力强,根本原因在于它是动态的解释型语言,天生支持动态数据结构,表达能力的上限较高。

难以热部署导致运维复杂

编译型语言不支持热部署,修改代码后经常需要重新编译并重启应用,系统安全较差,运维复杂度较高。

esProc SPL 解决一切

实现数据业务,还有一个更好的选择:esProc SPL+ 简单 SQL。

esProc SPL 是 Java 下开源的数据处理引擎,基本功能可涵盖数据业务的每个阶段,SPL 本身具有数据计算和流程处理能力,简单 SQL 负责读写数据库,前端 Java 代码通过 JDBC 调用 SPL。

读写数据库。SPL 提供了 query 函数执行 SQL,用来将数据库的查询读为内部的序表(SPL 的结构化数据对象)。

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

SPL 提供 update 函数将序表保存到数据库,SPL 引擎会对比修改前后的数据,自动解析为不同的 SQL DML 语句(insert、delete、update)并执行。比如,原序表为 T,经过增删改之后的序表为 NT, 将变化结果持久化到数据库:

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

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

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

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

去重:T.id(Client)

汇总:T.max(Amount)

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

流程处理。类似 Java 的 for/while/if 和存储过程的 loop/if 语句,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


 …

JDBC 接口。SPL 编写的数据业务代码可以保存在脚本文件中,Java 通过 JDBC 接口引用脚本文件名,形同调用存储过程。

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();


除了这些基础能力外,SPL+ 简单 SQL 还能克服复杂 SQL 和 Java+ 简单 SQL 的各种缺陷,扩展成本低、代码可移植性好,耦合性低、计算能力强、支持热部署。

扩展简单

用 SPL 实现数据业务,压力会集中在 SPL 上。作为 Java 类库,SPL 可以无缝被成熟的 Java 框架集成,便于横向扩展。

代码可移植好

SPL+ 简单 SQL 实现数据业务时,代码集中在数据计算和流程处理,这部分由 SPL 实现。SPL 与数据库无关,代码可在数据库间无缝移植。读写数据库由简单 SQL 实现,不涉及方言 SQL,移植起来也很方便。

SPL 的初衷之一就是便于移植,为此提供了许多工具。SPL 鼓励通过数据源名取数,移植时只要修改配置文件,不必修改代码。SPL 支持动态数据源,可通过参数或宏切换不同的数据库,从而进行更方便的移植。SPL 还提供了与具体数据库无关的标准 SQL 语法,使用 sqltranslate 函数可将标准 SQL 转为主流方言 SQL,仍然通过 query 函数执行。

耦合性低

数据库只负责存储,不负责数据业务。数据业务由 SPL+ 简单 SQL 实现,与应用处于同一位置。当数据业务发生变化时,只要修改应用中的代码,不必维护数据库,两者耦合度低。SPL 是普通的 Java 类库,可部署在不同应用中,应用间天然隔离。

计算能力强

SPL 提供了丰富的计算函数,可以用直观简短的代码实现 SQL 式计算:

子查询:Orders.new(OrderId,month(OrderDate):m).new(OrderId,m)

分组汇总: T.groups(year(OrderDate),Client; avg(Amount):amt)

各组前 3 名:Orders.groups(Client;top(3,Amount))

SPL 支持有序计算、集合计算、分步计算、关联计算,适合简化复杂的数据计算,计算能力超过 SQL。比如,最大连续上涨天数:


A

1

=orcl.query@x(select price from stock order by transDate)

2

=t=0,A1.max(t=if(price>price[-1],t+1,0))

再比如,找出司公中与其他人生日相同的员工:


A

1

=mysql5.query(“select * from emp”).group(month(birthday),day(birthday))

2

=A1.select(~.len()>1).conj()

SPL 还提供了更丰富的日期和字符串函数,在数量和功能上超过 Java 计算类库和 SQL。

值得一提的是,为了进一步提高开发效率,SPL 还创造了独特的函数语法。

支持热部署

SPL 是解释型语言,代码以脚本文件的形式外置于 JAVA,无须编译就能执行,脚本修改后立即生效,支持不停机的热部署,适合变化的业务逻辑,运维复杂度低。

SPL 还有其他优点:支持全功能调试,包括断点、单步、进入、执行到光标等;代码通常是脚本文件的形式,可存储于操作系统目录,方便进行团队代码管理;SPL 代码不依赖 JAVA,数据业务和前端代码物理分离,代码耦合性低。

SPL已开源免费,欢迎前往乾学院了解更多!

相关推荐

搭建一个20人的办公网络(适用于20多人的小型办公网络环境)

楼主有5台机上网,则需要一个8口路由器,组网方法如下:设备:1、8口路由器一台,其中8口为LAN(局域网)端口,一个WAN(广域网)端口,价格100--400元2、网线N米,这个你自己会看了:)...

笔记本电脑各种参数介绍(笔记本电脑各项参数新手普及知识)

1、CPU:这个主要取决于频率和二级缓存,频率越高、二级缓存越大,速度越快,现在的CPU有三级缓存、四级缓存等,都影响相应速度。2、内存:内存的存取速度取决于接口、颗粒数量多少与储存大小,一般来说,内...

汉字上面带拼音输入法下载(字上面带拼音的输入法是哪个)

使用手机上的拼音输入法打成汉字的方法如下:1.打开手机上的拼音输入法,在输入框中输入汉字的拼音,例如“nihao”。2.根据输入法提示的候选词,选择正确的汉字。例如,如果输入“nihao”,输...

xpsp3安装版系统下载(windowsxpsp3安装教程)

xpsp3纯净版在采用微软封装部署技术的基础上,结合作者的实际工作经验,融合了许多实用的功能。它通过一键分区、一键装系统、自动装驱动、一键设定分辨率,一键填IP,一键Ghost备份(恢复)等一系列...

没有备份的手机数据怎么恢复

手机没有备份恢复数据方法如下1、使用数据线将手机与电脑连接好,在“我的电脑”中可以看到手机的盘符。  2、将手机开启USB调试模式。在手机设置中找到开发者选项,然后点击“开启USB调试模式”。  3、...

电脑怎么激活windows11专业版

win11专业版激活方法有多种,以下提供两种常用的激活方式:方法一:使用激活密钥激活。在win11桌面上右键点击“此电脑”,选择“属性”选项。进入属性页面后,点击“更改产品密钥或升级windows”。...

华为手机助手下载官网(华为手机助手app下载专区)

华为手机助手策略调整,已不支持从应用市场下载手机助手,目前华为手机助手是需要在电脑上下载或更新手机助手到最新版本,https://consumer.huawei.com/cn/support/his...

光纤线断了怎么接(宽带光纤线断了怎么接)

宽带光纤线断了可以重接,具体操作方法如下:1、光纤连接的时候要根据束管内,同色相连,同芯相连,按顺序进行连接,由大到小。一般有三种连接方法,分别是熔接、活动连接和机械连接。2、连接的时候要开剥光缆,抛...

深度操作系统安装教程(深度操作系统安装教程图解)
  • 深度操作系统安装教程(深度操作系统安装教程图解)
  • 深度操作系统安装教程(深度操作系统安装教程图解)
  • 深度操作系统安装教程(深度操作系统安装教程图解)
  • 深度操作系统安装教程(深度操作系统安装教程图解)
win7旗舰版和专业版区别(win7旗舰版跟专业版)

1、功能区别:Win7旗舰版比专业版多了三个功能,分别是Bitlocker、BitlockerToGo和多语言界面; 2、用途区别:旗舰版的功能是所有版本中最全最强大的,占用的系统资源,...

万能连接钥匙(万能wifi连接钥匙下载)

1、首先打开wifi万能钥匙软件,若手机没有开启WLAN,就根据软件提示打开WLAN开关;2、打开WLAN开关后,会显示附近的WiFi,如果知道密码,可点击相应WiFi后点击‘输入密码’连接;3、若不...

雨林木风音乐叫什么(雨林木风是啥)

雨林木风的创始人是陈年鑫先生。陈年鑫先生于1999年创立了雨林木风公司,其初衷是为满足中国市场对高品质、高性能电脑的需求。在陈年鑫先生的领导下,雨林木风以技术创新、产品质量和客户服务为核心价值,不断推...

aics6序列号永久序列号(aics6破解序列号)

关于AICS6这个版本,虽然是比较久远的版本,但是在功能上也是十分全面和强大的,作为一名平面设计师的话,AICS6的现有的功能已经能够应付几乎所有的设计工作了……到底AICC2019的功能是不是...

win7正在启动windows 卡住(win7正在启动windows卡住了 进入安全模式)
  • win7正在启动windows 卡住(win7正在启动windows卡住了 进入安全模式)
  • win7正在启动windows 卡住(win7正在启动windows卡住了 进入安全模式)
  • win7正在启动windows 卡住(win7正在启动windows卡住了 进入安全模式)
  • win7正在启动windows 卡住(win7正在启动windows卡住了 进入安全模式)
手机可以装电脑系统吗(手机可以装电脑系统吗怎么装)

答题公式1:手机可以通过数据线或无线连接的方式给电脑装系统。手机安装系统需要一定的技巧和软件支持,一般需要通过数据线或无线连接的方式与电脑连接,并下载相应的软件和系统文件进行安装。对于大部分手机用户来...