wake-up-neo.com

Aufruf der gespeicherten Prozedur aus Java/JPA

Ich schreibe eine einfache Webanwendung, um eine gespeicherte Prozedur aufzurufen und Daten abzurufen. Dies ist eine sehr einfache Anwendung, die mit der Datenbank des Kunden interagiert. Wir übergeben die Personal-ID und die Firmen-ID, und die gespeicherte Prozedur gibt die Details zu den Mitarbeitern zurück.

Die Webanwendung kann keine Daten aktualisieren/löschen und verwendet SQL Server.

Ich setze meine Webanwendung in Jboss AS ein. Sollte ich JPA verwenden, um auf die gespeicherte Prozedur oder CallableStatement zuzugreifen. Jeder Vorteil der Verwendung von JPA in diesem Fall.

Wie lautet die SQL-Anweisung, um diese gespeicherte Prozedur aufzurufen. Ich habe noch nie gespeicherte Prozeduren verwendet und kämpfe mit dieser Prozedur. Google war keine große Hilfe.

Hier ist die gespeicherte Prozedur:

CREATE procedure getEmployeeDetails (@employeeId int, @companyId int)
as
begin
    select firstName, 
           lastName, 
           gender, 
           address
      from employee et
     where et.employeeId = @employeeId
       and et.companyId = @companyId
end

Aktualisieren:

Für alle anderen, die Probleme haben, die gespeicherte Prozedur mitJPAaufzurufen.

Query query = em.createNativeQuery("{call getEmployeeDetails(?,?)}",
                                   EmployeeDetails.class)           
                                   .setParameter(1, employeeId)
                                   .setParameter(2, companyId);

List<EmployeeDetails> result = query.getResultList();

Dinge, die mir aufgefallen sind:

  1. Die Parameternamen funktionierten für mich nicht. Versuchen Sie also, den Parameterindex zu verwenden.
  2. Korrekte SQL-Anweisung {call sp_name(?,?)} anstelle von call sp_name(?,?)
  3. Wenn die gespeicherte Prozedur eine Ergebnismenge zurückgibt, funktioniert getSingleResult nicht, auch wenn Sie nur eine Zeile kennen
  4. Übergeben Sie einen resultSetMapping-Namen oder eine Ergebnisklassendetails
85
user431514

JPA 2.1 unterstützt jetzt gespeicherte Prozeduren. Lesen Sie das Java-Dokument hier .

Beispiel:

StoredProcedureQuery storedProcedure = em.createStoredProcedureQuery("sales_tax");
// set parameters
storedProcedure.registerStoredProcedureParameter("subtotal", Double.class, ParameterMode.IN);
storedProcedure.registerStoredProcedureParameter("tax", Double.class, ParameterMode.OUT);
storedProcedure.setParameter("subtotal", 1f);
// execute SP
storedProcedure.execute();
// get result
Double tax = (Double)storedProcedure.getOutputParameterValue("tax");

Siehe detailliertes Beispiel hier .

49
Pau Kiat Wee

Ich setze meine Webanwendung in Jboss AS ein. Sollte ich JPA verwenden, um auf die gespeicherte Prozedur oder das CallableStatement zuzugreifen. Jeder Vorteil der Verwendung von JPA in diesem Fall.

Es wird nicht wirklich von JPA unterstützt, aber es ist doable . Trotzdem würde ich nicht diesen Weg gehen: 

  • die Verwendung von JPA, nur um das Ergebnis eines Aufrufs einer gespeicherten Prozedur in einigen Beans abzubilden, ist wirklich übertrieben,
  • insbesondere angesichts der Tatsache, dass JPA nicht geeignet ist, gespeicherte Prozeduren aufzurufen (die Syntax wird ziemlich ausführlich sein).

Ich würde daher lieber die Verwendung von Spring für den JDBC-Datenzugriff oder einen Data-Mapper wie MyBatis oder - angesichts der Einfachheit Ihrer Anwendung - von JDBC und CallableStatement in Betracht ziehen. Eigentlich wäre JDBC wahrscheinlich meine Wahl. Hier ist ein grundlegendes Kickoff-Beispiel: 

