JavaおよびJPAからストアード・プロシージャーを呼び出す方法


94

ストアドプロシージャを呼び出してデータを取得する簡単なWebアプリケーションを作成しています。その非常にシンプルなアプリケーションは、クライアントのデータベースと対話します。従業員IDと会社IDを渡すと、ストアドプロシージャが従業員の詳細を返します。

Webアプリケーションはデータを更新/削除できず、SQL Serverを使用しています。

WebアプリケーションをJboss ASにデプロイしています。JPAを使用してストアドプロシージャまたはにアクセスする必要がありますかCallableStatement。この場合にJPAを使用する利点。

また、このストアドプロシージャを呼び出すためのSQLステートメントは何ですか。これまでにストアドプロシージャを使用したことがなく、これに苦労しています。グーグルはあまり役に立たなかった。

これがストアドプロシージャです。

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

更新:

JPAを使用してストアドプロシージャを呼び出すときに問題が発生している他のユーザー向け

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

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

私が気付いたこと:

  1. パラメータ名が機能しなかったので、パラメータインデックスを使用してみてください。
  2. {call sp_name(?,?)}代わりにSQLステートメントを 修正するcall sp_name(?,?)
  3. ストアドプロシージャは、あなたが一列のみで知っていても、結果セットを返す場合は、getSingleResult文句を言わない仕事
  4. resultSetMapping名前または結果クラスの詳細を渡す

2
名前付きパラメーターをネイティブクエリで使用することはできません。名前付きパラメーターは、JPQLクエリでのみサポートされます。(名前付きパラメーターを使用する場合は、独自のクラスを作成して、名前付きパラメーターを番号付きパラメーターに変換できます。)
ViliamBúrJan

私は常にcreateNativeQueriesで名前付きパラメーターを使用しており、問題はありませんでした。私が今取り組んでいる現在のシステムを調べたところ、名前付きパラメーターを使用した多数のネイティブクエリがあります。確認のための参考資料を教えてください。私たちのセットはJPA 2とHibernate 4+です。
Jaumzera

回答:


58

JPA 2.1がストアドプロシージャをサポートするようになりました。Javaのドキュメントはこちらをご覧ください

例:

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

詳細な例はこちらをご覧ください。


23

WebアプリケーションをJboss ASにデプロイしています。JPAを使用してストアドプロシージャまたはCallableStatementにアクセスする必要がありますか。この場合にJPAを使用する利点。

JPAは実際にはサポートしていませんが、実行可能です。それでも私はこのようには行きません:

  • 一部のBeanでストアドプロシージャコールの結果をマップするためだけにJPAを使用するのは、やり過ぎです。
  • 特に、JPAはストアドプロシージャを呼び出すのに本当に適切ではないため(構文はかなり冗長になります)。

したがって、私はむしろ、JDBCデータアクセス、またはMyBatisのようなデータマッパー、またはアプリケーションの単純さを考慮して、生のJDBCおよびのSpringサポートの使用を検討しますCallableStatement。実際、おそらくJDBCが私の選択でしょう。基本的なキックオフの例を次に示します。

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

参照


以下回答で述べられているように、それはサポートされています-あなたは編集したいかもしれません
Mr_and_Mrs_D

10

ストアドプロシージャにパラメーターを渡す必要があります。

次のように機能するはずです。

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

更新:

あるいは、そうすべきではないかもしれません。

ブックでのアクションでEJB3、そのページ383、上の言うJPAは、ストアドプロシージャをサポートしていないページはプレビューで、あなたがフルテキストを取得しない(全体ブックは、以下を含むいくつかの場所でダウンロードできます。この1、これが合法かどうかはわかりません)。

とにかく、テキストはこれです:

JPAおよびデータベースストアドプロシージャ

SQLの大ファンなら、データベースストアドプロシージャの力を活用してもかまいません。残念ながら、JPAはストアドプロシージャをサポートしていないため、永続化プロバイダーの独自機能に依存する必要があります。ただし、ネイティブSQLクエリで(outパラメーターなしの)単純なストアド関数を使用できます。


私はこのエラーメッセージを取得しようとしました:java.sql.SQLException: '@ P0'の近くの構文が正しくありません。
user431514 '26 / 08/26

3
"{call getEmployeeDetails(:employeeId、:companyId)}"である必要があります。SQLサーバーの場合は中括弧が必要です。
Vedran、2012

@Vedran true。私はパラメータ設定部分だけに興味がありました
ショーン・パトリック・フロイド

9

JPAを使用してストアドプロシージャの出力パラメーターを取得する方法(2.0ではEclipseLinkインポートが必要で、2.1では必要ありません)

この回答では、ストアドプロシージャからレコードセットを返す方法について詳しく説明していますが、ここに投稿します。これは、それを理解するのに年齢がかかり、このスレッドが役に立ったためです。

私のアプリケーションはEclipselink-2.3.1を使用していましたが、JPA 2.1はストアード・プロシージャーのサポートがはるかに優れているため、Eclipselink-2.5.0に強制的にアップグレードします。

EclipseLink-2.3.1 / JPA-2.0の使用:実装依存

この方法では、「org.eclipse.persistence」からEclipseLinkクラスをインポートする必要があるため、Eclipselink実装に固有です。

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

