2016년 12월 21일 수요일

[자바교육,스프링교육,JPA교육학원_탑크리에듀](Spring Data JPA팁, REF_CURSOR오류)스프링JPA에서오라클 프로시저호출시 REF CURSOR로 여러레코드를 리턴받는 경우 스프링 JPA에서 발생하는 REF_CURSOR parameters should be accessed via results 에러 해결방법

(Spring Data JPA팁, REF_CURSOR오류)스프링JPA에서오라클 프로시저호출시 REF CURSOR로 여러레코드를 리턴받는 경우 스프링 JPA에서 발생하는 REF_CURSOR parameters should be accessed via results 에러 해결방법 

Spring Data JPA에서 @Procedure 어노테이션을 이용하여 오라클 프러시저를 간단히 쿼리 메소드 형태로 호출가능한데 REF CURSOR를 사용하면 다음과 같은 오류가 발생한다.

"REF_CURSOR parameters should be accessed via results"

해결 방법은 Spring Data JPA에서 @Procedure 어노테이션을 사용하지 않고 사용자 정의 레코지토리 형태로 메소드를 만들어서 StoredProcedureQuery query = em.createNamedStoredProcedureQuery("Emp.getEmpByDeptno"); 형태로 ResultSet을 직접받아 처리하도록 수정하면 된다. 예문을 참조하자~

예문에서는 두개의 메소드, 프러시저가 있는데 p_getEmpByDeptno 프로시저가 REF CURSOR로 정의되어 있으니 예문을 참조하자.

0. 오라클쪽 프로시저

CREATE OR REPLACE  PROCEDURE p_getEnameByEmpno(p_empno  IN   INTEGER,
                                                                       p_ename OUT VARCHAR2)          
IS        
BEGIN       
     SELECT  ename
     INTO    p_ename
     FROM    emp
     WHERE   empno  =  p_empno;

END p_getEnameByEmpno;
/

CREATE OR REPLACE  PROCEDURE p_getEmpByDeptno
(p_deptno  IN  INTEGER, emp_cursor OUT SYS_REFCURSOR )
IS
  sql_string Varchar2(100);
BEGIN       
     sql_string := 'select empno, ename, deptno from Emp where deptno = :deptno' ;
     Open emp_cursor FOR sql_string USING p_deptno;  
END p_getEmpByDeptno;
/




1. 엔티티

package jpa.model;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.NamedStoredProcedureQueries;
import javax.persistence.NamedStoredProcedureQuery;
import javax.persistence.ParameterMode;
import javax.persistence.StoredProcedureParameter;

@Entity
@NamedStoredProcedureQueries({    
   @NamedStoredProcedureQuery(name = "Emp.getEnameByEmpno", 
          procedureName = "p_getEnameByEmpno",          
          parameters = {
      //*************************************************************************
      // @StoredProcedureParameter에 name속성으로  DB에 만든프로시저 실제 파라미터명 사용가능 하지만 
         // out parameter와 같이 사용되는 경우  사용불가능 하다.
         // out parameter 사용하는 경우 프로시저의 파라미터 사용은 위치지정 방식으로 사용해야 한다.(1,2,3,,)
      // 또한 위치 지정방식과 Named 방식은 혼재 불가
         //*************************************************************************
            @StoredProcedureParameter(mode = ParameterMode.IN, type = Integer.class),
            @StoredProcedureParameter(mode = ParameterMode.OUT, type = String.class)
          }),
   @NamedStoredProcedureQuery(name = "Emp.getEmpByDeptno", 
       procedureName = "p_getEmpByDeptno",
       resultClasses = Emp.class,
       parameters = {       
           //*********************************************************************
       // REF CURSOR를 사용한다면 Spring Data JPA에서 직접사용 불가, 
       // Spring Data JPA의 Hibernate는  REF_CURSOR 지원하지 않지만  EclipseLink는 지원.
       //*********************************************************************
       @StoredProcedureParameter(mode = ParameterMode.IN, type = Integer.class),
       @StoredProcedureParameter(mode = ParameterMode.REF_CURSOR, type = void.class)
       })
})
public class Emp {
@Id
private Integer empno;
private String ename;
private Integer deptno;
public Integer getDeptno() {
return deptno;
}

public void setDeptno(Integer deptno) {
this.deptno = deptno;
}

public Integer getEmpno() {
return empno;
}

public void setEmpno(Integer empno) {
this.empno = empno;
}


public String getEname() {
return ename;
}

public void setEname(String ename) {
this.ename = ename;
}
public String toString() {
return "[empno=" + empno + ", ename=" + ename + ",deptno=" + deptno + "]";
}
}


