Java编程-SQL语句应存储在哪里?[关闭]


107

兼容JDBC的应用程序应在哪里存储其SQL语句,为什么?

到目前为止,我设法确定了这些选项:

  • 硬编码在业务对象中
  • 嵌入在SQLJ子句中
  • 封装在单独的类中,例如 数据访问对象
  • 驱动元数据(将对象模式与数据模式分离-在元数据中描述它们之间的映射)
  • 外部文件(例如,属性或资源文件)
  • 存储过程

各自的“优点”和“缺点”是什么?

应该将SQL代码视为“代码”还是“元数据”?

存储过程应该仅用于性能优化还是它们是数据库结构的合法抽象?

性能是决定的关键因素吗?什么厂商锁定

更好的是松耦合还是紧耦合,为什么?

编辑:谢谢大家的答案–以下是摘要:

元数据驱动,即对象关系映射(ORM)

优点:

  • 非常抽象-无需更改模型即可切换DB服务器
  • 广泛传播-实际上是一个标准
  • 减少所需的SQL数量
  • 可以将SQL存储在资源文件中
  • 性能(通常)是可以接受的
  • 元数据驱动的方法
  • (数据库)供应商独立性

缺点:

  • 隐藏SQL和真正的开发人员意图
  • DBA难以审核/更改SQL
  • 奇数情况下可能仍需要SQL
  • 可以强制使用专有查询语言,例如HQL
  • 不适合进行优化(抽象)
  • 可能缺乏参照完整性
  • 缺乏SQL知识或对数据库中的代码缺乏关注的替代品
  • 永远无法达到本机数据库的性能(即使相差无几)
  • 模型代码与数据库模型紧密结合

硬编码/封装在DAO层中

优点:

  • SQL保留在访问数据的对象(封装)中
  • SQL易于编写(开发速度)
  • 需要更改时,SQL很容易跟踪
  • 简单的解决方案(没有凌乱的架构)

缺点:

  • DBA无法检查/更改SQL
  • SQL可能成为特定于DB的
  • SQL可能变得难以维护

存储过程

优点:

  • SQL保存在数据库中(靠近数据)
  • SQL由DBMS解析,编译和优化
  • SQL易于DBA审核/更改
  • 减少网络流量
  • 增强安全性

缺点:

  • SQL与数据库绑定(供应商锁定)
  • SQL代码更难维护

外部文件(例如,属性或资源文件)

优点

  • 可以更改SQL,而无需重建应用程序
  • 将SQL逻辑与应用程序业务逻辑解耦
  • 所有SQL语句的中央存储库–易于维护
  • 更容易理解

缺点:

  • SQL代码可能变得难以维护
  • 难以检查SQL代码中的(语法)错误

嵌入在SQLJ子句中

优点:

  • 更好的语法检查

缺点:

  • 与Java的联系太紧密
  • 性能低于JDBC
  • 缺乏动态查询
  • 不太受欢迎

好问题,但一次回答都可能有点太多。要回答所有这些问题,需要花费几页时间:p
NickDK

+1好问题!您应该为每个@ocdecio添加“ ORM”。还要添加“撒在Java代码中的各处”(我见过,而且情况最糟)。
Jim Ferrans 09年

2
我非常反对存储过程中的“难以维护SQL代码”。在我的XP中,SQL进入数据库后更易于维护。部分原因是在外部文件(所有SQL语句的中央存储库–易于维护)中使用的原因,再加上参数更易于管理。
Michael Lloyd Lee mlk

1
在我看来,您错过了一个选择:视图的使用。您可以在视图中表达复杂的SQL,然后在这些视图上表达简单的选择(使用任何抽象类型:DAO,SQLJ,ORM等)。您将具有与存储过程类似的优点,但我认为您不会有任何缺点...
Lukas Eder

Answers:


31

通常,应用程序在大小和/或可重用性方面的增长越多,就越需要外部化/抽象化SQL语句。

