(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
댓글 없음:
댓글 쓰기