EclipseLink-2.5.0 / JPA-2.1の使用:実装に依存しない(すでにこのスレッドに記載されています)

このメソッドは実装に依存しません(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
ああ、目が痛い。これは実際にはJDBCよりもはるかに優れているのではないでしょうか。
Lukas Eder

はは、そうですね。ただし、これらのものを使用する利点は、データオブジェクトクラスを取得するためにコードのロードを入力する必要がなく、recordSetからデータクラスにすべてのデータを転送するビットを実行する必要がないことです。 。データオブジェクト(Entity)はまだありますが、Eclipseウィザードが生成します。
Malcolm Boekhoff 2014

1
はい、できます。しかし、私は、すべてが生成されるjOOQの開発者としてこれを言っています。あとは、プロシージャ/関数を実際に呼び出すだけです。
Lukas Eder

実際に下の例(実装に依存しない)を試しましたか?プロシージャがxmlファイルで定義されていて機能しなかったという違いで試してみました。OUTパラメータが読めません。
Roland、

6

私にとっては、Oracle 11gとGlassfish 2.1(Toplink)で動作するのは次のものだけでした。

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

中括弧が付いたバリアントは、ORA-00900になります。


1
休止状態のJPAプロバイダーであるOracle 11gで動作します。
David Mann

1
これは非常に大きなトラブルから私たちを導きました。私たちはjava6、oracle11g、Jboss6、Hibernateを使用していました。@Chornyiに感謝します。
Abdullah Khan

6

EclipseLinkを使用している場合は、@ NamedStoredProcedureQueryまたはStoreProcedureCallを使用して、出力パラメーターやアウトカーソルを含むストアドプロシージャを実行できます。ストアドファンクションおよびPLSQLデータ型のサポートも利用できます。

http://en.wikibooks.org/wiki/Java_Persistence/Advanced_Topics#Stored_Proceduresを参照してください


1
EntityManager.createNamedStoredProcedureQuery()があるEclipseLinkのバージョンはどれですか?
Mircea Ion

6
  1. このようなIN / OUTパラメータを使用する単純なストアドプロシージャの場合

    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から次のように呼び出すことができます。

    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パラメータを使用するストアドプロシージャの場合:

    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;

    次のように呼び出すことができます。

    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. 次のようなSQL関数の場合:

    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;

    次のように呼び出すことができます。

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

    JPA StoredProcedureQueryがSQL機能に対して機能しないため、少なくともHibernate 4.xおよび5.xを使用する場合。

JPAとHibernateを使用するときにストアドプロシージャと関数を呼び出す方法の詳細については、次の記事を確認してください。


「...の呼び出しで引数の数またはタイプが間違っています」というエラーメッセージが表示され続けました。と思っていたcreateNativeQuery。に切り替えましたcreateStoredProcedureQuery。その後、出来上がり!
アーメット


2

それはSql Srverと同じではないかもしれませんが、oracleとeclipslinkを使用している人々にとってはそれは私のために働いています

例:1つのINパラメータ(タイプCHAR)と2つのOUTパラメータ(NUMBER&VARCHAR)を持つプロシージャ

persistence.xmlで、persistence-unitを宣言します。

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

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>

コードでは、次のようにprocを呼び出すだけです。

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

2つの出力パラメーターを取得するには:

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

2

これでうまくいきました。

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

注:今後、findOneのデフォルトバージョンを使用する場合は、NamedNativeQueriesアノテーションをコメント化するだけで、JPAがデフォルトに切り替わります。


特定のパッケージ内でプロシージャを呼び出す場合は、次のように呼び出す必要があります:{package}。{procedure}を呼び出しますか?
Raju yourPepe

1

この回答は、エンティティマネージャーがいる場合に役立ちます

次の番号を作成するためのストアドプロシージャがあり、サーバー側にはシームフレームワークがあります。

クライアント側

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

データベースサイド(SQLサーバー)という名前のストアドプロシージャ getNextNmber


executeUpdate()はintを返します。sprocの出力を受け取っていますか?
コンスタンティングラッキー2013年

1

JPA 2.0はRETURN値をサポートせず、呼び出しのみをサポートします。

私の解決策は。PROCEDUREを呼び出すFUNCTIONを作成します。

したがって、JAVAコード内で、Oracle関数を呼び出すネイティブクエリを実行します。


0

ストアドプロシージャを呼び出すには、java.sqlパッケージのCallable Statementを使用できます。


お返事をありがとうございます。したがって、呼び出し可能ステートメントのSQLは{?= getEmployeeDetails(?、?)}を呼び出すか、すべての出力パラメーターを指定する必要があります
user431514

0

このコードを試してください:

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

0

@Query(value = "{call PROC_TEST()}", nativeQuery = true)リポジトリで使用できます。これでうまくいきました。

注意:「{」と「}」を使用しないと機能しません。


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
回答にコメントを追加することをためらわないでください(純粋なコード以外)。
ivan.mylyanyk 2015年

0

JPA 2.1以降、JPAは動的なStoredProcedureQueryと宣言的な@NamedStoredProcedureQueryを使用してストアドプロシージャを呼び出すことができます。


-2

私の解決策は。PROCEDUREを呼び出すFUNCTIONを作成します。

したがって、JAVAコード内で、Oracle関数を呼び出すネイティブクエリを実行します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.