Bagaimana memanggil prosedur yang tersimpan dari Java dan JPA


95

Saya menulis aplikasi web sederhana untuk memanggil prosedur tersimpan dan mengambil beberapa data. Ini adalah aplikasi yang sangat sederhana, yang berinteraksi dengan database klien. Kami memberikan id karyawan dan id perusahaan dan prosedur yang tersimpan akan mengembalikan detail karyawan.

Aplikasi web tidak dapat memperbarui / menghapus data dan menggunakan SQL Server.

Saya menerapkan aplikasi web saya di Jboss AS. Haruskah saya menggunakan JPA untuk mengakses prosedur tersimpan atau CallableStatement. Keuntungan menggunakan JPA dalam hal ini.

Juga apa yang akan menjadi pernyataan sql untuk memanggil prosedur tersimpan ini. Saya belum pernah menggunakan prosedur tersimpan sebelumnya dan saya berjuang dengan yang satu ini. Google tidak banyak membantu.

Berikut adalah prosedur yang tersimpan:

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

Memperbarui:

Untuk orang lain yang mengalami masalah memanggil prosedur tersimpan menggunakan JPA .

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

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

Hal-hal yang saya perhatikan:

  1. Nama parameter tidak berfungsi untuk saya, jadi coba gunakan indeks parameter.
  2. Pernyataan sql yang benar, {call sp_name(?,?)}bukan call sp_name(?,?)
  3. Jika prosedur tersimpan mengembalikan set hasil, bahkan jika Anda tahu hanya dengan satu baris, getSingleResulttidak akan berfungsi
  4. Berikan resultSetMappingnama atau detail kelas hasil

2
Anda tidak dapat menggunakan parameter bernama dalam kueri asli . Parameter bernama hanya didukung untuk kueri JPQL. (Jika Anda lebih suka parameter bernama, Anda dapat menulis kelas Anda sendiri untuk menerjemahkan nama ke parameter bernomor.)
Viliam Búr

Saya selalu menggunakan parameter bernama dengan createNativeQueries dan tidak pernah punya masalah. Saya baru saja melihat sistem saat ini yang telah saya kerjakan dan ada banyak kueri asli dengan parameter bernama. Bisakah Anda memberi kami beberapa referensi untuk penegasan Anda? Set kami adalah JPA 2 dan Hibernate 4+.
Jaumzera

Jawaban:


59

JPA 2.1 sekarang mendukung Prosedur Tersimpan, baca dokumen Java di sini .

Contoh:

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

Lihat contoh detailnya di sini .


23

Saya menerapkan aplikasi web saya di Jboss AS. Haruskah saya menggunakan JPA untuk mengakses prosedur tersimpan atau CallableStatement. Keuntungan menggunakan JPA dalam hal ini.

Ini tidak benar-benar didukung oleh JPA tetapi dapat dilakukan . Tetap saja saya tidak akan pergi seperti ini:

  • menggunakan JPA hanya untuk memetakan hasil dari panggilan prosedur tersimpan di beberapa kacang benar-benar berlebihan,
  • terutama mengingat JPA tidak benar-benar sesuai untuk memanggil prosedur yang tersimpan (sintaksnya akan sangat bertele-tele).

Oleh karena itu, saya lebih suka mempertimbangkan untuk menggunakan dukungan Spring untuk akses data JDBC , atau pemeta data seperti MyBatis atau, mengingat kesederhanaan aplikasi Anda, JDBC mentah dan CallableStatement. Sebenarnya, JDBC mungkin akan menjadi pilihan saya. Berikut adalah contoh awal pembukaan:

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

Referensi


Seperti yang dinyatakan dalam jawaban di bawah ini didukung - Anda mungkin ingin mengedit
Mr_and_Mrs_D

10

Anda harus meneruskan parameter ke prosedur tersimpan.

Ini harus bekerja seperti ini:

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

Memperbarui:

Atau mungkin seharusnya tidak.

Dalam Buku EJB3 Beraksi , disebutkan di halaman 383, bahwa JPA tidak mendukung prosedur tersimpan (halaman hanya pratinjau, Anda tidak mendapatkan teks lengkap, seluruh buku tersedia sebagai unduhan di beberapa tempat termasuk yang ini , Saya tidak tahu apakah ini legal).

