programing

스프링 데이터 JPA는 네이티브 쿼리 결과를 비엔티티 POJO에 매핑합니다.

golfzon 2023. 3. 20. 23:47
반응형

스프링 데이터 JPA는 네이티브 쿼리 결과를 비엔티티 POJO에 매핑합니다.

네이티브 쿼리를 사용하는 Spring Data 저장소 메서드가 있습니다.

@Query(value = "SELECT g.*, gm.* FROM group g LEFT JOIN group_members gm ON g.group_id = gm.group_id and gm.user_id = :userId WHERE g.group_id = :groupId", nativeQuery = true)
GroupDetails getGroupDetails(@Param("userId") Integer userId, @Param("groupId") Integer groupId);

결과를 Non-Entity POJO에 매핑하고 싶습니다.GroupDetails.

가능하다면 예를 들어 주시겠습니까?

그렇게 하는 가장 쉬운 방법은 투사라고 불리는 것을 사용하는 것이라고 생각합니다.쿼리 결과를 인터페이스에 매핑할 수 있습니다.사용.SqlResultSetMapping는 불편하고 코드를 추하게 만듭니다. : ).

스프링 데이터 JPA 소스 코드의 예:

public interface UserRepository extends JpaRepository<User, Integer> {

   @Query(value = "SELECT firstname, lastname FROM SD_User WHERE id = ?1", nativeQuery = true)
   NameOnly findByNativeQuery(Integer id);

   public static interface NameOnly {

     String getFirstname();

     String getLastname();

  }
}

이 방법을 사용하여 투영 목록을 가져올 수도 있습니다.

예측에 대한 자세한 내용은 이 스프링 데이터 JPA 문서 항목을 확인하십시오.

주 1:

잊지 마세요.Userentity defined as normal - 투영 인터페이스의 필드는 이 엔티티의 필드와 일치해야 합니다.그렇지 않으면 필드 매핑이 끊어질 수 있습니다(getFirstname()는 성 등의 값을 반환할 수 있습니다).

주 2:

사용하시는 경우SELECT table.column ...표기법은 항상 엔티티 이름과 일치하는 에일리어스를 정의합니다.예를 들어 이 코드가 제대로 작동하지 않습니다(프로젝션은 각 getter에 대해 null을 반환합니다).

@Query(value = "SELECT user.firstname, user.lastname FROM SD_User user WHERE id = ?1", nativeQuery = true)
NameOnly findByNativeQuery(Integer id);

하지만 이 방법은 잘 작동합니다.

@Query(value = "SELECT user.firstname AS firstname, user.lastname AS lastname FROM SD_User user WHERE id = ?1", nativeQuery = true)
NameOnly findByNativeQuery(Integer id);

더 복잡한 질의의 경우, 저는 차라리JdbcTemplate대신 커스텀 저장소를 사용합니다.

Group Details가 orid의 답변이라고 가정하고 JPA 2.1 @Constructor Result를 사용해 보셨습니까?

@SqlResultSetMapping(
    name="groupDetailsMapping",
    classes={
        @ConstructorResult(
            targetClass=GroupDetails.class,
            columns={
                @ColumnResult(name="GROUP_ID"),
                @ColumnResult(name="USER_ID")
            }
        )
    }
)

@NamedNativeQuery(name="getGroupDetails", query="SELECT g.*, gm.* FROM group g LEFT JOIN group_members gm ON g.group_id = gm.group_id and gm.user_id = :userId WHERE g.group_id = :groupId", resultSetMapping="groupDetailsMapping")

저장소 인터페이스에서 다음을 사용합니다.

GroupDetails getGroupDetails(@Param("userId") Integer userId, @Param("groupId") Integer groupId);

Spring Data JPA 문서에 따르면 Spring은 먼저 메서드 이름과 일치하는 명명된 쿼리를 검색하려고 합니다.따라서 다음을 사용합니다.@NamedNativeQuery,@SqlResultSetMapping그리고.@ConstructorResult당신은 그 행동을 할 수 있을 것이다

마이클의 접근법이 더 나은 것 같아그러나 네이티브 쿼리에서 결과를 얻을 수 있는 방법이 하나 더 있습니다.

@Query(value = "SELECT g.*, gm.* FROM group g LEFT JOIN group_members gm ON g.group_id = gm.group_id and gm.user_id = :userId WHERE g.group_id = :groupId", nativeQuery = true)
String[][] getGroupDetails(@Param("userId") Integer userId, @Param("groupId") Integer groupId);

이제 이 2D 문자열 배열을 원하는 엔티티로 변환할 수 있습니다.

네이티브 또는 비네이티브 쿼리를 원하는 방식으로 작성할 수 있으며 JPQL 쿼리 결과를 사용자 정의 결과 클래스의 인스턴스로 래핑할 수 있습니다.쿼리에서 반환된 열 이름이 같은 DTO를 만들고 쿼리에서 반환된 순서와 이름을 가진 all 인수 생성자를 만듭니다.그런 다음 다음 다음 방법을 사용하여 데이터베이스를 쿼리합니다.