硬编码(作为静态最终常量)是第一步。下一步是存储在文件(properties / xml文件)中。最后一步是驱动元数据(由像Hibernate / JPA这样的ORM完成)。

硬编码的缺点是您的代码可能变得特定于数据库,并且每次更改都需要重写/重建/重新分发。好处是您可以将它放在1个地方。

存储在文件中的缺点是,随着应用程序的增长,它可能变得无法维护。优点是您无需重写/重建应用程序,除非您需要添加额外的DAO方法。

元数据驱动的缺点是您的模型代码与数据库模型紧密结合。对于数据库模型中的每个更改,您都需要重写/重建/重新分发代码。优点是它非常抽象,您可以轻松地从DB服务器切换而无需更改模型(但现在问自己:公司多久从DB服务器切换一次?可能至少每3年一次)是吗?)。

为此,我不会将存储过程称为“好”解决方案。它们的目的完全不同。即使,您的代码将取决于所使用的数据库/配置。


21

我不知道这是否是最佳选择,但是根据我的经验,它们最终会在DAO层中进行硬编码(即字符串文字)。


5
这可能不是最佳选择,但我也是这么做的。易于编写,易于跟踪,并且无需担心混乱的体系结构。
James Cronen 09年

21
跟踪硬编码SQL所花费的所有时间都是作业安全性。如果您是唯一知道SQL在哪里的人,那么您就不会被解雇。
S.Lott

11
总是让我感到惊讶的是,许多精心构建结构良好,干净的OO Java代码的人都是容忍编写混乱的,性能不佳的SQL并只是将其作为字符串粘贴在随机位置的人。如果您的SQL只是DAO层中的字符串,那么我可以保证您的团队中没有DBA。至少不是一个好的 DBA。
Daniel Pryden 09年

3
-1。可以有DAO,但至少要将查询移到某个属性文件中,以便DBA可以根据需要对其进行检查和调整!
cethegeek

3
我的经验是,如果您是直接使用JDBC,则在无法使用ORM解决方案的情况下,将查询字符串放在数据访问对象层中可能是最好的方法。请注意,如果您这样做,请确保每个页面上的每个人都具有DAO类的编码标准。我经历过资源捆绑和存储过程路由,而且它们都是绝对的维护噩梦,因为它将数据访问逻辑分布在多个层次上,因此向查询中添加列要求您在不同的地方进行更改。
杰森·格里特曼

12

我认为没有人会给您您想要的专业意见,因为这是一个很大的问题。因此,这里是我过去使用的东西,以及以后将使用的东西。

我曾经使用在DAL中硬编码的SQL。我认为这很好,直到DBA想要使用SQL。然后,您必须对其进行挖掘,格式化并将其发送到DBA。谁会嘲笑它,并取代一切。但是没有好的问号,或没有按错误的顺序问号,让您将其留在Java代码中。

我们还使用了ORM,尽管这对开发人员来说非常好,但我们的DBA讨厌它,因为没有SQL可供他们嘲笑。我们还使用了一个奇怪的ORM(第三方供应商定制的ORM),它习惯于杀死数据库。从那时起,我就一直在使用JPA,而且功能非常强大,但是使用它通过DBA进行的任何复杂操作都是艰巨的任务。

现在,我们使用存储过程(带有硬编码的调用语句)。现在,每个人都会抱怨的第一件事是您已绑定到数据库。你是。但是,您多久更改一次数据库?我知道一个事实,就是我们根本无法尝试它,依赖于它的其他代码数量加上重新训练DBA以及迁移数据的能力。这将是非常昂贵的操作。但是,如果在您的世界中需要一口气更换数据库,则可能需要SP。

展望未来,我想将存储过程与代码生成工具一起使用,以便从Oracle软件包中创建Java类。

编辑2013-01-31:几年后和DBA一起使用,现在我们使用Hibernate,仅在绝对需要时才使用SQL(数据库中存储的proc)。我认为这是最好的解决方案。数据库有99%的时间不需要担心SQL,而他们有1%的时间在他们已经习惯的地方。


