java.sql.ResultSet、CallableStatement、SQLInput 没有通用接口

Posted

技术标签:

【中文标题】java.sql.ResultSet、CallableStatement、SQLInput 没有通用接口【英文标题】:No common interface to java.sql.ResultSet, CallableStatement, SQLInput 【发布时间】:2011-05-27 20:08:58 【问题描述】:

情况是这样的

在jOOQ 中,非常需要对 JDBC 进行抽象。我希望 jOOQ 客户端代码不知道这样一个事实,即一些数据是从简单的 ResultSet 中检索的,一些数据是从 SQLInput(对于 UDT)或 CallableStatements(对于存储过程/函数)中检索的。因此,我想对这些 JDBC 类型添加抽象:

java.sql.ResultSet
java.sql.CallableStatement
java.sql.SQLInput
java.sql.SQLOutput

现在它们的工作方式几乎相同。对于java.sql.Types 中的每种数据类型,它们通常都有一个getset 方法。例如,他们提供了类似

的方法
BigDecimal getBigDecimal(int index);
int getInt(int index);

它们都有类似的方法

boolean wasNull();

问题

不幸的是,这些 JDBC 接口没有扩展单个通用接口,这让那些想要在这里编写像 sn-p 这样的通用 JDBC 代码的人的生活更轻松(只是一个支持我的问题的示例):

// As JDBC has no support for BigInteger types directly,
// read a BigDecimal and transform it to a BigInteger
BigDecimal result = null;

if (source instanceof ResultSet) 
    result = ((ResultSet) source).getBigDecimal(index);

else if (source instanceof CallableStatement) 
    result = ((CallableStatement) source).getBigDecimal(index);

else if (source instanceof SQLInput) 
    result = ((SQLInput) source).readBigDecimal();


return result == null ? null : result.toBigInteger();

上面的代码需要为他们三个都写,ResultSetCallableStatementSQLInput。而且类似的例子还有很多

我的问题是

有人知道优雅地解决这个问题的 JDBC 扩展库吗? 或者我应该自己为所有这些类型编写一个简单的包装类(或适配器)? 或者您会接受这个事实并继续复制内部库代码?

您更喜欢哪种解决方案以及为什么?感谢您提供任何反馈

【问题讨论】:

这不是适配器设计模式的用途吗? @Skaffman,你可能和我说“包装类”时的意思一样...... 【参考方案1】:
有人知道优雅地解决这个问题的 JDBC 扩展库吗?

不,必须有人发明它。


或者我应该自己为所有这些类型编写一个简单的包装类(或适配器)?

我肯定会去的。从一个通用的包装接口开始。

public interface DataProvider 
    public BigInteger getBigInteger(int columnIndex);
    // ...

让所有具体的包装器实现它。

public class ResultSetDataProvider implements DataProvider 
    private ResultSet resultSet;

    public ResultSetDataProvider(ResultSet resultSet) 
        this.resultSet = resultSet;
    

    public BigInteger getBigInteger(int columnIndex) 
        BigDecimal bigDecimal = resultSet.getBigDecimal(columnIndex);
        return bigDecimal != null ? bigDecimal.toBigInteger() : null;
    

    // ...

改用它。

try 
    // Acquire ResultSet.
    DataProvider dataProvider = new ResultSetDataProvider(resultSet);
    // Process DataProvider.
 finally 
    // Close ResultSet.


或者您会接受这个事实并继续复制内部库代码?

不,我不会。保留您的代码DRY。

【讨论】:

我猜包装器是要走的路。感谢您的详细说明! 我不明白这有多干。您的 CallableStatementDataProvider 必须使用 CallableStatement 私有字段(而不是 ResultSet 一个)复制 public BigInteger getBigInteger(int columnIndex) 方法!我认为没有解决办法,因为 JDBC 库作者未能定义 ResultSetCallableStatement 的公共子类型【参考方案2】:

就个人而言,我不会使用这样的东西。我没有看到你通过这种抽象让我的生活变得更轻松。

我认为 SQL 抽象没有理由从持久层中泄露出来。我进行调用、映射到对象并关闭 SQL 抽象。你听起来像是希望他们留下来,这是个坏主意。

