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:
{call sp_name(?,?)}
au lieu de call sp_name(?,?)
getSingleResult
ne fonctionnera pas.resultSetMapping
ou les détails de la classe de résultatsJPA 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 .
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:
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();
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:
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.
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.
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
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"));
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
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.
Ce qui suit fonctionne pour moi:
Query query = em.createNativeQuery("BEGIN VALIDACIONES_QPAI.RECALC_COMP_Assembly('X','X','X',0); END;");
query.executeUpdate();
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);
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();
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.
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.
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];
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
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.
À 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.
Essayez ce code:
return em.createNativeQuery("{call getEmployeeDetails(?,?)}",
EmployeeDetails.class)
.setParameter(1, employeeId)
.setParameter(2, companyId).getResultList();
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.
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();
}
Pour appeler une procédure stockée, nous pouvons utiliser Callable Statement dans le package Java.sql.