1
+1代表编写存储过程,然后从它们生成Java代码的想法,而不是相反。
Daniel Pryden 09年

我认为Java层不应以任何方式模仿或映射DB层。我认为,如果您要抽象Oracle软件包,请制作另一个软件包或更多包装过程。我尝试并通过实践将两者逻辑上完全分开。
JE队列

2
@Xepoch:我实际上同意-也许我应该以不同的措辞表达我的评论。您的数据库应该反映您的数据模型(实体关系模型),对象模型也应该反映您的数据模型(尽管不一定相同)。因此,它们至少应该相关。从存储过程生成Java代码方面,关键是应该从数据模型的结构派生访问数据库的API,而不是从对象的结构派生数据模型。
Daniel Pryden 09年

您可能对使用jooq.org感兴趣。它完全按照您所说的进行:“生成代码以从Oracle软件包创建Java类”。除此之外,如果您需要用Java表达SQL,则它附带类似于SQL的DSL,类似于C#中的LINQ,您不能将其放入存储过程中。
卢卡斯·埃德

10

通过使用ORM(例如休眠),希望您不必担心SQL语句。性能通常是可以接受的,并且您也可以获得供应商的独立性。


13
-1您将拥有HQL语句,并且大多数问题仍与HQL有关。它们是否在代码(字符串文字)中,在批注中的命名查询,在xml文件中的命名查询,存储在属性文件中?
flybywire

1
@flybywire-使用Hibernate很少使用HQL。在98%的情况下,只需要通过示例和条件(即使用对象)进行查询。
SingleShot

2
@SingleShot,我不同意。如果它的东西比选择更复杂的ID,我认为这与HQL完成。我想说的是,在进行用户界面驱动的搜索时(如在图书馆目录的搜索屏幕中),将使用标准和示例。但是,让我们看看其他人的想法。
flybywire

3
@SingleShot-我非常不同意。我们使用大量的HQL,尤其是用于报告查询。某些标准完全不支持某些HQL功能(使用自定义SQL函数,select子句中的构造函数)。QBE有时会导致更多问题,然后才解决。
javashlook,2009年

4
“迄今为止,使用HQL很少使用HQL”是我今天听到的最有趣的事情。QBE太荒谬了;虽然你可能不得不求助于标准UI查询,定义明确的查询(报表/业务交互的/ etc ...)都应该在HQL。
ChssPly76

10

应该将SQL代码视为“代码”还是“元数据”?

码。

存储过程应该仅用于性能优化还是它们是数据库结构的合法抽象?

存储过程允许重用,包括在其他存储过程内部。这意味着您可以一次访问数据库并让其执行支持的指令-最小的流量是理想的。ORM或sproc,到达数据库和返回数据库的时间是您无法弥补的。

ORM由于其抽象而不适合进行优化。IME,ORM还意味着缺乏参照完整性-使得数据库难以报告。节省下来的复杂性现在已经增加,能够以可行的方式获取数据。

性能是决定的关键因素吗?供应商锁定如何?

不,简单是。供应商锁定也发生在数据库中-SQL是相对标准化的,但是仍然有特定于供应商的处理方式。


4
+1用于调用SQL代码。太多的ORM工具试图隐藏SQL,而实际上,它通常是表达您要执行的操作的最佳语言。我同意您的观点,即存储过程比ORM更好,尽管我怀疑这将成为流行的观点。
Daniel Pryden 09年

9

担心Java世界中的供应商锁定很有趣。

我希望您没有为Oracle Enterprise支付$ 50000 pr的CPU,然后仅使用最小公分母来随时切换到Mysql。就像任何优秀的DBA都会告诉您的那样,不同的大名称数据库之间存在细微的差异,尤其是在锁定模型以及它们如何实现一致性方面。

因此,不要仅根据供应商不可知SQL的原则来决定如何实施SQL调用-这样做的真实原因(业务原因)。


