Java ve JPA'dan saklı yordam nasıl çağırılır


96

Depolanan bir prosedürü çağırmak ve bazı verileri almak için basit bir web uygulaması yazıyorum. Müşterinin veritabanıyla etkileşime giren çok basit bir uygulamadır. Çalışan kimliğini ve şirket kimliğini iletiriz ve depolanan prosedür çalışan ayrıntılarını döndürür.

Web uygulaması verileri güncelleyemiyor / silemiyor ve SQL Server kullanıyor.

Web uygulamamı Jboss AS'de kuruyorum. Depolanan yordama erişmek için JPA kullanmalı mıyım veya CallableStatement. Bu durumda JPA kullanmanın herhangi bir avantajı.

Ayrıca bu saklı yordamı adlandırmak için sql ifadesi ne olacaktır. Daha önce hiç depolanmış prosedür kullanmadım ve bununla mücadele ediyorum. Google pek yardımcı olmadı.

İşte saklı yordam:

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

Güncelleme:

JPA kullanarak saklı yordamı çağırmada sorun yaşayan herkes için .

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

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

Fark ettiğim şeyler:

  1. Parametre adları benim için çalışmadı, bu yüzden parametre dizinini kullanmayı deneyin.
  2. Doğru sql deyimi {call sp_name(?,?)}yerine call sp_name(?,?)
  3. Saklı yordam bir sonuç kümesi döndürüyorsa, yalnızca bir satır olduğunu bilseniz bile getSingleResultçalışmaz
  4. Bir resultSetMappingad veya sonuç sınıfı ayrıntılarını iletin

2
Yerel sorgularda adlandırılmış parametreleri kullanamazsınız . Adlandırılmış parametreler yalnızca JPQL sorguları için desteklenir. (Adlandırılmış parametreleri tercih ederseniz, adı numaralı parametrelere çevirmek için kendi sınıfınızı yazabilirsiniz.)
Viliam Búr

CreateNativeQueries ile her zaman adlandırılmış parametreleri kullandım ve hiç sorun yaşamadım. Şu an çalıştığım sisteme bir baktım ve adlandırılmış parametrelere sahip tonlarca yerel sorgu var. Onayınız için bize biraz referans verebilir misiniz? Setimiz JPA 2 ve Hibernate 4+.
Jaumzera

Yanıtlar:


59

JPA 2.1 artık Depolanan Prosedürü destekliyor, Java belgesini buradan okuyun .

Misal:

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

Burada ayrıntılı örneğe bakın .


23

Web uygulamamı Jboss AS'de kuruyorum. Depolanan yordama veya CallableStatement'a erişmek için JPA kullanmalı mıyım? Bu durumda JPA kullanmanın herhangi bir avantajı.

JPA tarafından gerçekten desteklenmiyor ama yapılabilir . Yine de bu şekilde gitmezdim:

  • Sadece bazı çekirdeklerde saklı yordam çağrısının sonucunu eşlemek için JPA kullanmak gerçekten aşırı
  • özellikle JPA'nın saklı yordamı çağırmak için gerçekten uygun olmadığı düşünüldüğünde (sözdizimi oldukça ayrıntılı olacaktır).

Ben böylece yerine kullanmayı düşünün JDBC veri erişimi için Bahar desteği ya da benzeri bir veri eşleyicisinde MyBatis başvurunuzun basitlik, ham JDBC ve verilen veya CallableStatement. Aslında, JDBC muhtemelen benim seçimim olacaktır. İşte temel bir başlangıç ​​örneği:

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

Referans


Belirtildiği gibi aşağıda cevabını o desteklenir - Eğer düzenlemek isteyebilirsiniz
Mr_and_Mrs_D

11

Parametreleri saklı yordama geçirmeniz gerekir.

Şu şekilde çalışmalıdır:

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

Güncelleme:

Belki de olmamalı.

EJB3 Kitabı Çalışırken, 383. sayfada, JPA'nın depolanan prosedürleri desteklemediği yazıyor (sayfa yalnızca bir önizlemedir, tam metni alamazsınız, kitabın tamamı bu dahil birçok yerden indirilebilir , Bunun yasal olup olmadığını bilmiyorum).

Her neyse, metin şu:

JPA ve veritabanında depolanan prosedürler

