101 votes

Comment appeler une procédure stockée depuis Java et JPA

J'écris une application web simple 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 passons l'identifiant de l'employé et l'identifiant de la société et la procédure stockée renvoie les détails de l'employé.

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

Je déploie mon application web dans Jboss AS. Dois-je utiliser JPA pour accéder à la procédure stockée ou bien CallableStatement . Y a-t-il un avantage à utiliser JPA dans ce cas ?

Aussi, quelle sera l'instruction sql pour appeler cette procédure stockée. Je n'ai jamais utilisé de procédures stockées auparavant et j'ai du mal avec celle-ci. Google ne m'a pas beaucoup aidé.

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

Mise à jour :

Si quelqu'un d'autre a des problèmes pour appeler une procédure stockée en utilisant JPA .

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

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

Des choses que j'ai remarquées :

  1. Les noms de paramètres n'ont pas fonctionné pour moi, alors essayez d'utiliser l'index des paramètres.
  2. Instruction sql correcte {call sp_name(?,?)} au lieu de call sp_name(?,?)
  3. Si la procédure stockée renvoie un ensemble de résultats, même si vous ne connaissez qu'une seule ligne, getSingleResult ne fonctionne pas
  4. Passez un resultSetMapping nom ou détails de la classe de résultats

2 votes

Vous ne pouvez pas utiliser de paramètres nommés dans indigène requêtes. Les paramètres nommés ne sont supportés que pour les requêtes JPQL. (Si vous préférez les paramètres nommés, vous pouvez écrire votre propre classe pour traduire les paramètres nommés en paramètres numérotés).

0 votes

J'ai toujours utilisé des paramètres nommés avec createNativeQueries et je n'ai jamais eu de problème. Je viens de jeter un coup d'œil au système actuel sur lequel je travaille et il y a des tonnes de requêtes natives avec des paramètres nommés. Pouvez-vous nous fournir des références pour étayer votre affirmation ? Notre ensemble est JPA 2 et Hibernate 4+.

64voto

Pau Kiat Wee Points 5008

JPA 2.1 supporte maintenant les procédures stockées, lisez la doc Java ici .

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 l'exemple détaillé ici .

23voto

Pascal Thivent Points 295221

Je déploie mon application web dans Jboss AS. Dois-je utiliser JPA pour accéder à la procédure stockée ou à CallableStatement ? Y a-t-il un avantage à utiliser JPA dans ce cas ?

Il n'est pas vraiment pris en charge par JPA, mais il est réalisable . Je n'irais quand même pas par là :

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

Je préférerais donc envisager d'utiliser Support Spring pour l'accès aux données JDBC ou un mappeur de données comme MyBatis ou, compte tenu de la simplicité de votre application, JDBC brut et CallableStatement . En fait, JDBC serait probablement mon choix. Voici un exemple de démarrage de base :

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

Référence

0 votes

Comme indiqué dans le réponse ci-dessous il est pris en charge - vous pourriez vouloir modifier

11voto

Sean Patrick Floyd Points 109428

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

Cela devrait fonctionner comme suit :

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

Mise à jour :

Ou peut-être que ça ne devrait pas.

Dans le livre EJB3 en action il est dit à la page 383, que JPA ne prend pas en charge les procédures stockées (la page n'est qu'un aperçu, vous n'obtenez pas le texte complet, le livre entier est disponible en téléchargement à plusieurs endroits, notamment celui-ci Je ne sais pas si c'est légal).

Bref, le texte est le suivant :

JPA et procédures stockées dans la base de données

Si vous êtes un grand fan de SQL, vous pouvez être désireux d'exploiter la puissance des procédures stockées dans les bases de données. Malheureusement, JPA ne supporte pas procédures stockées, et vous devez dépendre d'une fonctionnalité propriétaire de votre fournisseur de persistance. Cependant, vous pouvez utiliser des fonctions stockées simples (sans paramètres de sortie) avec une requête SQL native.

0 votes

J'ai essayé et j'ai obtenu ce message d'erreur : java.sql.SQLException : Syntaxe incorrecte près de '@P0'.

3 votes

Il devrait être "{call getEmployeeDetails(:employeeId,:companyId)}", pour SQL server il doit avoir des accolades.

0 votes

@Vedran vrai. Je n'étais intéressé que par la partie paramétrage

9voto

Malcolm Boekhoff Points 141

Comment récupérer le paramètre de sortie d'une procédure stockée à l'aide de JPA (la version 2.0 nécessite des importations EclipseLink et la version 2.1 non) ?

Même si cette réponse traite du retour d'un jeu d'enregistrements à partir d'une procédure stockée, Je poste ici, car cela m'a pris des années à comprendre et ce fil de discussion m'a aidé.

Mon application utilisait Eclipselink-2.3.1, mais je vais forcer une mise à jour vers Eclipselink-2.5.0. Eclipselink-2.5.0, car JPA 2.1 offre un bien meilleur support pour 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 de "org.eclipse.persistence", elle est donc spécifique à l'implémentation d'Eclipselink.

Je l'ai trouvé à " http://www.yenlo.nl/en/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épendant de l'implémentation (déjà documenté dans ce fil de discussion)

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 votes

Aah, mes yeux me font mal. Ce n'est pas vraiment mieux que JDBC, n'est-ce pas ?

0 votes

Haha, oui, j'ai compris. Cependant, l'avantage d'utiliser ces choses est que vous n'avez pas à taper une charge de code pour obtenir la classe d'objet de données et vous n'avez pas à faire la partie où vous transférez toutes les données du recordSet dans votre classe de données. Il y a toujours un objet de données (Entity), mais l'assistant Eclipse le génère pour vous.

1 votes

Oui, vous pourriez. Mais je dis ça en tant que développeur de jOOQ où tout est généré. La seule chose qui reste à faire est d'appeler réellement la procédure/fonction.

6voto

James Points 18355

Si vous utilisez EclipseLink, vous pouvez utiliser @NamedStoredProcedureQuery ou StoreProcedureCall pour exécuter n'importe quelle procédure stockée, y compris celles avec des paramètres de sortie, ou des curseurs de sortie. Le support des fonctions stockées et des types de données PLSQL est également disponible.

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

1 votes

Quelle version d'EclipseLink possède la fonction EntityManager.createNamedStoredProcedureQuery() ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X