2. 레포지터리

[EmpRepositoryCustom.java]

package jpa.repository;

import java.util.List;

import jpa.model.Emp;

public interface EmpRepositoryCustom {
//******************************************************
// 10번 부서의 사원들 추출
// 부서코드를 입력매개변수로 REF CURSOR로 사원들을 리턴받는 프로시저 호출
//******************************************************
List<Emp> getEmpByDeptno(int deptno);
}


[EmpRepositoryImpl.java]

package jpa.repository;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.StoredProcedureQuery;

import jpa.model.Emp;

public class EmpRepositoryImpl implements EmpRepositoryCustom {
@PersistenceContext
EntityManager em;

@Override
//******************************************************
// 10번 부서의 사원들 추출
// 부서코드를 입력매개변수로 REF CURSOR로 사원들을 리턴받는 프로시저 호출
//******************************************************
public List<Emp> getEmpByDeptno(int deptno) {
StoredProcedureQuery query = em.createNamedStoredProcedureQuery("Emp.getEmpByDeptno");
query.setParameter(1, deptno);
query.execute();

@SuppressWarnings("unchecked")
List<Emp> emps = query.getResultList();
return emps;
}
}


[EmpRepository.java]
package jpa.repository;

import org.springframework.data.jpa.repository.query.Procedure;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;

import jpa.model.Emp;

public interface EmpRepository extends CrudRepository<Emp, Integer>, EmpRepositoryCustom {
//***************************************************
//***** 사번을 입력받아 이름을 OUT 파라미터로 던지는 프로시저를 호출
//***************************************************
@Procedure(name = "Emp.getEnameByEmpno")
String getEnameByEmpno(@Param("p_empno") int p_empno);

//@Procedure
//String p_getEnameByEmpno(Integer p_empno); 집접 DB에 만든 프로시저 이름을 써도 된다.
//****************************************************************************
// 주의사항!!
//****************************************************************************
// REF CURSOR를 사용하는 경우 Spring Data JPA에서 직접지원하지 않으므로 직접 구현해야 한다.
// 오류메시지 : REF_CURSOR parameters should be accessed via results
//****************************************************************************
//@Procedure(name = "Emp.getEmpByDeptno")
//List<Emp> getEmpByDeptno(@Param("p_deptno") int p_deptno);

}


[스프링 부트 메인]

package jpa;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import jpa.model.Emp;
import jpa.repository.EmpRepository;

@SpringBootApplication
public class ProcfunctestApplication implements CommandLineRunner {

public static void main(String[] args) {
SpringApplication.run(ProcfunctestApplication.class, args);
}
@Autowired
EmpRepository empRepository;

@Override
//@Transactional
public void run(String... arg0) throws Exception {
//****************************************************
// 사번을 입력 매개변수로 프로시저를 호출하여 OUT 파라미터로 이름을 받음
//****************************************************
String ename1 = empRepository.getEnameByEmpno(7369);
System.out.println("p_getEnameByEmpno 호출 : " + ename1);
//****************************************************
// 부서코드를 입력변수로 프로시저를 호출하여 OUT 파라미터로 사원들을 받음
//****************************************************
List<Emp> emps = empRepository.getEmpByDeptno(10);
System.out.println("f_getEmpByDeptno 호출 : ");
emps.forEach(System.out::println);
}
}


첨부 파일 참조하세요,

첨부파일 URL참조 - http://ojc.asia/bbs/board.php?bo_table=LecJpa&wr_id=259

댓글 없음:

댓글 쓰기