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

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

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

应用中的数据业务通常涉及持久化数据的访问、数据计算和流程处理。数据库中的持久化数据可以用 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已开源免费,欢迎前往乾学院了解更多!

相关推荐

win8手机下载安装(win8安卓)

在电脑上面就可以下载,打开浏览器搜索windous8系统会出现一些下拉选择,选择第一条或者选择有官网字样的,就直接有下载按钮,然后点击下载就可以了关闭应用自动更新第一步、在系统中找到应用商店。第二...

台式电脑显卡怎么升级(台式电脑显卡升级方案)

一般情况下,建议到产品(您的显卡)品牌官网上去下载相应最新的驱动,这虽然并不能保证一定就是显卡最新的驱动,但相对于稳定性来说是首选。如果是高级玩家,追求更新、更好的性能发挥,可以利用驱动精灵一类的驱动...

u盘数据丢失的原因(u盘数据丢失的原因有哪些)

U盘出现了损坏造成的磁道出现了损坏。这个U盘的磁道是最容易损坏的,有的时候你不知道怎么碰到它,它就有数据丢失了就无法显示这样的情况,你可以在电脑上进行修复,首先你点击U盘右键找到属性选择修复,这样把...

window7下载哪个版本的ie(windows7用哪个版本的ie浏览器)

WIN7系统自带的IE浏览器是8.0版本的。IE全称InternetExplorer,是美国微软公司推出的一款网页浏览器。IE8扩展的新功能有:1、Activities(活动内容服务)。用户可以从网页...

服务器回收(上海服务器回收)

回收服务器内存后,首先应该彻底清除内存存储的所有数据和敏感信息,然后进行分类处理。如果内存仍然有效,可以进行检测、测试和修复后再重新使用。如果内存已损坏或过期,应该妥善处理,比如通过专业的硬件回收公司...

戴尔官网入口学生通道(戴尔学生渠道)

戴尔官网地址如下,在浏览器输入就可以加入了。DELL官方网站http://www.dell.com.cn/DELL官方旗舰店(天猫)http://dell.tmall.com/DELL官方旗舰店(京东...

win7旗舰版激活码病毒(win7旗舰版激活密钥 永久激活码)

激活和破解工具会修改一些系统文件或数据,一般都会被杀毒软件识别为木马。而且现在网上的windows和office激活工具有的确实是带有木马的,最好去值得信任的网站或者论坛下载。

破解版office2019百度云(破解版office2016百度云)
  • 破解版office2019百度云(破解版office2016百度云)
  • 破解版office2019百度云(破解版office2016百度云)
  • 破解版office2019百度云(破解版office2016百度云)
  • 破解版office2019百度云(破解版office2016百度云)
421文档 全文免费阅读(421小说在线阅读)

根据1,我回答如下:1,"421文档"是在4月21日发布的。2,"421文档"作为一份重要的文件,通常在特定日期或时间点发布,供相关人员参考和使用。3,除了发布日...

阿里旺旺网页版登录入口(阿里旺旺卖家版官网)

手机淘宝的旺旺在打开商品后,会看到左下角有个旺旺的图标,点击就可以联系了。  阿里旺旺是将原先的淘宝旺旺与阿里巴巴贸易通整合在一起的一个新品牌。它是淘宝和阿里巴巴为商人量身定做的免费网上商务沟通软件,...

网易电脑版登录入口(main.163网易电脑版登录入口)

http://mail.163.com步骤一:输入邮箱官网网址http://mail.163.com/打开163邮箱登录入口界面。步骤二:输入已有的邮箱账号、密码,点击登录即可。步骤三:如果没有邮箱账...

qq截图快捷键不能用(qq快捷截图不能使用)

笔记本上面的PrtSC能用么,是不是需要用Fn+这个按键才能截屏呀。有些机器在bios下有Fn快捷键设置,你需要把这个功能键开启或者关闭后才能单独点击使用。或者你使用Fn+PrtSC在试试,另外,何必...

大地影视中文第二页的背景故事

大地影院通常会有明显的入口标识,你可以根据影院外部的建筑结构和指示牌来找到入口。一般来说,大地影院的入口可能位于建筑的主立面或者侧面,有时也可能位于地下停车场或者商场内部。当你到达影院所在的建筑时,可...

win10共享打印机需要密码(win10共享打印机需要密码吗)

共享打印机需要输入用户名和密码,该如何操作,下面给大家来介绍下。1、首先打开电脑的控制面板,点击管理工具。2、然后在打开的管理工具窗口中,点击本地安全策略。3、接着在打开的页面中,点击本地策略。4、然...

系统引导启动项怎么设置(系统引导按哪个键)

如果您想在计算机启动时添加系统启动引导选项,可以按照以下步骤进行操作:1.打开“运行”窗口,可以通过快捷键Win+R打开“运行”窗口。2.在“运行”窗口中输入“msconfig”命令,并点击“...