CallableStatement cstmt = con.prepareCall("{call getEmployeeDetails(?, ?)}");
cstmt.setInt("employeeId", 123);
cstmt.setInt("companyId", 456);
ResultSet rs = cstmt.executeQuery();

Referenz

21
Pascal Thivent

Sie müssen die Parameter an die gespeicherte Prozedur übergeben.

Es sollte so funktionieren:

    List result = em
      .createNativeQuery("call getEmployeeDetails(:employeeId,:companyId)")
      .setParameter("emplyoyeeId", 123L)
      .setParameter("companyId", 456L)
      .getResultList();

Update:

Oder vielleicht sollte es nicht.

Im Buch EJB3 in Aktion heißt es auf Seite 383, dass JPA keine gespeicherten Prozeduren unterstützt (Seite ist nur eine Vorschau, Sie erhalten nicht den vollständigen Text, das gesamte Buch ist als verfügbar ein Download an mehreren Stellen, einschließlich dieses , ich weiß nicht, ob dies legal ist). 

Jedenfalls lautet der Text so:

Gespeicherte JPA- und Datenbankprozeduren

Wenn Sie ein großer Fan von SQL sind, können Sie bereit, die Macht von .__ auszunutzen. gespeicherte Datenbankprozeduren . Leider unterstützt die JPA keine gespeicherte Prozeduren, und Sie müssen hängen von einer proprietären Funktion von .__ ab. Ihr Persistenzanbieter. Jedoch, Sie können einfache gespeicherte Funktionen verwenden (ohne out-Parameter) mit einem nativen SQL-Abfrage.

9

Abrufen der Ausgabeparameter für gespeicherte Prozeduren mit JPA (2.0 benötigt EclipseLink-Importe und 2.1 nicht)

Obwohl diese Antwort darauf hinausläuft, ein Re-Cord-Set aus einer gespeicherten Prozedur zurückzugeben, poste ich hier, da ich mich lange Zeit erkundigt habe und dieser Thread mir geholfen hat.

Meine Anwendung verwendete Eclipselink-2.3.1, aber ich werde ein Upgrade auf Eclipselink-2.5.0 erzwingen, da JPA 2.1 für gespeicherte Prozeduren eine wesentlich bessere Unterstützung bietet.

Verwendung von EclipseLink-2.3.1/JPA-2.0: Implementierungsabhängig

Diese Methode erfordert den Import von EclipseLink-Klassen aus "org.Eclipse.persistence", daher ist sie spezifisch für die Eclipselink-Implementierung.

Ich habe es unter " http://www.yenlo.nl/de/calling-Oracle-stored-procedures-von-eclipselink-with-multiple-out-parameters " gefunden.

StoredProcedureCall storedProcedureCall = new StoredProcedureCall();
storedProcedureCall.setProcedureName("mypackage.myprocedure");
storedProcedureCall.addNamedArgument("i_input_1"); // Add input argument name.
storedProcedureCall.addNamedOutputArgument("o_output_1"); // Add output parameter name.
DataReadQuery query = new DataReadQuery();
query.setCall(storedProcedureCall);
query.addArgument("i_input_1"); // Add input argument names (again);
List<Object> argumentValues = new ArrayList<Object>();
argumentValues.add("valueOf_i_input_1"); // Add input argument values.
JpaEntityManager jpaEntityManager = (JpaEntityManager) getEntityManager();
Session session = jpaEntityManager.getActiveSession();
List<?> results = (List<?>) session.executeQuery(query, argumentValues);
DatabaseRecord record = (DatabaseRecord) results.get(0);
String result = String.valueOf(record.get("o_output_1")); // Get output parameter

Verwendung von EclipseLink-2.5.0/JPA-2.1: Implementierung unabhängig (in diesem Thread bereits dokumentiert)

Diese Methode ist implementierungsunabhängig (Eclipslink-Importe sind nicht erforderlich).

