본문 바로가기
Java/Spring

[Spring data JPA] native query + XML + DTO

by java개발자 2021. 4. 24.

기준: springboot 2.1.9

 

JPA를 사용하는 이유가

객체지향적인 설계와 개발 생산성을 위함인데,

왜 String을 여러라인으로 붙여 쓰고 있지? + + + + + +

왜 SQL 결과를 Object[]로 받는 방법을 사용하는지 이해가 안된다.

(다시 10년전 과거로 돌아간 기분이다...)

ResultMapping을 어노테이션으로 잡다하게 설정하는게 과연 Mybatis에서 JPA로 탈출한 취지에 맞는가?

 

String 여러 라인을 붙여쓰거나

리턴으로 Object[]로 받을거면

그냥 Mybatis 쓰는게 낫겠다.

 

* multiLine String은 XML로...

* Object[]은 Dto로 해결...

 

native query + XML + DTO

 

org.example.api.dto.NativeDto1.java

package org.example.api.dto;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@AllArgsConstructor
@NoArgsConstructor
@Data
public class NativeDto1 {
	private Long id;
	private String name;
	private Long aaa;
	private String bbb;
}

 

repository.java

package org.example.repositories;

import java.util.List;

import org.example.api.dto.NativeDto1;
import org.example.models.Team;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

public interface TeamRepository extends JpaRepository<Team, Long>{
	
	@Query(nativeQuery = true)
	public List<NativeDto1> findByName4(@Param("name") String aaa);
}

META-INF/orm.xml

<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://xmlns.jcp.org/xml/ns/persistence/orm" version="2.2">
	
 	<named-native-query name="Team.findByName4" result-set-mapping="aaaaa5">
		<query>
        <![CDATA[ 
			select
				t.id as id,
				t.name as name,
				m.id as aaa,
				m.name as bbb
			from team t
			inner join Member m
			on t.id = m.team_id
			where t.name = :name
        ]]></query>
	</named-native-query>
	
	<sql-result-set-mapping name="aaaaa5">
		<constructor-result target-class="org.example.api.dto.NativeDto1">
			<column name="id" class="java.lang.Long" />
			<column name="name" />
			<column name="aaa" class="java.lang.Long" />
			<column name="bbb" /> 
		</constructor-result>
	</sql-result-set-mapping>
</entity-mappings>

 

주의!!!!

repository에서 @Query(nativeQuery = true) 를 하지 않으면 다음 에러가 발생한다.

Got different size of tuples and aliases; nested exception is org.hibernate.HibernateException: Got different size of tuples and aliases

xml에 <named-native-query>를 사용했는데, 왜 또 설정을 해야 하나?

 

org.hibernate.jpa.spi.NativeQueryTupleTransformer

public NativeTupleImpl(Object[] tuple, String[] aliases) {
  // ...
  if ( tuple.length != aliases.length ) {
    throw new HibernateException( "Got different size of tuples and aliases" );
  }
  // ...
}

디버깅해보면... (size가 안맞긴 안맞네...)

tuple쪽은 이미 Dto생성자로 잘 만들었는데, 왜 aliases는 여전히 sql column으로 비교를 하는 걸까?

@Query(nativeQuery = true)를 사용하면, NativeTupleImpl 함수 자체를 실행하지 않는다.

아.. NativeQueryTupleTransformer 클래스 자체에 들어오지 않네!!

 

참고:

stackoverflow.com/questions/49056084/got-different-size-of-tuples-and-aliases-exception-after-spring-boot-2-0-0-rel

 

 

 

springboot 2.4.4 에서도 아직 해결이 되지 않았다.-_-;

 

 

 

==============================================================

DTO를 사용하지 않으면, Object[]로 받아야 한다.

public List<Object[]> findByName4(@Param("name") String aaa);
 	<named-native-query name="Team.findByName4" result-set-mapping="aaaaa5">
		<query>
        <![CDATA[ 
			select
				t.id as id,
				t.name as name,
				m.id as aaa,
				m.name as bbb
			from team t
			inner join Member m
			on t.id = m.team_id
			where t.name = :name
        ]]></query>
	</named-native-query>
	<sql-result-set-mapping name="aaaaa5">
		<entity-result entity-class="org.example.models.Team" />
		<column-result name="aaa" /> 
		<column-result name="bbb" /> 
	</sql-result-set-mapping>

Object[0] 에는 Team

Object[1] 에는 aaa

Object[2] 에는 bbb

 

 

 

==============================================================

// TODO

<constructor-result>를 사용하면서

entity는 entity대로 받고, 나머지 컬럼만 따로 받으면 가장 좋을듯