web-dev-qa-db-fra.com

Appel de procédure stockée à partir de Java/JPA

J'écris une simple application Web pour appeler une procédure stockée et récupérer des données .. C'est une application très simple, qui interagit avec la base de données du client. Nous transmettons l’identité de l’employé et la société et la procédure stockée renverra les détails de l’employé.

L'application Web ne peut pas mettre à jour/supprimer des données et utilise SQL Server.

Je déploie mon application Web dans Jboss AS. Devrais-je utiliser JPA pour accéder à la procédure stockée ou à CallableStatement. Tout avantage d'utiliser JPA dans ce cas.

En outre, quelle sera l'instruction SQL pour appeler cette procédure stockée. Je n'ai jamais utilisé de procédures stockées auparavant et je me bats avec celui-ci. Google n'était pas d'une grande aide.

Voici la procédure stockée:

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

Mettre à jour:

Pour toute autre personne ayant des difficultés à appeler une procédure stockée à l’aide deJPA.

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

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

Les choses que j'ai remarquées:

  1. Les noms de paramètre ne fonctionnaient pas pour moi, essayez donc d'utiliser l'index de paramètre.
  2. Corriger l'instruction {call sp_name(?,?)} au lieu de call sp_name(?,?)
  3. Si la procédure stockée renvoie un jeu de résultats, même si vous savez qu'il n'y a qu'une seule ligne, getSingleResult ne fonctionnera pas.
  4. Transmettez un nom resultSetMapping ou les détails de la classe de résultats
85
user431514

JPA 2.1 prend désormais en charge la procédure stockée, lisez le document Java here .

Exemple:

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");

Voir exemple détaillé ici .

49
Pau Kiat Wee

Je déploie mon application Web dans Jboss AS. Devrais-je utiliser JPA pour accéder à la procédure stockée ou à CallableStatement. Tout avantage d'utiliser JPA dans ce cas.

Ce n'est pas vraiment supporté par JPA mais c'est faisable . Pourtant, je ne voudrais pas aller de cette façon: 

  • utiliser JPA pour mapper le résultat d'un appel de procédure stockée dans certains beans est vraiment excessif,
  • d'autant plus que JPA n'est pas vraiment approprié pour appeler une procédure stockée (la syntaxe sera assez détaillée).

Je préférerais donc utiliser le support Spring pour l’accès aux données JDBC , ou un mappeur de données tel que MyBatis ou, étant donné la simplicité de votre application, les formats JDBC brut et CallableStatement. En fait, JDBC serait probablement mon choix. Voici un exemple de base de coup d'envoi: 

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

Référence

21
Pascal Thivent

Vous devez transmettre les paramètres à la procédure stockée.

Cela devrait fonctionner comme ceci:

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

Mettre à jour:

Ou peut-être qu'il ne devrait pas.

Dans le livre EJB3 in Action , il est indiqué à la page 383 que JPA ne prend pas en charge les procédures stockées (page est uniquement un aperçu, vous ne recevez pas le texte intégral, le livre entier est disponible en un téléchargement à plusieurs endroits, y compris celui-ci , je ne sais pas si cela est légal). 

Quoi qu'il en soit, le texte est le suivant:

Procédures stockées JPA et base de données

Si vous êtes un grand fan de SQL, vous pouvez être prêt à exploiter le pouvoir de procédures stockées dans la base de données . Malheureusement, JPA ne prend pas en charge procédures stockées, et vous devez dépend d'une caractéristique exclusive de votre fournisseur de persistance. Toutefois, vous pouvez utiliser de simples fonctions mémorisées (sans paramètres out) avec un natif Requête SQL.

9
Sean Patrick Floyd

Comment récupérer un paramètre de sortie de procédure stockée à l'aide de JPA (2.0 nécessite des importations EclipseLink et 2.1 pas)

Même si cette réponse explique en détail le retour d'un jeu d'enregistrements à partir d'une procédure stockée, Je poste ici, car il m'a fallu beaucoup de temps pour le comprendre et ce fil m'a aidé.

Mon application utilisait Eclipselink-2.3.1, mais je forcerai une mise à niveau vers Eclipselink-2.5.0, car JPA 2.1 prend beaucoup mieux en charge les procédures stockées.

Utilisation d'EclipseLink-2.3.1/JPA-2.0: dépendant de l'implémentation

