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

Roh*_*017 9 java sql jdbc

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

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

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

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

Luk*_*der 30

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

我们来谈谈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类型?真?
  • 如果数据库抛出多个异常怎么办?
  • 如果数据库通过与异常不同的API引发错误怎么办(hello SQL Server)
  • 如果您需要在获取异常之前收集警告,该怎么办?
  • 您是否知道某些数据库首先向您发送更新计数,然后才会向您发送实际结果集(例如,当触发器触发时)
  • 您是否考虑过处理多个结果集?
  • 现在将上述与形式OUT参数结合起来
  • 我们来谈谈这种BOOLEAN类型
  • ......我可以坚持几个小时.本网站上有更多例子
  • 您是否知道某些PostgreSQL语句在autoCommit设置为true 时不起作用?
  • 不是每个人都支持保存点
  • 想使用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()过滤吗?在这里,我已经为你记录了它.
  • 有些数据库SELECT不支持FROM.在其他数据库中,您需要类似DUAL表格的东西.你去了,所有记录.
  • 有些数据库假装他们不需要那个DUAL表,直到他们的解析器中断并且你仍然需要它(你好MySQL)
  • 有些数据库支持SELECT没有FROM,但他们需要FROMWHERE/ HAVING/GROUP BY
  • 你对此有何看法:(SELECT 1 UNION SELECT 2) UNION ALL SELECT 3.它适用于所有数据库吗?(我的意思是带括号的嵌套)
  • 是否EXCEPT ALL支持?难道EXCEPT甚至支持?
  • 是否FULL OUTER JOIN支持?
  • 派生表需要别名还是没有别名?
  • AS派生表上是否允许使用关键字?
  • ORDER BY子句是否可以包含引用该SELECT子句中别名的表达式?或只是引用该FROM子句中的列的表达式?
  • ORDER BY子句可以包含表达式吗?
  • 派生表可以包含一个ORDER BY子句吗?
  • 我们来谈谈功能.难道是打电话SUBSTRING()SUBSTR()INSTR()或什么?
  • 提示,这是如何REPEAT()在SQLite上模拟函数
  • 你会如何模仿VALUES()构造函数,如SELECT * FROM (VALUES (1), (2)) t(a)?很少有数据库具有本机支持
  • 实际上,table(column)如果不支持,您将如何模拟派生列列表(一次性别名)?这是一个时髦的想法.
  • 实际上,让我们讨论用它们构建的行值表达式和谓词.这个:(a, b) > (x, y)与此相同:a > x OR a = x AND b > y.前者在任何地方都不受支持
  • UPDATE .. RETURNING可以使用Oracle 12c中的PL/SQL块模拟PostgreSQL :

    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;
    
    Run Code Online (Sandbox Code Playgroud)

结论

如你所见,它可以完全完成.我做到了,它被称为jOOQ.这可能是我职业生涯中最大的挑战,而且很有趣.jOOQ 3.10将提供一个解析器,它可以从一个SQL字符串(在任何方言中)转换为另一个SQL字符串(在特定的方言中),这是供应商不可知的下一级别.

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

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

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

您可以尝试构建自己的jOOQ(或Hibernate).这是一个有趣的挑战.但如果您有最后期限,我建议您查看以上选项.


Nev*_*uyt 5

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

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

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

说了这么多......

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

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

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

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


Vla*_*cea 5

正如我在书中解释的那样,有两种解决数据库可移植性的方法:

  1. 减去非常见功能-您可以删除所有特定于数据库的功能,这将使您拥有SQL-92功能集。这是ORM工具所做的,因为所有RDBMS对通用CRUD语句的处理几乎相同。但是,当您需要执行数据库特定的查询时,可以使用本机SQL
  2. 专业化-您可以使用特定于数据库的方言,并仿真从一个DB到另一个DB的功能,例如PIVOT。这是jOOQ使用的方法。

但是,由于您明确声明:

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

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

唯一不采用ORM或jOOQ方法(实际上意味着实现自己的数据访问框架)的方法是对每个受支持的数据库使用自定义DAO。

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

  • OracleProductDAOImpl
  • MySQLProductDAOImpl
  • PostgreSQLProductDAOImpl
  • SQLServerProductDAOImpl

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

因此,尽管有可能,但是您基本上处于2000年代初期的水平,那时我们既没有Hibernate也没有jOOQ。根据我的经验,与使用成熟的数据访问框架相比,它的工作量更大。


Cam*_*amW 3

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

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

jOOQ 致力于成为一种用于进行可移植数据库调用的 Java 本机语言,因此听起来这正是您正在寻找的。

https://www.jooq.org/

  • 好的,但请记住,您在这里所说的几乎正是构建 jOOQ 的人的起点。因此,最终您将使用一种工具或框架,它只是您构建的工具或框架,而不是预先存在的工具或框架。 (3认同)