我认为 Spring 的人们已经尽可能简单地使用 JDBC。我可能是错的,但我认为没有理由走你建议的道路。

如果我查看 SQLInput 的 javadocs,我会看到:

此接口,仅用于自定义 映射,由后面的驱动程序使用 场景,程序员永远不会 直接调用 SQLInput 方法。

我不确定您为什么认为有必要公开此接口。

对于 ResultSet 和 CallableStatement(或任何 Statement),它们最终可以返回一个或多个 ResultSet 以返回查询结果。我宁愿看到围绕它的抽象。我相信你暴露其他人是在搅浑水。我不会推荐它。

也许从未做过的事实是不应该做的另一个迹象。但是欢迎您这样做,看看市场是否会投票给您。

【讨论】:

我不确定我是否理解您所指的“SQL 抽象”。是一般的jOOQ还是只是上面的陈述?因为上述语句是 jOOQ 相当内部部分的示例。当然,所有 JDBC 对象都已正确关闭,但出于示例的目的,我省略了 99% 的 jOOQ 代码 ;-) 此外,jOOQ 远远超出了 Spring 人员对 JDBC 所做的工作。比较 Spring JDBC 示例 (static.springsource.org/spring/docs/2.0.x/reference/jdbc.html) 和 jOOQ 示例 (sourceforge.net/apps/trac/jooq/wiki/Examples) 可能不是一个适当的比较,因为 Spring 最高到 3.1 版。无论如何我都不确定我是否会购买它,因为 Spring 的吸引力远远超过 jOOQ。这是我第一次听说。根据我所看到的,我确定我现在不感兴趣,但也许有一天。 您对 Spring 版本的看法是正确的。我的错。不过,功能集在 3.1 中似乎并没有根本的不同。当然,您有权对任何第三方软件发表自己的意见,但既然您已经花时间表达了这种意见,也许您仍然可以考虑实际问题本身? :) 实际上,这是一个相当技术性的问题。 我确实考虑了实际问题。我和其他回答过的人都不知道这样的事情;有人建议需要发明它。如果有人这样做,我不会对做这样的事情或使用它感兴趣。我可以使用的抽象足以满足我的需求。 你好达菲莫!我检查了 Javadoc,关于 SQLInput,你是对的。这让我感到非常惊讶和困惑,因为它似乎是为 UDT 创建自定义映射的完美方式。在我们的应用程序(不是 jOOQ)中,我们使用 Oracle 高级队列在服务器之间进行通信。 Oracle AQ 使用 UDT 将数据从数据库发送到 Java 回调方法。在我们看来,实现 SQLData,从 SQLInput 读取数据似乎是接收该数据的完美方式。让我们看看这些抽象是否对其他人有用...再次感谢您的宝贵时间!【参考方案3】:
    不确定 JDBC 的扩展。 包装类或访问者模式可能有用,具体取决于您设计的其余部分。 如何扩展每一个并让它们实现具有相同方法签名的相同接口以获取所需数据。

【讨论】:

我不确定访客模式。据我所知,这主要用于访问数据结构中的节点,通常对于任何设计来说都是多余的。扩展每一个,让扩展实现相同的接口将恰好是一个“包装类”。 访问者模式可以在访问节点之外使用,但是我不知道你的设计的其余部分是什么样子的,所以这可能不合适。包装类将只包含该类的一个实例并对其进行调用。这与扩展对象和实现附加接口不同。 好的,您对访问者和扩展名的看法是正确的。但是扩展在这里不是一个选项。我们谈论的是 ResultSet、CallableStatement 等。具体实现是 JDBC 驱动程序特定的类,例如oracle.jdbc.driver.OracleResultSetImpl。我不能/不想扩展该类。

以上是关于java.sql.ResultSet、CallableStatement、SQLInput 没有通用接口的主要内容,如果未能解决你的问题,请参考以下文章

如何从 java.sql.ResultSet 获取列名列表 [重复]

java.sql.ResultSet、CallableStatement、SQLInput 没有通用接口

java.sql.ResultSet.next() 光标常见错误总结分析

为啥 java.sql.ResultSet 的“getFloat(column)”方法不返回任何内容,而其他方法则返回?

java.sql.ResultSet 在增加列长度后返回修剪后的字符串

JDBC结果集