StoredProcedureQuery query = getEntityManager().createStoredProcedureQuery("mypackage.myprocedure");
query.registerStoredProcedureParameter("i_input_1", String.class, ParameterMode.IN);
query.registerStoredProcedureParameter("o_output_1", String.class, ParameterMode.OUT);
query.setParameter("i_input_1", "valueOf_i_input_1");
boolean queryResult = query.execute();
String result = String.valueOf(query.getOutputParameterValue("o_output_1"));
9

Wenn Sie EclipseLink verwenden, können Sie @NamedStoredProcedureQuery oder StoreProcedureCall verwenden, um gespeicherte Prozeduren auszuführen, einschließlich Prozeduren mit Ausgabeparametern oder Out-Cursor. Unterstützung für gespeicherte Funktionen und PLSQL-Datentypen ist ebenfalls verfügbar.

Siehe http://en.wikibooks.org/wiki/Java_Persistence/Advanced_Topics#Stored_Procedures

6
James

Für mich funktionierten nur folgende mit Oracle 11g und Glassfish 2.1 (Toplink):

Query query = entityManager.createNativeQuery("BEGIN PROCEDURE_NAME(); END;");
query.executeUpdate();

Die Variante mit geschweiften Klammern führte zu ORA-00900.

6
Dmitry Chornyi

Folgendes funktioniert für mich:

Query query = em.createNativeQuery("BEGIN VALIDACIONES_QPAI.RECALC_COMP_Assembly('X','X','X',0); END;");
query.executeUpdate();
3
roberto quevedo
  1. Für eine einfache gespeicherte Prozedur, die IN/OUT-Parameter wie folgt verwendet

    CREATE OR REPLACE PROCEDURE count_comments (  
       postId IN NUMBER,  
       commentCount OUT NUMBER )  
    AS 
    BEGIN 
        SELECT COUNT(*) INTO commentCount  
        FROM post_comment  
        WHERE post_id = postId; 
    END;
    

    Sie können es von JPA aus wie folgt aufrufen:

    StoredProcedureQuery query = entityManager
        .createStoredProcedureQuery("count_comments")
        .registerStoredProcedureParameter(1, Long.class, 
            ParameterMode.IN)
        .registerStoredProcedureParameter(2, Long.class, 
            ParameterMode.OUT)
        .setParameter(1, 1L);
    
    query.execute();
    
    Long commentCount = (Long) query.getOutputParameterValue(2);
    
  2. Für eine gespeicherte Prozedur, die einen SYS_REFCURSOR OUT-Parameter verwendet:

    CREATE OR REPLACE PROCEDURE post_comments ( 
       postId IN NUMBER, 
       postComments OUT SYS_REFCURSOR ) 
    AS 
    BEGIN
        OPEN postComments FOR
        SELECT *
        FROM post_comment 
        WHERE post_id = postId; 
    END;
    

    Sie können es wie folgt aufrufen:

    StoredProcedureQuery query = entityManager
        .createStoredProcedureQuery("post_comments")
        .registerStoredProcedureParameter(1, Long.class, 
             ParameterMode.IN)
        .registerStoredProcedureParameter(2, Class.class, 
             ParameterMode.REF_CURSOR)
        .setParameter(1, 1L);
    
    query.execute();
    
    List<Object[]> postComments = query.getResultList();
    
  3. Für eine SQL-Funktion, die wie folgt aussieht:

    CREATE OR REPLACE FUNCTION fn_count_comments ( 
        postId IN NUMBER ) 
        RETURN NUMBER 
    IS
        commentCount NUMBER; 
    BEGIN
        SELECT COUNT(*) INTO commentCount 
        FROM post_comment 
        WHERE post_id = postId; 
        RETURN( commentCount ); 
    END;
    

    Man kann es so nennen:

    BigDecimal commentCount = (BigDecimal) entityManager
    .createNativeQuery(
        "SELECT fn_count_comments(:postId) FROM DUAL"
    )
    .setParameter("postId", 1L)
    .getSingleResult();
    

    Zumindest bei Verwendung von Hibernate 4.x und 5.x, da die JPA StoredProcedureQuery nicht für SQL-FUNKTIONEN funktioniert.

