通过JDBC中的JDBC实现数据库独立性

时间:2017-06-01 09:04:48

标签: java sql jdbc

使用JDBC存在一些限制,数据库依赖性是其中一个限制。

是否有任何模式或方法可以在JDBC中实现数据库独立性(不使用任何其他ORM框架或工具)。

我试图通过动态多态来实现这一点(根据特定的SQL语法为不同的DBMS和覆盖的常见CRUD操作创建特定的类)。

例如,有没有办法编写通用SQL语句,以便它们几乎可以在每个SQL相关的DBMS中执行?

5 个答案:

答案 0 :(得分:28)

我认为我有资格回答,是jOOQ的作者,已在another answer中提出过。正如我所展示的那样,完全有可能实现你想要做的事情,但如果你想自己动手,那么你前面还有很长的路要走。

让我们谈谈JDBC

JDBC是一种出色的网络协议抽象,因此它是一个很好的起点。当你继续解决像你正在尝试构建的API中的更复杂的问题时,有很多需要注意的事项。例如:

  • 获取生成的密钥真的很难。很少有JDBC驱动程序能够做到这一点
  • 您确定正确处理LOB吗?提示:你不是
  • 比LOB更糟糕的是什么? Oracle OBJECT类型中的LOB
  • 我是否提到过Oracle OBJECT类型?它们可以放在数组(以及数组的数组中。当事情变得非常毛茸茸时
  • 但与PostgreSQL的TYPE类型相比,Oracle的OBJECT类型非常棒。 JDBC驱动程序并没有帮助你 ,那里
  • 尝试绑定DB2 NULL值。有时它有效,有时它不会。
  • 想支持java.sql.Date java.time.LocalDate?祝你好运!
  • 说到日期,您知道对TIMESTAMP WITH TIME ZONE数据类型有多少种不同的解释吗?
  • 想支持INTERVAL类型?真的?
  • 如果数据库抛出多个异常怎么办?
  • 如果数据库通过与异常(hello SQL Server)不同的API引发错误怎么办
  • 如果您需要在获取异常之前收集警告,该怎么办?
  • 您是否知道某些数据库首先向您发送更新计数,而实际结果集(例如,当触发器触发时)
  • 您是否考虑过处理多个结果集?
  • 现在将上述内容与正式的OUT参数
  • 结合起来
  • 让我们谈谈BOOLEAN类型
  • ......我可以坚持几个小时。 More examples on this website
  • 您是否知道当autoCommit设置为true时,某些PostgreSQL语句不起作用?
  • 并非每个人都支持保存点
  • 想要使用JDBC DatabaseMetaData对您的架构进行反向工程吗?算了吧!
  • 想要使用ResultSetMetaData来发现合格的列名?嗯......

正如您所见,即使JDBC对大多数人的工作做得非常好(并且上面的每一个都适用于单个数据库总是有一个hacky解决方法。但是你想写一个适用于所有数据库的API,所以你必须修复/解决上述所有问题。相信我。这会让你忙一阵子!

让我们谈谈SQL

但到目前为止,我们只讨论了绑定到JDBC的难度。我们还没有讨论标准化SQL的难度。所以我们暂时讨论一下:

  • LIMIT n OFFSET m很好,是吗?或者是LIMIT m, n?还是TOP n START AT m?还是OFFSET m ROWS FETCH NEXT n ROWS ONLY?如果您想支持旧数据库怎么办?你会推出自己的ROW_NUMBER()过滤吗? Here, I've documented it for you
  • 有些数据库支持SELECT而没有FROM。在其他数据库中,您需要类似DUAL表的内容。 There you go, all documented
  • 有些数据库假装他们不需要那个DUAL表,直到他们的解析器中断并且你仍然需要它(你好MySQL)
  • 有些数据库支持SELECT而没有FROM,但FROM / WHERE / HAVING
  • 需要GROUP BY
  • 您对此采取了什么:(SELECT 1 UNION SELECT 2) UNION ALL SELECT 3。它适用于所有数据库吗? (我的意思是括号嵌套)
  • 是否支持EXCEPT ALLEXCEPT是否支持?
  • 是否支持FULL OUTER JOIN
  • 派生表需要别名,还是没有别名?
  • 派生表上是否允许关键字AS
  • ORDER BY子句是否包含引用SELECT子句中别名的表达式?或只是引用FROM子句中的列的表达式?
  • ORDER BY子句可以包含表达式吗?
  • 派生表可以包含ORDER BY子句吗?
  • 让我们谈谈功能。是来电话SUBSTRING()还是SUBSTR()INSTR()还是什么?
  • 提示,this is how to emulate the REPEAT() function on SQLite
  • 您如何模仿VALUES()构造函数,如SELECT * FROM (VALUES (1), (2)) t(a)中所示?很少有数据库具有原生支持
  • 实际上,如果不支持,您将如何模拟派生列列表(一次性别名table(column))? Here's a funky idea
  • 实际上,让我们讨论用它们构建的行值表达式和谓词。这:(a, b) > (x, y)与此相同:a > x OR a = x AND b > y
  • 无法支持前者
  • PostgreSQL的UPDATE .. RETURNING可以使用Oracle 12c中的PL / SQL块进行模拟:

    declare
      t0 dbms_sql.number_table;
      t1 dbms_sql.date_table;
      c0 sys_refcursor;
      c1 sys_refcursor;
    begin
      update "TEST"."T_2155"
      set "TEST"."T_2155"."D1" = date '2003-03-03'
      returning 
        "TEST"."T_2155"."ID", 
        "TEST"."T_2155"."D1"
      bulk collect into t0, t1;
      ? := sql%rowcount; // Don't forget to fetch the row count
      open c0 for select * from table(t0);
      open c1 for select * from table(t1);
      ? := c0; // These need to be bound as OracleTypes.CURSOR OUT params
      ? := c1; // These need to be bound as OracleTypes.CURSOR OUT params
    end;
    

结论

正如您所看到的,它可以完全完成。我已经完成了它,它被称为jOOQ。它可能是我职业生涯中最大的挑战,而且很有趣。 jOOQ 3.10 will feature a parsertranslate from a SQL string (in any dialect) to another SQL string (in a specific dialect),这是供应商不可知的下一级别。

但要到达这里还有很长的路要走。在我开始使用jOOQ(2009年开始)之前,我已经深入研究了Oracle SQL和基于JDBC的内部框架(就像你计划编写的那样)。我写了jOOQ,因为我已经看到很多内部框架正在编写,但没有一个能够很好地完成工作。开发人员总是处理SELECT .. FROM .. WHERE - 这很容易。有些人设法在游戏中获得JOIN,也许GROUP BY就是这样。然后他们放弃了这项任务,因为他们有更重要的事情要做,而不是维护枯燥乏味的基础设施软件。

现在,我不知道你自己的动机是什么,但我的建议是:

  • 如果要编写与供应商无关的SQL
  • ,请使用jOOQ
  • 如果要实现与供应商无关的对象图持久性
  • ,请使用Hibernate

您可以尝试构建自己的jOOQ(或Hibernate)。这是一个有趣的挑战。但如果您有截止日期,我建议您查看上述选项。

答案 1 :(得分:5)

首先,数据库独立性很难。真的,真的很难;要在不使用ORM或其他工具的情况下实现它,您将需要交换解决方案设计的其他方面。简化和可维护性将受到影响,实施解决方案的努力也将受到影响。

所以,我真的非常确定这是一个高优先级的要求 - 假设“如果我们想从Oracle切换到SQL Server会怎么样”的问题 - 在我看来 - 没有足够的理由来招致额外的成本...

如果您必须提供此功能,并且ORM是迄今为止最简单的方法。 ORM框架在设计时考虑了数据库独立性(其复杂性至少部分归因于该要求)。他们通过将数据库实现抽象到更高级别来实现这一目标;而不是在SQL语句中思考,我们鼓励您考虑域对象。

说完了所有......

我已经提供了一个允许数据库独立的解决方案(不是Java,但原则就是这样)。我们将SQL语句存储为资源,并通过资源文件加载它们。默认值为ANSI SQL,在任何现代SQL兼容数据库上工作。

我们支持的每种数据库风格的资源文件(在我们的例子中是MySQL和Oracle),并且使用覆盖来加载特定于数据库的SQL语句(如果它们存在)。

这在大多数语言中都像国际化一样 - 首先查找特定于语言环境的字符串,如果找不到,则回退到默认字符串。

Java的资源包会让这很容易。在您的应用程序中不对您的SQL进行硬编码还有其他好处 - 修复数据库问题而不更改应用程序代码要容易得多,您可以将修补程序部署为“资源”更新,而不是运送新的二进制文件等。

答案 2 :(得分:5)

正如我在my book中所解释的,有两种方法可以解决数据库的可移植性问题:

  1. 减去非常见功能 - 您可以删除所有特定于数据库的功能,这将为您提供SQL-92功能集。这就是ORM工具所做的事情,因为所有RDBMS几乎完全相同地处理了常见的CRUD语句。但是,当您需要执行特定于数据库的查询时,请turn to native SQL
  2. 专业化 - 您可以使用特定于数据库的方言并模拟从一个数据库到另一个数据库的功能,例如PIVOT。这是jOOQ使用的方法。
  3. 但是,既然你明确说过:

      

    是否有任何模式或方法可以在JDBC中实现数据库独立性   (不使用任何其他ORM框架或工具)。

    您需要使用JDBC API解决此问题,对吗?

    实现这一目标的唯一方法是在不采用实际意味着实现自己的数据访问框架的ORM或jOOQ方法的情况下,为每个受支持的数据库使用自定义DAO。

    因此,您可以定义服务层使用的DAO接口,例如ProductDAO,并为每个受支持的数据库实现每个接口:

    • OracleProductDAOImpl
    • MySQLProductDAOImpl
    • PostgreSQLProductDAOImpl
    • SQLServerProductDAOImpl

    或者,您可以使用单个ProductDAOImpl,但是您需要使用存储过程并确保每个存储过程都在数据库中实现:

    所以,虽然它是可能的,但是当我们没有Hibernate或者jOOQ时,你基本上就像我们在2000年初那样。根据我的经验,与使用成熟的数据访问框架相比,它的工作量更大。

答案 3 :(得分:3)

在我看来,通过创建一个允许您一般访问数据库的库,您实际上要在那里构建的内容与许多ORM已经完成的内容非常相似。

你试过jOOQ吗?它与其他ORM完全不同,并且可以满足您的需求。人们可以将其称为“工具”,但我也可以将你想要建立的东西称为“工具”。

jOOQ努力成为一种java本地语言,用于进行便携式数据库调用,听起来就像是你正在寻找的。

https://www.jooq.org/

答案 4 :(得分:1)

虽然参加派对非常晚,而且没有真正带来任何新内容,但我还是要赞一下并扩展Neville'sVlad's答案(作为一个合理的选择)。

像jOOQ和Hibernate这样的框架可能是更好的方法,尤其是 IF 你想要获得的是完全通用的东西(即处理大多数可以想象的数据库操作),而不是涉及SQL 的少数特定任务。但是,由于它们似乎不适合你,以某种方式外化SQL 实际上可能会让你走得太远(从SQL方言的角度来看,不一定是来自JDBC)在Lukas' answer之前很好地概述了约束问题。)

我之前曾在Oracle和DB2上运行过自定义的基于Java的ETL引擎(成功)。您可以想象,我们需要在速度方面获得最佳查询,我们的DBA正在使用本书中的每一个技巧优化它们,然后是一些!因此,不仅我们有两种SQL方言可供使用,而且我们必须执行的查询也是高度自定义的(提示等)...我们解决的解决方案是使用模板在运行时生成SQL引擎(Velocity,当时)和自定义模板,为目标数据库量身定制(可能类似于Hibernate或jOOQ构建最终SQL的方式)。不可否认,我们有自己的"背景"具有明确的需求......