2010-12-14 10 views
6

In meiner Datenbank Interface-Bibliothek jOOQ möchte ich Unterstützung für Oracle (oder DB2, usw.) Pakete hinzufügen. Ich habe bereits eine Unterstützung für gespeicherte Prozeduren/Funktionen implementiert, wobei jedes gespeicherte Objekt als generierte Java-Klasse modelliert wird. Zum Beispiel diese gespeicherte FunktionMapping zwischen Oracle-Paketen und Java-Paketen

CREATE FUNCTION f_author_exists (author_name VARCHAR2) RETURNS NUMBER; 

wird eine Klasse erzeugen, wie diese verwendet werden können (beachten Sie, gibt es auch viele bequeme Methoden, dieses Beispiel zeigt nur das allgemeine Design):

// 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(); 

Der Grund, warum ich eine Zuordnung SQL-Funktion ->Java-Klasse wählte, ist, weil gespeicherte Prozeduren komplexe Rückgabewerte (mehrere OUT- oder IN OUT-Parameter) ermöglichen, die ich nach dem Aufruf der Prozedur nacheinander abrufen kann:

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

Jetzt funktioniert dieses Design gut mit gespeicherten Funktionen/Prozeduren, wobei überladen nicht möglich ist. Innerhalb von Oracle (oder DB2 ist) Pakete jedoch kann ich mehrere Funktionen mit dem gleichen Namen, wie

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; 

Wenn ich eine Klasse pro Funktion (oder Prozedur) erzeugen, werde ich Namensgebung Auseinandersetzungen mit mehreren FAuthorExists Java Klassen . Eine lahme Lösung besteht darin, dem Klassennamen einen Index hinzuzufügen, z. B. FAuthorExists2, FAuthorExists3. Eine andere lahme Lösung besteht darin, eine Art Hash-Wert (oder den Wert selbst) aus den Parameternamen/-typen direkt in den Klassennamen zu erzeugen, wie z. B. FAuthorExistsVARCHAR2, FAuthorExistsVARCHAR2VARCHAR2. Keine der beiden Lösungen ist aus offensichtlichen Gründen wünschenswert.

Hat jemand eine einfache Lösung für dieses Problem? Oder vielleicht eine Idee eines besseren Gesamtdesigns, das keine Probleme mit dem Überladen von Funktionsnamen erzeugen würde?

Jedes Feedback geschätzt!

Antwort

0

Ich habe keinen anderen brauchbaren Weg gefunden, dieses Problem zu lösen, als einen "Überladungsindex" für generierte Klassen zu verwenden. Daher das Paket

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; 

diese Klassen produzieren:

public class FAuthorExists1 { /* ... */ } 
public class FAuthorExists2 { /* ... */ } 

Andere Ideen würden nur neue Konflikte verursachen bei Codegenerierung Zeit oder zur Laufzeit.

UPDATE: Hinweis, scheint diese Lösung auch den einzigen, Situationen wie diese richtig zu handhaben:

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; 

Wie es scheint, ist diese Art von Überlastung möglich ist in PL/SQL, auch.

3

Ihre getReturnValue Funktion bei Anrufzeit die Funktion bestimmen könnte überlastet abhängig zu nennen, wie viele Eingangsparameter festgelegt wurden - aber ich denke, es ist einfacher, am Ende wird sein, wenn Sie so etwas wie setParam1 Stick statt setName

+0

Die 'execute()' Methode macht den eigentlichen Aufruf. Die Methode heißt wegen des Funktionsarguments 'name'' setName() '. Ich habe das im Beispiel behoben, um es klarer zu machen. Deine Idee ist nicht schlecht. Obwohl das Problem darin besteht, dass Funktionsnamen mit sehr unterschiedlichen Argumentgruppen überladen werden, kann es verwirrend sein, herauszufinden, welche Kombination von Argumenten möglich ist. Aber mit den Convenience-Methoden könnte das tatsächlich funktionieren! +1 für die Idee, den richtigen Aufruf zur Laufzeit zu bestimmen –

+0

@Lukas passend zu Argumenttypen statt Namen war was ich mit meinem Vorschlag meinte - ich dachte, es könnte einfacher sein. Ich denke, dass beides prinzipiell möglich wäre. –

+0

Es ist einfach, eine gute Implementierung zu finden, wie man die Funktion basierend auf Argumentnamen/types/position aufruft. Aber der schwierige Teil besteht darin, den generierten Code für Entwickler einfach zu verwenden. Deshalb verwende ich den Namen des Arguments in den generierten Methoden –

0

Sie könnten die Beschränkungen der Überlastung überwinden, indem Sie für jede Funktion eindeutige Namen vergeben.Dies würde auch die Lesbarkeit des Codes verbessern (das ist ein Grund why Golang doesn't have overloading). Zum Beispiel f_author_name_exists, f_author_name_country_exists. Eine andere Möglichkeit, die Java-Klassen zu komplizieren, besteht darin, zur Laufzeit zu entscheiden, welche Prozedur aufgerufen werden soll, je nachdem welcher überladene Java-Konstruktor verwendet wurde oder welche Setter verwendet wurden.

+0

Danke für Ihre Hinweise. Wie in der Frage erwähnt, handelt es sich um [jOOQ] (http://www.jooq.org), ein Dienstprogramm, das Quellcode für gespeicherte Prozeduren generiert, so dass ich keine Kontrolle über das Überladen von Prozedurnamen habe - was ich nicht weiß. überhaupt nicht. Es trägt zur Aussagekraft einer API beim Erstellen von Convenience-Methoden bei. Auf der anderen Seite ist es wegen des Vorhandenseins von OUT-Parametern schwierig zu entscheiden, welche Prozedur zur Laufzeit aufgerufen wird, wenn dieser Aufruf zur Codegenerierungszeit nicht fest verdrahtet ist ... –