Weitere Informationen zum Aufrufen von gespeicherten Prozeduren und Funktionen bei Verwendung von JPA und Ruhezustand finden Sie in den folgenden Artikeln

3
Vlad Mihalcea

Das hat bei mir funktioniert.

@Entity
@Table(name="acct")
@NamedNativeQueries({
 @NamedNativeQuery(callable=true, name="Account.findOne", query="call sp_get_acct(?), resultClass=Account.class)})
public class Account{
 // Code 
}

Hinweis: Wenn Sie sich in Zukunft für die Verwendung der Standardversion von findOne entscheiden, kommentieren Sie einfach die Annotation NamedNativeQueries und JPA wird auf die Standardeinstellung gesetzt

2
Amit

Vielleicht ist es für Sql Srver nicht dasselbe, aber für Leute, die Oracle und Eclipslink verwenden, funktioniert es für mich 

beispiel: eine Prozedur mit einem IN-Parameter (Typ CHAR) und zwei OUT-Parametern (NUMBER & VARCHAR)

in der persistence.xml deklarieren Sie die Persistence-Unit:

<persistence-unit name="presistanceNameOfProc" transaction-type="RESOURCE_LOCAL">
    <provider>org.Eclipse.persistence.jpa.PersistenceProvider</provider>
    <jta-data-source>jdbc/DataSourceName</jta-data-source>
    <mapping-file>META-INF/eclipselink-orm.xml</mapping-file>
    <properties>
        <property name="eclipselink.logging.level" value="FINEST"/>
        <property name="eclipselink.logging.logger" value="DefaultLogger"/>
        <property name="eclipselink.weaving" value="static"/>
        <property name="eclipselink.ddl.table-creation-suffix" value="JPA_STORED_PROC" />
    </properties>
</persistence-unit>

und deklarieren Sie die Struktur der proc in der Datei eclipselink-orm.xml

<?xml version="1.0" encoding="UTF-8"?><entity-mappings version="2.0"
xmlns="http://Java.Sun.com/xml/ns/persistence/orm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://Java.Sun.com/xml/ns/persistence/orm orm_2_0.xsd">
<named-stored-procedure-query name="PERSIST_PROC_NAME" procedure-name="name_of_proc" returns-result-set="false">
    <parameter direction="IN" name="in_param_char" query-parameter="in_param_char" type="Character"/>
    <parameter direction="OUT" name="out_param_int" query-parameter="out_param_int" type="Integer"/>
    <parameter direction="OUT" name="out_param_varchar" query-parameter="out_param_varchar" type="String"/>
</named-stored-procedure-query>

im code musst du deinen proc einfach so aufrufen:

try {
        final Query query = this.entityManager
                .createNamedQuery("PERSIST_PROC_NAME");
        query.setParameter("in_param_char", 'V'); 
        resultQuery = (Object[]) query.getSingleResult();

    } catch (final Exception ex) {
        LOGGER.log(ex);
        throw new TechnicalException(ex);
    }

um die zwei Ausgangsparameter zu erhalten:

Integer myInt = (Integer) resultQuery[0];
String myStr =  (String) resultQuery[1];
2
Réda

Diese Antwort kann hilfreich sein, wenn Sie über einen Entitätsmanager verfügen

Ich hatte eine gespeicherte Prozedur, um die nächste Nummer zu erstellen.

Client-Seite

 Object on = entityManager.createNativeQuery("EXEC getNextNmber").executeUpdate();
        log.info("New order id: " + on.toString());

Datenbankseite (SQL Server) Ich habe eine Prozedur mit dem Namen getNextNmber gespeichert.

1
tosha Shah

JPA 2.0 unterstützt keine RETURN-Werte, nur Aufrufe.

Meine Lösung war. Erstellen Sie eine FUNKTION, die PROCEDURE aufruft.

Innerhalb von Java-Code führen Sie also eine NATIVE QUERY aus, die Oracle FUNCTION aufruft.

1

Ab JPA 2.1 unterstützt JPA das Aufrufen gespeicherter Prozeduren mithilfe der dynamischen StoredProcedureQuery und der deklarativen @NamedStoredProcedureQuery.

0
H.Ostwal

Versuchen Sie diesen Code:

return em.createNativeQuery("{call getEmployeeDetails(?,?)}",
                               EmployeeDetails.class)           
                               .setParameter(1, employeeId)
                               .setParameter(2, companyId).getResultList();
0

Sie können @Query(value = "{call PROC_TEST()}", nativeQuery = true) in Ihrem Repository verwenden. Das hat bei mir funktioniert.

Achtung: verwende '{' und '}', sonst funktioniert es nicht.

0

persistence.xml

 <persistence-unit name="PU2" transaction-type="RESOURCE_LOCAL">
<non-jta-data-source>jndi_ws2</non-jta-data-source>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties/>

codigo Java

  String PERSISTENCE_UNIT_NAME = "PU2";
    EntityManagerFactory factory2;
    factory2 = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME);

    EntityManager em2 = factory2.createEntityManager();
    boolean committed = false;
    try {

        try {
            StoredProcedureQuery storedProcedure = em2.createStoredProcedureQuery("PKCREATURNO.INSERTATURNO");
            // set parameters
            storedProcedure.registerStoredProcedureParameter("inuPKEMPRESA", BigDecimal.class, ParameterMode.IN);
            storedProcedure.registerStoredProcedureParameter("inuPKSERVICIO", BigDecimal.class, ParameterMode.IN);
            storedProcedure.registerStoredProcedureParameter("inuPKAREA", BigDecimal.class, ParameterMode.IN);
            storedProcedure.registerStoredProcedureParameter("isbCHSIGLA", String.class, ParameterMode.IN);
            storedProcedure.registerStoredProcedureParameter("INUSINCALIFICACION", BigInteger.class, ParameterMode.IN);
            storedProcedure.registerStoredProcedureParameter("INUTIMBRAR", BigInteger.class, ParameterMode.IN);
            storedProcedure.registerStoredProcedureParameter("INUTRANSFERIDO", BigInteger.class, ParameterMode.IN);
            storedProcedure.registerStoredProcedureParameter("INTESTADO", BigInteger.class, ParameterMode.IN);
            storedProcedure.registerStoredProcedureParameter("inuContador", BigInteger.class, ParameterMode.OUT);

            BigDecimal inuPKEMPRESA = BigDecimal.valueOf(1);
            BigDecimal inuPKSERVICIO = BigDecimal.valueOf(5);
            BigDecimal inuPKAREA = BigDecimal.valueOf(23);
            String isbCHSIGLA = "";
            BigInteger INUSINCALIFICACION = BigInteger.ZERO;
            BigInteger INUTIMBRAR = BigInteger.ZERO;
            BigInteger INUTRANSFERIDO = BigInteger.ZERO;
            BigInteger INTESTADO = BigInteger.ZERO;
            BigInteger inuContador = BigInteger.ZERO;

            storedProcedure.setParameter("inuPKEMPRESA", inuPKEMPRESA);
            storedProcedure.setParameter("inuPKSERVICIO", inuPKSERVICIO);
            storedProcedure.setParameter("inuPKAREA", inuPKAREA);
            storedProcedure.setParameter("isbCHSIGLA", isbCHSIGLA);
            storedProcedure.setParameter("INUSINCALIFICACION", INUSINCALIFICACION);
            storedProcedure.setParameter("INUTIMBRAR", INUTIMBRAR);
            storedProcedure.setParameter("INUTRANSFERIDO", INUTRANSFERIDO);
            storedProcedure.setParameter("INTESTADO", INTESTADO);
            storedProcedure.setParameter("inuContador", inuContador);

            // execute SP
            storedProcedure.execute();
            // get result

            try {
                long _inuContador = (long) storedProcedure.getOutputParameterValue("inuContador");
                varCon = _inuContador + "";
            } catch (Exception e) {
            } 
        } finally {

        }
    } finally {
        em2.close();
    }

Um eine gespeicherte Prozedur aufzurufen, können wir die Callable-Anweisung im Java.sql-Paket verwenden.

0
vinoth