Cette méthode nécessite l'importation des classes EclipseLink à partir de "org.Eclipse.persistence", elle est donc spécifique à l'implémentation Eclipselink.

Je l'ai trouvé à l'adresse " http://www.yenlo.nl/fr/calling-Oracle-stored-procedures-from-eclipselink-with-multiple-out-parameters ".

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

Utilisation d'EclipseLink-2.5.0/JPA-2.1: indépendante de l'implémentation (déjà documentée dans ce fil)

Cette méthode est indépendante de l'implémentation (ne nécessite pas d'importations Eclipslink).

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
Malcolm Boekhoff

Si vous utilisez EclipseLink, vous pouvez utiliser @NamedStoredProcedureQuery ou StoreProcedureCall pour exécuter toute procédure stockée, y compris celles avec des paramètres de sortie ou des curseurs de sortie. La prise en charge des fonctions stockées et des types de données PLSQL est également disponible.

Voir, http://fr.wikibooks.org/wiki/Java_Persistence/Advanced_Topics#Stored_Procedures

6
James

Pour moi, seuls les logiciels suivants ont fonctionné avec Oracle 11g et Glassfish 2.1 (Toplink):

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

La variante avec des accolades a abouti à ORA-00900.

6
Dmitry Chornyi

Ce qui suit fonctionne pour moi:

Query query = em.createNativeQuery("BEGIN VALIDACIONES_QPAI.RECALC_COMP_Assembly('X','X','X',0); END;");
query.executeUpdate();
3
roberto quevedo
  1. Pour une procédure stockée simple utilisant des paramètres IN/OUT comme ceci

    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;
    

    Vous pouvez l'appeler depuis JPA comme suit:

    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. Pour une procédure stockée qui utilise un paramètre SYS_REFCURSOR OUT:

    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;
    

    Vous pouvez l'appeler comme suit:

    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. Pour une fonction SQL qui se présente comme suit:

    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;
    

    Vous pouvez l'appeler comme ça:

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

    Du moins lors de l’utilisation de Hibernate 4.x et 5.x, car JPA StoredProcedureQuery ne fonctionne pas pour SQL FUNCTIONS.

Pour plus d'informations sur l'appel des procédures stockées et des fonctions lors de l'utilisation de JPA et Hibernate, consultez les articles suivants.

3
Vlad Mihalcea

Cela a fonctionné pour moi.

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

Remarque: à l'avenir, si vous décidez d'utiliser la version par défaut de findOne, commentez simplement l'annotation NamedNativeQueries et JPA passera à la version par défaut.

2
Amit

Ce n'est peut-être pas la même chose pour Sql Srver, mais pour les utilisateurs d'Oracle et d'eclipslink, cela fonctionne pour moi. 

ex: une procédure qui a un paramètre IN (type CHAR) et deux paramètres OUT (NUMBER & VARCHAR)

dans le fichier persistence.xml, déclarez l'unité de persistance:

<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>

et déclarer la structure du proc dans 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>

dans le code, il vous suffit d'appeler votre proc comme suit:

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);
    }

pour obtenir les deux paramètres de sortie:

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

Cette réponse pourrait être utile si vous avez un gestionnaire d'entités

J'avais une procédure stockée pour créer le numéro suivant et côté serveur, j'ai un framework de couture.

Côté client

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

Côté base de données (serveur SQL) J'ai une procédure stockée nommée getNextNmber

1
tosha Shah

JPA 2.0 ne prend pas en charge les valeurs RETURN, mais uniquement les appels.

Ma solution était. Créez une PROCEDURE appelant une PROCEDURE.

Ainsi, dans le code Java, vous exécutez une requête NATIVE QUERY en appelant Oracle FUNCTION.

1
Musashi Miyamoto

À partir de JPA 2.1, JPA prend en charge l'appel des procédures stockées à l'aide de la méthode dynamique StoredProcedureQuery et de la déclaration @NamedStoredProcedureQuery.

0
H.Ostwal

Essayez ce code:

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

Vous pouvez utiliser @Query(value = "{call PROC_TEST()}", nativeQuery = true) dans votre référentiel. Cela a fonctionné pour moi.

Attention: utilisez '{' et '}' sinon cela ne fonctionnera pas.

0
Everton Oliveira

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();
    }
0

Pour appeler une procédure stockée, nous pouvons utiliser Callable Statement dans le package Java.sql.

0
vinoth