Büyük bir SQL hayranıysanız, veritabanında depolanan prosedürlerin gücünden yararlanmaya istekli olabilirsiniz. Ne yazık ki, JPA depolanan prosedürleri desteklemez ve kalıcılık sağlayıcınızın tescilli bir özelliğine güvenmeniz gerekir. Ancak, yerel bir SQL sorgusu ile basit depolanan işlevleri (parametreler olmadan) kullanabilirsiniz.


Denedim ve şu hata mesajını alıyorum: java.sql.SQLException: '@ P0' yakınında yanlış sözdizimi.
user431514

3
"{GetEmployeeDetails (: EmployeeDetails (: EmployeeDetails:: companyId) çağrısı)}" olmalıdır, SQL sunucusu için kaşlı ayraç olması gerekir.
Vedran

@Vedran doğru. Sadece parametre ayarlama kısmıyla ilgileniyordum
Sean Patrick Floyd

9

Depolanan Prosedür çıktı parametresi JPA kullanılarak nasıl alınır (2.0, EclipseLink içe aktarmaya ihtiyaç duyar ve 2.1'de yoktur)

Bu yanıt, bir kayıt kümesini depolanan bir yordamdan geri döndürme konusunda ayrıntılı bilgi verse de, buraya gönderiyorum, çünkü anlamam yıllar aldı ve bu iş parçacığı bana yardımcı oldu.

Uygulamam Eclipselink-2.3.1 kullanıyordu, ancak JPA 2.1 depolanan prosedürler için çok daha iyi desteğe sahip olduğundan Eclipselink-2.5.0'a yükseltmeye zorlayacağım.

EclipseLink-2.3.1 / JPA-2.0 Kullanımı: Uygulamaya Bağlı

Bu yöntem, EclipseLink sınıflarının "org.eclipse.persistence" dan içe aktarılmasını gerektirir, bu nedenle Eclipselink uygulamasına özgüdür.

Bunu " http://www.yenlo.nl/en/calling-oracle-stored-procedures-from-eclipselink-with-multiple-out-parameters " adresinde buldum .

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

EclipseLink-2.5.0 / JPA-2.1 Kullanımı: Uygulamadan Bağımsız (bu ileti dizisinde zaten belgelenmiştir)

Bu yöntem uygulamadan bağımsızdır (Eclipslink içe aktarmalarına gerek yoktur).

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

8
Aah, gözlerim ağrıyor. Bu gerçekten JDBC'den çok daha iyi değil, değil mi?
Lukas Eder

Haha, evet nokta alındı. Bununla birlikte, bunları kullanmanın yararı, veri nesnesi sınıfını almak için bir kod yükü yazmanız gerekmemesi ve kayıt kümesinden tüm verileri veri sınıfınıza aktardığınız biti yapmanız gerekmemesidir. . Hala bir veri nesnesi (Varlık) vardır, ancak Eclipse sihirbazı bunu sizin için oluşturur.
Malcolm Boekhoff

1
Evet yapabilirdin. Ama bunu her şeyin üretildiği jOOQ'un geliştiricisi olarak söylüyorum . Yapılması gereken tek şey prosedürü / işlevi gerçekten çağırmaktır.
Lukas Eder

Gerçekten alttaki örneği denediniz mi (uygulamadan bağımsız)? Prosedürün bir xmldosyada tanımlanmış olması ve çalışmaması farkıyla denedim . OUTParametreyi okuyamıyorum .
Roland

Her nasılsa JPA - 2.1 Uygulaması için, Adlandırılmış Parametreler benim için çalışmıyor. Bunun yerine, Depolanan Prosedürlerdeki Konum İndeksini geçmek zorunda kaldım ve Çıktı Parametresinin sonucunu başarıyla alabildim. Bu, Çoklu Sonuç Kümelerini Geri Döndüren Prosedürü Sakladığım zamandı. 1 Sonuç Seti için sadece @Query
Radhesh Khanna'yı

7
  1. Bunun gibi GİRİŞ / ÇIKIŞ parametrelerini kullanan basit bir saklı yordam için

    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;
    

    JPA'dan şu şekilde çağırabilirsiniz:

    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. SYS_REFCURSOROUT parametresi kullanan bir saklı yordam için :

    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;
    

    Bunu şu şekilde arayabilirsiniz:

    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. Aşağıdaki gibi görünen bir SQL işlevi için:

    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;
    

    Şöyle diyebilirsiniz:

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

    En azından Hibernate 4.x ve 5.x kullanırken, çünkü JPA StoredProcedureQuerySQL FONKSİYONLARI için çalışmıyor.

JPA ve Hazırda Bekletme kullanılırken saklı yordamların ve işlevlerin nasıl çağrılacağı hakkında daha fazla ayrıntı için aşağıdaki makalelere göz atın


"Çağrıda yanlış numara veya değişken türleri ..." hata mesajı almaya devam ettim. Aradığımı fark ettim createNativeQuery. Geçiş yaptım createStoredProcedureQuery. Sonra, voila!
Ahmet

6

Benim için yalnızca aşağıdakiler Oracle 11g ve Glassfish 2.1 (Toplink) ile çalıştı:

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

Küme parantezli varyant ORA-00900 ile sonuçlandı.


1
Benim için Oracle 11g üzerinde çalışıyor, JPA sağlayıcısını hazırda beklet.
David Mann

1
Bu bizi son derece büyük bir sorundan kurtardı. Java6, oracle11g, Jboss6, Hibernate kullanıyorduk. Teşekkürler @Chornyi.
Abdullah Khan



2

Sql Srver için aynı olmayabilir ama oracle ve eclipslink kullanan insanlar için benim için çalışıyor

örn: bir IN parametresi (tip CHAR) ve iki OUT parametresi (NUMBER & VARCHAR) olan bir prosedür

Kalıcılık.xml dosyasında kalıcılık birimini bildirin:

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

ve eclipselink-orm.xml dosyasında proc yapısını bildiriniz

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

kodda, proc'unuzu şu şekilde aramanız yeterlidir:

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

iki çıktı parametresini almak için:

Integer myInt = (Integer) resultQuery[0];
String myStr =  (String) resultQuery[1];

2

Bu benim için çalıştı.

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

Not: Gelecekte, findOne'ın varsayılan sürümünü kullanmaya karar verirseniz, NamedNativeQueries açıklamasını yorumlamanız yeterlidir; JPA varsayılana geçecektir.


Belirli bir paket dahilindeki prosedürü aramak istersem, şu şekilde aramalıyım: {paket}. {Prosedür}?
Raju yourPepe

1

Varlık yöneticiniz varsa bu cevap yardımcı olabilir

Bir sonraki numarayı oluşturmak için saklı bir prosedürüm vardı ve sunucu tarafında dikiş çerçevem ​​var.

İstemci tarafı

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

Veritabanı Tarafı (SQL sunucusu) adlı saklı yordam var getNextNmber


executeUpdate () dönüş int. Bir sproc çıkışı aldığınızdan emin misiniz?
Constantine Gladky

1

@Query(value = "{call PROC_TEST()}", nativeQuery = true)Deponuzda kullanabilirsiniz . Bu benim için çalıştı.

Dikkat: '{' ve '}' kullanın, yoksa işe yaramaz.


1

JPA 2.0, RETURN değerlerini desteklemez, yalnızca çağrıları destekler.

Benim çözümüm oldu. PROSEDÜR'ü çağıran bir FUNCTION oluşturun.

Dolayısıyla, JAVA kodunun içinde oracle FUNCTION'ı çağıran bir NATIVE QUERY çalıştırırsınız.


0

Depolanan yordamı çağırmak için java.sql paketinde Çağrılabilir İfadeyi kullanabiliriz.


Cevabın için teşekkürler. Yani çağrılabilir ifade için sql {? = getEmployeeDetails (?,?)} çağrısı yapın veya tüm çıktı parametrelerini belirtmeniz gerekir
user431514

0

Bu kodu deneyin:

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

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

4
lütfen yanıtınıza herhangi bir yorum eklemekten çekinmeyin (salt kod dışında).
ivan.mylyanyk

0

JPA 2.1'den itibaren, JPA, dinamik StoredProcedureQuery ve bildirim temelli @NamedStoredProcedureQuery kullanarak saklı yordamları çağırmayı destekler.


-2

Benim çözümüm oldu. PROSEDÜR'ü çağıran bir FUNCTION oluşturun.

Dolayısıyla, JAVA kodunun içinde oracle FUNCTION'ı çağıran bir NATIVE QUERY çalıştırırsınız.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.