Bagaimanapun, teksnya adalah ini:

JPA dan database yang disimpan prosedur

Jika Anda adalah penggemar SQL, Anda mungkin ingin memanfaatkan kekuatan database yang tersimpan prosedur. Sayangnya, JPA tidak mendukung prosedur tersimpan, dan Anda harus bergantung pada fitur kepemilikan dari penyedia persistensi Anda. Namun, Anda dapat menggunakan fungsi simpanan sederhana (tanpa parameter) dengan kueri SQL asli.


Saya mencoba dan mendapatkan pesan kesalahan ini: java.sql.SQLException: Sintaks salah di dekat '@ P0'.
pengguna431514

3
Seharusnya "{call getEmployeeDetails (: employeeId,: companyId)}", untuk SQL server harus memiliki kurung kurawal.
Vedran

@Tokopedia Saya hanya tertarik pada bagian pengaturan parameter
Sean Patrick Floyd

9

Cara mengambil parameter keluaran Prosedur Tersimpan menggunakan JPA (2.0 membutuhkan impor EclipseLink dan 2.1 tidak)

Meskipun jawaban ini menguraikan tentang mengembalikan kumpulan data dari prosedur yang tersimpan, saya memposting di sini, karena butuh waktu lama untuk mengetahuinya dan utas ini membantu saya.

Aplikasi saya menggunakan Eclipselink-2.3.1, tetapi saya akan memaksakan peningkatan ke Eclipselink-2.5.0, karena JPA 2.1 memiliki dukungan yang jauh lebih baik untuk prosedur tersimpan.

Menggunakan EclipseLink-2.3.1 / JPA-2.0: Implementasi-Tergantung

Metode ini memerlukan impor kelas EclipseLink dari "org.eclipse.persistence", jadi ini khusus untuk implementasi Eclipselink.

Saya menemukannya di " 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

Menggunakan EclipseLink-2.5.0 / JPA-2.1: Implementasi-Independen (sudah didokumentasikan di utas ini)

