Java 및 JPA에서 저장 프로 시저를 호출하는 방법


94

저장 프로 시저를 호출하고 일부 데이터를 검색하는 간단한 웹 응용 프로그램을 작성 중입니다. 클라이언트의 데이터베이스와 상호 작용하는 매우 간단한 응용 프로그램입니다. 직원 ID와 회사 ID를 전달하면 저장 프로 시저가 직원 세부 정보를 반환합니다.

웹 응용 프로그램은 데이터를 업데이트 / 삭제할 수 없으며 SQL Server를 사용하고 있습니다.

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 쿼리에 대해서만 지원됩니다. (당신이 명명 된 매개 변수를 원하는 경우 번호 매개 변수에 이름을 번역하는 자신의 클래스를 작성할 수 있습니다.)
Viliam BUR

나는 항상 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

Jboss AS에 웹 애플리케이션을 배포하고 있습니다. JPA를 사용하여 저장 프로 시저 또는 CallableStatement에 액세스해야합니까? 이 경우 JPA 사용의 장점.

정말 JPA 지원하지 않습니다하지만 그건 드리겠습니다 . 그래도 나는 이런 식으로 가지 않을 것입니다.

  • 일부 빈에서 저장 프로 시저 호출의 결과를 매핑하기 위해 JPA를 사용하는 것은 정말 과잉입니다.
  • 특히 JPA가 저장 프로 시저를 호출하는 데 실제로 적합하지 않다는 점을 감안할 때 (구문은 매우 장황 할 것입니다).

따라서 JDBC 데이터 액세스에 대한 Spring 지원 또는 MyBatis 와 같은 데이터 매퍼 를 사용하거나 응용 프로그램의 단순성을 고려할 때 원시 JDBC 및 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는 저장 프로 시저를 지원하지 않습니다 , 책 전체를 포함한 여러 곳에서 다운로드 할 수있는 페이지를 미리보기로, 당신은 전체 텍스트를하지 않는 것입니다 ( 이 하나 , 이것이 합법적인지 모르겠습니다).

어쨌든 텍스트는 다음과 같습니다.

JPA 및 데이터베이스 저장 프로 시저

SQL의 열렬한 팬이라면 데이터베이스 저장 프로 시저의 힘을 기꺼이 활용할 수 있습니다. 불행히도 JPA는 저장 프로 시저를 지원하지 않으므로 지속성 공급자의 독점 기능에 의존해야합니다. 그러나 네이티브 SQL 쿼리와 함께 간단한 저장 함수 (매개 변수없이)를 사용할 수 있습니다.


다음과 같은 오류 메시지가 표시됩니다. java.sql.SQLException : '@ P0'근처에 잘못된 구문이 있습니다.
user431514

3
"{call getEmployeeDetails (: employeeId, : companyId)}"여야합니다. SQL 서버의 경우 중괄호가 있어야합니다.
Vedran 2012-08-24

@Vedran 사실. 매개 변수 설정 부분에만 관심이있었습니다
Sean Patrick Floyd

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 2013

하하, 그래요. 그러나 이러한 것을 사용하는 이점은 데이터 개체 클래스를 가져 오기 위해 코드로드를 입력 할 필요가없고 모든 데이터를 recordSet에서 데이터 클래스로 전송하는 작업을 수행 할 필요가 없다는 것입니다. . 여전히 데이터 개체 (엔티티)가 있지만 Eclipse 마법사가이를 생성합니다.
Malcolm Boekhoff

1
예, 할 수 있습니다. 그러나 나는 모든 것이 생성되는 jOOQ 개발자로서 이것을 말하고 있습니다. 남은 일은 실제로 프로 시저 / 함수를 호출하는 것입니다.
Lukas Eder 2014

실제로 하단 예제 (구현 독립적)를 시도 했습니까? 절차가 xml파일 에 정의되어 있지만 작동하지 않는다는 차이점을 가지고 시도했습니다 . OUT매개 변수를 읽을 수 없습니다 .
Roland

6

나를 위해 Oracle 11g 및 Glassfish 2.1 (Toplink)에서는 다음 만 작업했습니다.

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

중괄호가있는 변형은 ORA-00900이되었습니다.


1
Oracle 11g, 최대 절전 모드 JPA 공급자에서 나를 위해 작동합니다.
David Mann

1
이것은 우리를 매우 큰 문제에서 벗어나게했습니다. 우리는 java6, oracle11g, Jboss6, Hibernate를 사용하고있었습니다. 감사합니다 @Chornyi.
압둘라 칸

6

EclipseLink를 사용하는 경우 @NamedStoredProcedureQuery 또는 StoreProcedureCall을 사용하여 출력 매개 변수 또는 out 커서가있는 저장 프로 시저를 포함하여 모든 저장 프로 시저를 실행할 수 있습니다. 저장된 함수 및 PLSQL 데이터 유형에 대한 지원도 사용할 수 있습니다.

http://en.wikibooks.org/wiki/Java_Persistence/Advanced_Topics#Stored_Procedures를 참조하십시오 .


1
EntityManager.createNamedStoredProcedureQuery ()가있는 EclipseLink 버전은 무엇입니까?
미르 이온

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

    적어도 Hibernate 4.x 및 5.x를 사용할 때 JPA StoredProcedureQuery가 SQL FUNCTIONS에서 작동하지 않기 때문 입니다.

JPA 및 Hibernate를 사용할 때 저장 프로 시저 및 함수를 호출하는 방법에 대한 자세한 내용은 다음 문서를 확인하십시오.


"...에 대한 호출에서 인수의 잘못된 수 또는 유형"오류 메시지가 계속 표시됩니다. 나는 내가 전화하고 있다는 것을 깨달았다 createNativeQuery. 로 전환했습니다 createStoredProcedureQuery. 그럼 짜잔!
Ahmet


2

Sql Srver와 같지 않을 수도 있지만 oracle 및 eclipslink를 사용하는 사람들에게는 나를 위해 일하고 있습니다.

예 : 하나의 IN 매개 변수 (CHAR 유형)와 두 개의 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에서 proc의 구조를 선언하십시오.

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

두 개의 출력 매개 변수를 얻으려면 :

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가 기본값으로 전환됩니다.


특정 패키지 내에서 프로 시저를 호출하려면 다음과 같이 호출해야합니다. call {package}. {procedure}?
라주 yourPepe

1

이 답변은 엔티티 관리자가있는 경우 유용 할 수 있습니다.

다음 번호를 생성하는 저장 프로 시저가 있었고 서버 측에는 seam 프레임 워크가 있습니다.

고객 입장에서

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

데이터베이스 측 (SQL 서버) getNextNmber


executeUpdate ()는 int를 반환합니다. sproc의 수신 출력이 확실합니까?
Constantine Gladky

1

JPA 2.0은 RETURN 값을 지원하지 않고 호출 만 지원합니다.

내 해결책은. PROCEDURE를 호출하는 FUNCTION을 만듭니다.

따라서 JAVA 코드 내에서 oracle FUNCTION을 호출하는 NATIVE QUERY를 실행합니다.


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

코디 고 자바

  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 FUNCTION을 호출하는 NATIVE QUERY를 실행합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.