1
哦,不是那样的!主要关注的问题是允许支持团队更改SQL语句(例如,用于调优,与DBA相关的任务),并提高应用程序功能(与数据库有关)的可见性。支持团队没有Java专门知识,因此他们不会很高兴查看代码,该应用程序将成为使用Ingres数据库的大量现有应用程序的新增功能
Adrian

6

存储过程中的SQL已由数据库系统优化,并针对速度进行了编译-这是它的自然本源。SQL由数据库系统理解,由数据库系统解析。如果可以,将SQL保留在数据库中;将其包装在存储过程或函数中,或者数据库系统提供的任何逻辑单元中,并使用您或任何其他人提到的任何一种工具对其进行简单调用。

为什么要在db外部存储数据库系统的SQL代码?通常是为了加快发展速度。为什么要使用ORM映射?-有人说ORM映射提供了不同数据库系统之间的兼容性;但是,在现实世界中,很少有应用程序在构建后就离开数据库平台,特别是当它开始使用复制等高级功能时,并且在极少数情况下确实发生了数据库系统被换出的情况,因此需要做一些工作。我相信,ORM的缺点之一通常可以替代SQL知识的缺乏或对数据库代码的缺乏的关注。而且,即使ORM接近本机数据库性能,它也永远无法与之匹敌。

我站在将SQL代码保留在数据库中,并通过您希望使用的任何API或接口对其进行简单调用的立场。通过将这些调用放在抽象类或OO接口(由方法表示)之后,还可以抽象出进行数据库调用的时间,因此,如果您交换了一种新型的数据源,它将对业务层无缝。


+1不错的观点。我想您会对这篇博客帖子感兴趣:database-programmer.blogspot.com/2010/12/…
卢卡斯·埃德

5

您要问的唯一一个肯定的问题是“是SQL代码还是元数据?” 它绝对是代码,因此应保留在某种源代码控制中,并具有一个可以轻松更新到最新版本并出现问题时可以回滚的系统。

我已经看到了在应用程序中执行SQL的三种方法,每种方法各有利弊。没有最好的方法,但是最好的办法就是选择一种与您的应用程序兼容的应用并坚持下去。

  • ORM-这减少了您需要编写的SQL数量,并为您处理了许多细节。您将需要执行一些自定义SQL。确保您有一个可以正常处理的ORM。
  • 数据访问对象-将SQL保留在访问数据的对象中。这将封装您的数据库并使之成为数据库,因此您的应用程序的其余部分无需了解底层数据库结构,而只需了解这些对象的接口即可。
  • 存储过程-这将您所有的SQL保留在数据库中,并使DBA可以轻松了解正在发生的事情。您所需要做的就是让您的代码调用存储的proc

4

我们碰巧使用了iBatis SQL映射器,它比像Hibernate这样的ORM更接近金属。在iBatis中,您将SQL语句放入资源文件(XML)中,该文件必须位于类路径中。

如果添加@ocdecio的ORM选项,则您的方法列表似乎很全面。我会说使用ORM以及使用SQL映射器和资源文件是两种最佳方法。我会避免使用SQLJ,因为SQLJ尚未被广泛使用,并且与Java的联系也太紧密了。还应远离存储过程,因为它们将您绑定到特定的数据库供应商(存储过程几乎不存在标准)。


4

像我们大多数人一样,我已经看到了整个过程,但是我们需要将SQL视为一流的语言。我什至看到存储在数据库中的SQL被拉低然后又执行回去。

我见过的最成功的系统采用存储过程,功能和视图。

存储的proc将SQL文本保留在DB中,并允许那些DEPLOYING和CUSTOMIZING(需要大量适当的设计来支持它)进行相对立即的更改。

出于相同的原因,所有投影都应通过视图和简单选择进行,所有投影逻辑都应包含在视图内。


+1表示意见。问题的摘要中尚未反映这一点
Lukas Eder

2

我建议使用具有工厂布局的DAO。因此,您需要的示例对象是:

