No converter found capable of converting from type
failed because of: org.hibernate.MappingException: Unable to find resultset-ref definition: mappinMyNativeQuery
Native Query를 사용하다 보면 이런 오류들을 자주 마주하게 될것이다.
여러 테이블과의 조인하여 결과값을 도출하기 위해서 Native Query 를 사용하던 도중
Return 타입을 단순히 관련 엔티티의 타입으로 맞추어만 주면 자동으로 매핑되서 결과가 나오는줄 알았지만 아니다.
리턴 타입을 커스텀하게 받고 싶어서 삽질을 하다가 해결이 되어 정리한다.
Repository
@Repository
public interface NamedQueryTestRepository_V1 extends JpaRepository<T_INTERFACE_RECV_MSG,Long> {
@Query(value = "SELECT s.MSG_SN,s.SYS_CD,s.IF_ID, s.RCPTN_DT, s.RMK" +
" FROM t_interface_recv_msg s " +
" UNION " +
" SELECT r.MSG_SN,r.SYS_CD,r.IF_ID,r.SEND_DT as RCPTN_DT, r.RMK " +
" FROM t_interface_send_msg r ", nativeQuery = true)
public List<T_INTERFACE_RECV_MSG> list();
}
Entity1
public class T_INTERFACE_RECV_MSG {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "MSG_SN")
private Long msgSn;
@Column(name = "IF_ID")
private String ifId;
@Column(name = "ORGNL_DATA")
private String orgnlData;
@Column(name = "RCPTN_DT")
private LocalDateTime rcptnDt;
@Column(name = "RMK")
private String rmk;
@Column(name = "SYS_CD")
private String sysCd;
@Column(name = "TYPE")
private String type;
Entity2
@Entity
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "t_interface_send_msg")
public class T_INTERFACE_SEND_MSG {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "MSG_SN")
private Long msgSn;
@Column(name = "IF_ID")
private String ifId;
@Column(name = "ORGNL_DATA")
private String orgnlData;
@Column(name = "SEND_DT")
private LocalDateTime sendDt;
@Column(name = "RMK")
private String rmk;
@Column(name = "SYS_CD")
private String sysCd;
}
위 코드 처럼 단순히 리턴 값을 엔티티로 매핑을 하면

컬럼을 찾을수 없다면서 exception을 뱉게된다.
Native Query를 다시 확인해보면
SELECT s.MSG_SN,s.SYS_CD,s.IF_ID, s.RCPTN_DT, s.RMK" +
" FROM t_interface_recv_msg s " +
" UNION " +
" SELECT r.MSG_SN,r.SYS_CD,r.IF_ID,r.SEND_DT as RCPTN_DT, r.RMK " +
" FROM t_interface_send_msg r
t_interface_recv_msg 테이블과 t_interface_send_msg 테이블을 유니온 하여 내가 원하는 컬럼값만 가져온다.
그러나 반환 값은 t_interface_recv_msg 엔티티로 매핑해주고 있기 때문에 t_interface_recv_msg 엔티티에 선언된 컬럼값들이 모두 존재해야한다.
위의 오류는 존재하는 컬럼을 명시해주지 않았기 때문에 찾을수 없다는 오류를 내뱉고 있다.
그래서 모든존재 컬럼을 명시해주고 실행하면 제대로 되겠지만 문제는 유니온 하고 있는 다른 테이블의 컬럼이 똑같지 않다는 것이다.(type 컬럼)
결국 커스텀하게 원하는 컬럼값만 추출해서 리턴 값을 받고 싶다.
내가 사용한 방법 2가지를 정리한다.
Entity
@Entity
@Table(name = "t_interface_recv_msg")
@NoArgsConstructor
@AllArgsConstructor
@Data
@NamedNativeQuery(
name = "myQuery",
query = "SELECT s.MSG_SN,s.SYS_CD,s.IF_ID, s.RCPTN_DT, s.RMK FROM t_interface_recv_msg s" +
" UNION" +
" SELECT r.MSG_SN,r.SYS_CD,r.IF_ID,r.SEND_DT as RCPTN_DT, r.RMK" +
" FROM t_interface_send_msg r ",
resultSetMapping = "mapppinNativeQuery"
)
@SqlResultSetMapping(
name = "mapppinNativeQuery", // same as resultSetMapping above in NativeQuery
classes = {
@ConstructorResult(
targetClass = RecvVO.class,
columns = {
@ColumnResult( name = "MSG_SN", type = Long.class),
@ColumnResult( name = "SYS_CD", type = String.class),
@ColumnResult( name = "IF_ID", type = String.class),
@ColumnResult( name = "RCPTN_DT", type = LocalDateTime.class),
@ColumnResult( name = "RMK", type = String.class)
}
)
}
)
public class T_INTERFACE_RECV_MSG {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "MSG_SN")
private Long msgSn;
@Column(name = "IF_ID")
private String ifId;
@Column(name = "ORGNL_DATA")
private String orgnlData;
@Column(name = "RCPTN_DT")
private LocalDateTime rcptnDt;
@Column(name = "RMK")
private String rmk;
@Column(name = "SYS_CD")
private String sysCd;
@Column(name = "TYPE")
private String type;
}
RecvVO
@Data
@AllArgsConstructor
public class RecvVO {
private Long MSG_SN;
private String SYS_CD;
private String IF_ID;
private LocalDateTime RCPTN_DT;
private String RMK;
}
Repository
@Repository
public class NamedQueryTestRepository_V0 {
@PersistenceContext
EntityManager em;
public List<RecvVO> v0(){
Query query = em.createNamedQuery("myQuery");
List<RecvVO> test= query.getResultList();
return test;
};
}
RecvDTO
public interface RecvDTO {
Integer getMSG_SN();
String getSYS_CD();
String getIF_ID();
Instant getRCPTN_DT();
String getRMK();
}
Repository
@Repository
public interface NamedQueryTestRepository_V1 extends JpaRepository<T_INTERFACE_RECV_MSG,Long> {
@Query(value = "SELECT s.MSG_SN,s.SYS_CD,s.IF_ID, s.RCPTN_DT, s.RMK" +
" FROM t_interface_recv_msg s " +
" UNION " +
" SELECT r.MSG_SN,r.SYS_CD,r.IF_ID,r.SEND_DT as RCPTN_DT, r.RMK " +
" FROM t_interface_send_msg r ", nativeQuery = true)
public List<RecvDTO> v1();
}
@SpringBootTest
public class ComboTableRepositoryTest {
@Autowired
NamedQueryTestRepository_V0 repository_v0;
@Autowired
NamedQueryTestRepository_V1 repository_v1;
@Test
@DisplayName("Native Query 출력하기 v0")
public void v0(){
//When
List<RecvVO> expected = repository_v0.test();
expected.forEach(v -> {
System.out.println(v);
});
//Then
Assertions.assertThat(expected.size()).isGreaterThan(0);
}
@Test
@DisplayName("Native Query 출력하기 v1")
public void v1(){
//When
List<RecvDTO> expected = repository_v1.list();
expected.forEach(v -> {
System.out.println(v);
});
//Then
Assertions.assertThat(expected.size()).isGreaterThan(0);
}
}

테스트 결과 Named Query 방법 보다는 Interface 반환 방법이 빠르다.
후자를 사용하자