Oracle 包和 Java 包之间的映射

Posted

技术标签:

【中文标题】Oracle 包和 Java 包之间的映射【英文标题】:Mapping between Oracle Packages and Java Packages 【发布时间】:2011-05-25 06:21:16 【问题描述】:

在我的数据库接口库jOOQ 中,我想添加对Oracle(或DB2 等)包的支持。我已经实现了存储过程/函数支持,其中每个存储对象都被建模为生成的 Java 类。比如这个存储函数

CREATE FUNCTION f_author_exists (author_name VARCHAR2) RETURNS NUMBER;

会生成一个可以这样使用的类(注意,还有很多方便的方法,这个例子只是展示了一般的设计):

// A new "function call instance". The function needs to be instanciated
// once per call
FAuthorExists f = new FAuthorExists();

// Set the function parameters on the call instance and call it
f.setAuthorName("Paulo");
f.execute(connection);

// Fetch the result from the function call instance
BigDecimal result = f.getReturnValue();

我选择映射 SQL 函数 -> Java 类 的原因是因为存储过程允许我想要的复杂返回值(几个 OUT 或 IN OUT 参数)调用过程后可以一一获取:

p.getOutParam1();
p.getOutParam2();

现在这种设计适用于存储函数/过程,其中 重载 是不可能的。但是,在 Oracle(或 DB2)的包中,我可以拥有多个同名的函数,例如

CREATE PACKAGE my_package IS
  FUNCTION f_author_exists (name VARCHAR2) RETURNS NUMBER;
  FUNCTION f_author_exists (name VARCHAR2, country VARCHAR2) RETURNS NUMBER;
END my_package;

当我为每个函数(或过程)生成一个类时,我将与几个FAuthorExists Java 类发生命名冲突。一个蹩脚的解决方案是在类名中添加一个索引,例如FAuthorExists2FAuthorExists3。另一个蹩脚的解决方案是从参数名称/类型直接生成某种哈希值(或值本身)到类名中,例如FAuthorExistsVARCHAR2FAuthorExistsVARCHAR2VARCHAR2。出于显而易见的原因,这两种解决方案都是不可取的。

有没有人有这个问题的简单解决方案?或者可能是一个更好的整体设计理念,不会产生此类函数名称重载问题?

感谢任何反馈!

【问题讨论】:

【参考方案1】:

您的getReturnValue 函数可以在调用时确定要调用哪个重载函数,具体取决于设置了多少输入参数 - 但我认为如果您坚持使用 setParam1 而不是 @987654323 之类的东西,它最终会更简单@

【讨论】:

execute() 方法进行实际调用。由于函数参数name,该方法被称为setName()。我在示例中修复了该问题,以使其更加清晰。你的想法不错。虽然,问题是如果您的函数名称重载具有非常不同的参数集,那么找出可能的参数组合可能会变得令人困惑。但是使用方便的方法,这可能真的有效! +1 用于在运行时确定正确调用的想法 @Lukas 匹配参数类型而不是名称是我的建议的意思 - 我认为它可能更简单。我认为原则上两者都是可能的。 很容易找到关于如何根据参数名称/类型/位置调用函数的良好实现。但困难的部分是使生成的代码易于开发人员使用。这就是我在生成的方法中使用参数名称的原因【参考方案2】:

我发现除了在生成的类上使用“重载索引”之外没有其他可行的方法来解决这个问题。因此,包

CREATE PACKAGE my_package IS
  FUNCTION f_author_exists (name VARCHAR2) RETURNS NUMBER;
  FUNCTION f_author_exists (name VARCHAR2, country VARCHAR2) RETURNS NUMBER;
END my_package;

将产生这些类:

public class FAuthorExists1  /* ... */ 
public class FAuthorExists2  /* ... */ 

其他想法只会在代码生成时或运行时引起新的冲突。

更新:请注意,此解决方案似乎也是唯一能正确处理此类情况的解决方案:

CREATE PACKAGE my_package IS
  PROCEDURE f_author_exists (name VARCHAR2);
  PROCEDURE f_author_exists (name CHAR);
  PROCEDURE f_author_exists (name CHAR, country OUT VARCHAR2);
END my_package;

看起来,这种重载在 PL/SQL 中也是可能的。

【讨论】:

【参考方案3】:

您可以通过为每个函数指定唯一名称来克服重载的限制。这也将提高代码的可读性(这是why Golang doesn't have overloading 的原因之一)。例如 f_author_name_exists、f_author_name_country_exists。

另一种会使 Java 类复杂化的方法是在运行时根据使用的重载 Java 构造函数或使用的 setter 来决定调用哪个过程。

【讨论】:

感谢您的提示。正如问题中所述,这是关于jOOQ,一个为存储过程生成源代码的实用程序,所以我无法控制过程名称重载——我一点也不介意。在创建便捷方法时,它增加了 API 的表现力。另一方面,由于OUT 参数的存在,如果该调用在代码生成时没有硬连线,则很难决定在运行时调用哪个过程......

以上是关于Oracle 包和 Java 包之间的映射的主要内容,如果未能解决你的问题,请参考以下文章

Hibernate 映射关系

双数据源实体类映射oracle字段有的字段无法映射

Hibernate 映射继承

.NET (ADFS / WIF) 和 Java (Federation) 之间的属性如何映射

无法使用 Pig 中的 Elephant Bird 访问带有包和元组的嵌套 JSON

将 java.long 映射到 oracle.Number(14)