public class CoolBusinessObject
public class DAOFactory.java
public implementation CoolBusinessOjectDAO
public class CoolBusinessOjectDAOOracleImpl implements CoolBusinessOjectDAO

这种样式使数据交互分层,因此,如果您切换数据库或使用ORM技术,则只需更改一层代码。


2

这三个之间实际上并没有任何实质性的区别:

  1. 硬编码在业务对象中
  2. 嵌入在SQLJ中子句中
  3. 封装在单独的类中,例如数据访问对象

我假设您将SQL代码以字符串形式直接嵌入到Java代码中。虽然1和3可能会直接使用JDBC(或诸如Apache DbUtils之类的工具),但2在堆栈中添加预处理器技术,从而在编译之前生成相关的JDBC代码。

因此,从本质上讲,如果这些解决方案涉及嵌入SQL,则最好使用以下任何一种技术:

  • JPA Criteria API,将JPQL建模为Java中内部特定于域的语言
  • jOOQ,将SQL建模为Java中内部特定于域的语言

与通过SQLJ或通过实际的字符串连接相比,可能还有其他工具可以帮助您以更加类型安全的方式将SQL嵌入Java中。


1

从我的经验来看,在DAO对象中硬编码sql语句是被广泛使用的方法,但是,我认为它应该是最不受欢迎的方法。最佳实践应该是将sql语句存储在属性文件中。并通过属性文件(例如java.util.Properties)的接口获取DAO对象中的语句。sql语句可以通过“ Prepared Statement”方法插入“?”以传递参数。

这种方法有助于将sql逻辑与应用程序业务逻辑分离。这样就可以为所有sql语句提供一个中央存储库,从而使修改变得更加容易,从而消除了在应用程序逻辑中搜索数据库语句的需求。


有人可能会反对这会带来SQL注入的风险。您对此有何看法?
阿德里安

2
如果您仍将SQL代码存储在应用程序外部,那么将其作为字符串存储在某个地方比将其存储在数据库层(作为存储过程)有什么好处?存储过程可以更有效地利用数据库的优化器,因此它们几乎总是胜过准备好的语句。
Daniel Pryden 09年

1
丹尼尔,您好,我不是说,您不编写sql proc,我只是说,您以这种方式调用了存储的proc。它还可以帮助您更好地控制,也可以将参数传递给存储的proc。
机器

1

我的最终陷入了资源束。我知道这是不正常的,但对我和任何“我以外的人”来说,维护起来最简单。这是直接且合乎逻辑的。

我真的很想知道是否有人也使用我的方法。


好奇为什么不将SQL保存在数据库中?
JE队列

@Xepoch-你是什么意思?语句位于资源束(属性文件)中,该资源束与实体位于同一包中,因此customer.properties与Customer.class相关。数据存储在数据库中。
Brett Ryan

1

作为一个rexem所写,SQL语句是代码-应该将它们视为代码,而不是外部化(除非您有充分的理由),而应与处理该语句的SQL数据的代码放置在一起。如今的ORM / iBatis框架为日常JDBC开发提供了许多简化方法。

您将在此问题中找到问题的一些答案:)如何存储SQL语句的问题取决于您的应用程序。你有什么需要 高安全性,编写代码或维护的便利性,跨平台或供应商锁定?下一个问题,您需要纯SQL还是ORM框架会很好?

* Hardcoded in business objects
* Encapsulate in separate classes e.g. Data Access Objects

最简单的解决方案(P),难以维护(C)

* Embedded in SQLJ clauses

更好的语法检查(P),缺少动态查询(C),性能比JDBC(C)低,没有那么流行(C)

* Metadata driven (decouple the object schema from the data schema - describe the mappings between them in metadata)

在特定情况下,您应该执行(C)或使用ORM(P);)

* External files (e.g. Properties or Resource files)

易于维护(P),但更难检查错误(C)

* Stored Procedures

高安全性(P),难以维护供应商锁定问题的代码(C)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.