Metode ini implementasi independen (tidak perlu impor 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"));

8
Aah, mataku sakit. Ini tidak jauh lebih baik dari JDBC, bukan?
Lukas Eder

Haha, ya sudah diambil poinnya. Namun manfaat menggunakan hal-hal ini adalah Anda tidak perlu mengetikkan beban kode untuk mendapatkan kelas objek data dan Anda tidak perlu melakukan sedikit pun di mana Anda mentransfer semua data dari recordSet ke kelas data Anda. . Masih ada objek data (Entity), tetapi wizard Eclipse membuatnya untuk Anda.
Malcolm Boekhoff

1
Ya, Anda bisa. Tapi saya mengatakan ini sebagai pengembang jOOQ , di mana semuanya dihasilkan. Satu-satunya hal yang harus dilakukan adalah benar-benar memanggil prosedur / fungsi.
Lukas Eder

Apakah Anda benar-benar mencoba contoh bawah (implementasi independen)? Saya mencobanya dengan perbedaan bahwa prosedur ditentukan dalam sebuah xmlfile dan tidak berhasil. Saya tidak bisa membaca OUTparameter.
Roland

Entah bagaimana untuk Implementasi JPA - 2.1, Parameter Bernama tidak berfungsi untuk saya. Sebagai gantinya, saya harus meneruskan Indeks Posisi mereka dalam Prosedur Tersimpan dan berhasil mendapatkan hasil untuk Parameter Output. Ini adalah kasus ketika saya memiliki Prosedur Tersimpan yang Mengembalikan Beberapa Kumpulan Hasil. Untuk 1 ResultSet, saya cukup menggunakan @Query
Radhesh Khanna

7
  1. Untuk prosedur tersimpan sederhana yang menggunakan parameter IN / OUT seperti ini

    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;

    Anda dapat memanggilnya dari JPA sebagai berikut:

    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. Untuk prosedur tersimpan yang menggunakan SYS_REFCURSORparameter 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;

    Anda bisa menyebutnya sebagai berikut:

    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. Untuk fungsi SQL yang terlihat seperti berikut:

    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;

    Anda bisa menyebutnya seperti ini:

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

    Setidaknya saat menggunakan Hibernate 4.x dan 5.x karena JPA StoredProcedureQuerytidak berfungsi untuk SQL FUNCTIONS.

Untuk detail lebih lanjut tentang cara memanggil prosedur dan fungsi yang disimpan saat menggunakan JPA dan Hibernate, lihat artikel berikut


Saya terus mendapatkan pesan kesalahan "nomor atau jenis argumen yang salah dalam panggilan ke ...". Saya menyadari bahwa saya menelepon createNativeQuery. Saya beralih ke createStoredProcedureQuery. Kalau begitu, voila!
Ahmet

6

Bagi saya, hanya yang berikut ini yang bekerja dengan Oracle 11g dan Glassfish 2.1 (Toplink):

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

Varian dengan kawat gigi keriting menghasilkan ORA-00900.


1
Bekerja untuk saya di Oracle 11g, penyedia JPA hibernasi.
David Mann

1
Ini membuat kami keluar dari masalah yang sangat besar. Kami menggunakan java6, oracle11g, Jboss6, Hibernate. Terima kasih @Chornyi.
Abdullah Khan



2

Mungkin tidak sama untuk Sql Srver tetapi untuk orang yang menggunakan oracle dan eclipslink, ini berfungsi untuk saya

contoh: prosedur yang memiliki satu parameter IN (tipe CHAR) dan dua parameter OUT (NOMOR & VARCHAR)

di persistence.xml nyatakan unit-persistence:

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

dan mendeklarasikan struktur proc di 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>

dalam kode Anda hanya perlu memanggil proc Anda seperti ini:

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

untuk mendapatkan dua parameter keluaran:

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

2

Ini berhasil untuk saya.

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

Catatan: di masa mendatang jika Anda memutuskan untuk menggunakan versi default findOne, cukup beri komentar pada anotasi NamedNativeQueries dan JPA akan beralih ke default


Jika saya ingin memanggil prosedur dalam paket tertentu, haruskah saya memanggil dengan cara ini: panggil {package}. {Procedure}?
Raju yourPepe

1

Jawaban ini mungkin berguna jika Anda memiliki manajer entitas

Saya memiliki prosedur tersimpan untuk membuat nomor berikutnya dan di sisi server saya memiliki kerangka kerja jahitan.

Sisi klien

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

Database Side (SQL server) Saya memiliki prosedur tersimpan bernama getNextNmber


executeUpdate () return int. Apakah Anda yakin bahwa Anda menerima keluaran dari sproc?
Constantine Gladky

1

Kamu bisa memakai @Query(value = "{call PROC_TEST()}", nativeQuery = true) di repositori Anda. Ini berhasil untuk saya.

Perhatian: gunakan '{' dan '}' atau tidak akan berhasil.


1

JPA 2.0 tidak mendukung nilai RETURN, hanya panggilan.

Solusi saya adalah. Buat FUNCTION memanggil PROCEDURE.

Jadi, di dalam kode JAVA Anda mengeksekusi NATIVE QUERY yang memanggil oracle FUNCTION.


0

Untuk memanggil stored procedure kita bisa menggunakan Callable Statement dalam paket java.sql.


Terima kasih untuk balasan Anda. Jadi sql untuk pernyataan callable akan menjadi {? = panggil getEmployeeDetails (?,?)} atau perlu menentukan semua parameter keluaran
user431514

0

Coba kode ini:

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
tolong, jangan ragu untuk menambahkan komentar ke jawaban Anda (selain kode murni).
ivan.mylyanyk

0

Dari JPA 2.1, JPA mendukung untuk memanggil prosedur yang tersimpan menggunakan StoredProcedureQuery dinamis, dan deklaratif @NamedStoredProcedureQuery.


-2

Solusi saya adalah. Buat FUNCTION memanggil PROCEDURE.

Jadi, di dalam kode JAVA Anda mengeksekusi NATIVE QUERY yang memanggil oracle FUNCTION.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.