@Query("SELECT NEW example.CountryAndCapital(c.name, c.capital.name) FROM Country AS c")

DTO 작성:

package example;

public class CountryAndCapital {
    public String countryName;
    public String capitalName;

    public CountryAndCapital(String countryName, String capitalName) {
        this.countryName = countryName;
        this.capitalName = capitalName;
    }
}

이것은 Map으로 변환한 후 커스텀오브젝트로 변환하기 위한 솔루션입니다.

private ObjectMapper objectMapper;

public static List<Map<String, Object>> convertTuplesToMap(List<?> tuples) {
    List<Map<String, Object>> result = new ArrayList<>();

    tuples.forEach(object->{
        if(object instanceof Tuple single) {
            Map<String, Object> tempMap = new HashMap<>();
            for (TupleElement<?> key : single.getElements()) {
                tempMap.put(key.getAlias(), single.get(key));
            }
            result.add(tempMap);
        }else{
            throw new RuntimeException("Query should return instance of Tuple");
        }
    });

    return result;
}

public <T> List<T> parseResult(List<?> list, Class<T> clz){
    List<T> result = new ArrayList<>();
    convertTuplesToMap(list).forEach(map->{
        result.add(objectMapper.convertValue(map, clz));
    });
    return result;
}

public static class CustomDTO{
    private String param1;
    private Integer param2;
    private OffsetDateTime param3;
}

public List<CustomDTO> doSomeQuery(){
    Query query = entityManager.createNativeQuery("SELECT param1, param2 param3 ... ", Tuple.class);
    return parseResult(query.getResultList(), CustomDTO.class);
}

인터페이스에서 기본 방식을 사용하여 EntityManager에 ResultTransformer를 설정할 기회를 부여하면 다음과 같이 순수 POJO를 반환할 수 있습니다.

final String sql = "SELECT g.*, gm.* FROM group g LEFT JOIN group_members gm ON g.group_id = gm.group_id and gm.user_id = ? WHERE g.group_id = ?";
default GroupDetails getGroupDetails(Integer userId, Integer groupId) {
    return BaseRepository.getInstance().uniqueResult(sql, GroupDetails.class, userId, groupId);
}

BaseRepository.java는 다음과 같습니다.

@PersistenceContext
public EntityManager em;

public <T> T uniqueResult(String sql, Class<T> dto, Object... params) {
    Session session = em.unwrap(Session.class);
    NativeQuery q = session.createSQLQuery(sql);
    if(params!=null){
        for(int i=0,len=params.length;i<len;i++){
            Object param=params[i];
            q.setParameter(i+1, param);
        }
    }
    q.setResultTransformer(Transformers.aliasToBean(dto));
    return (T) q.uniqueResult();
}

이 솔루션은 저장소 인터페이스 파일의 다른 방법에는 영향을 주지 않습니다.

JPA PROJections 사용 사용자 정의 유형의 개체로 데이터를 검색하는 것이 바람직할 수 있습니다.이러한 유형은 루트 클래스의 부분 보기를 반영하며, 여기에는 관심 있는 속성만 포함됩니다.바로 여기서 투영법이 유용하게 쓰일 수 있습니다.처음에 엔티티를 @immutable로 선언하다

@Entity
@Immutable

퍼블릭 클래스 주소 {

@Id
private Long id;

저장소 설정

public interface AddressView {
    String getZipCode();
}

그런 다음 저장소 인터페이스에서 사용합니다.

public interface AddressRepository extends Repository<Address, Long> {
      @Query("EXEC SP_GETCODE ?1")
    List<AddressView> getAddressByState(String state);
}

@repository 구조 및 @service 구조에서 스프링 부트 시 커스텀 SQL 쿼리를 실행하는 경우.한번 봐주세요.

https://stackoverflow.com/a/71501509/4735043

뭐 이런 거 할 수 있어요

@NamedQuery(name="IssueDescriptor.findByIssueDescriptorId" ,

    query=" select new com.test.live.dto.IssuesDto (idc.id, dep.department, iss.issueName, 
               cat.issueCategory, idc.issueDescriptor, idc.description) 
            from Department dep 
            inner join dep.issues iss 
            inner join iss.category cat 
            inner join cat.issueDescriptor idc 
            where idc.id in(?1)")

그리고 이런 컨스트럭터도 있을 거예요

public IssuesDto(long id, String department, String issueName, String issueCategory, String issueDescriptor,
            String description) {
        super();
        this.id = id;
        this.department = department;
        this.issueName = issueName;
        this.issueCategory = issueCategory;
        this.issueDescriptor = issueDescriptor;
        this.description = description;
    }

언급URL : https://stackoverflow.com/questions/29082749/spring-data-jpa-map-the-native-query-result-to-non-entity-pojo

반응형