codememo

유형 간에 변환할 수 있는 변환기를 찾을 수 없습니다.

tipmemo 2023. 4. 13. 20:53
반응형

유형 간에 변환할 수 있는 변환기를 찾을 수 없습니다.

다음 스택 트레이스를 받습니다.

org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [referencedata.ABDeadlineType] to type [referencedata.DeadlineType]
    at org.springframework.core.convert.support.GenericConversionService.handleConverterNotFound(GenericConversionService.java:324)
    at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:206)
    at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:187)
    at org.springframework.data.repository.query.ResultProcessor$ProjectingConverter.convert(ResultProcessor.java:256)
    at org.springframework.data.repository.query.ResultProcessor$ChainingConverter$1.convert(ResultProcessor.java:201)
    at org.springframework.data.repository.query.ResultProcessor$ChainingConverter.convert(ResultProcessor.java:212)
    at org.springframework.data.repository.query.ResultProcessor.processResult(ResultProcessor.java:149)
    at org.springframework.data.jpa.repository.query.AbstractJpaQuery.doExecute(AbstractJpaQuery.java:121)
    at org.springframework.data.jpa.repository.query.AbstractJpaQuery.execute(AbstractJpaQuery.java:106)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:483)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:461)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:56)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:133)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:57)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
    at com.sun.proxy.$Proxy143.findAllSummarizedBy(Unknown Source)
    at 

나의 수업은 다음과 같다.

마감 시간유형

@Data
public class DeadlineType extends DefaultIdAndText {
    @Value("#{target.id}")
    String id;

    @Value("#{target.code}")
    String text;

    @Value("#{target.id}")
    public String getId() {
        return id;
    }

    @Value("#{target.code}")
    public String getText() {
        return text;
    }

}

ABDeadline유형

@Data
@Entity
@Table(name = "deadline_type")
@AllArgsConstructor
@NoArgsConstructor
public class ABDeadlineType {

    private @Id
    String id;
    private String code;
}

DefaultIdAndText

@Data @AllArgsConstructor
@NoArgsConstructor
public class DefaultIdAndText implements IdAndText {

    public DefaultIdAndText(IdAndText idAndText){
        this.id = idAndText.getId();
        this.text = idAndText.getText();
    }

    @NotEmpty String id;
    String text;
}

기한 타입 저장소

public interface DeadlineTypeRepository extends JpaRepository<ABDeadlineType, Long> {
    List<DeadlineType> findAllSummarizedBy();
}

갱신하다

프로젝션/매핑이 다음을 사용하는 경우 문제가 될 수 있습니까?@Value("#{target.id}")인터페이스가 아닌 클래스에서 실행되었기 때문에 올바르게 동작하지 않는가?

돌아가다ABDeadlineType저장소:

public interface ABDeadlineTypeRepository extends JpaRepository<ABDeadlineType, Long> {
    List<ABDeadlineType> findAllSummarizedBy();
}

그런 다음 DeadlineType으로 변환합니다.수동으로 하거나 맵 구조를 사용합니다.

또는 에서 컨스트럭터를 호출합니다.@Query주석:

public interface DeadlineTypeRepository extends JpaRepository<ABDeadlineType, Long> {

    @Query("select new package.DeadlineType(a.id, a.code) from ABDeadlineType a ")
    List<DeadlineType> findAllSummarizedBy();
}

또는 사용@Projection:

@Projection(name = "deadline", types = { ABDeadlineType.class })
public interface DeadlineType {

    @Value("#{target.id}")
    String getId();

    @Value("#{target.code}")
    String getText();

}

업데이트: 스프링은 필요 없습니다.@Projection주석:

public interface DeadlineType {
    String getId();    
    String getText();
}

이미 동작하고 있는 경우도 있습니다만, 이하의 클래스로 테스트 프로젝트를 작성했습니다.이것에 의해, 엔티티, 투영, 또는 dto 로 데이터를 취득할 수 있습니다.

투영 - 코드 열이 한 번 명명된 코드와 명명된 텍스트(예: 전용)로 두 번 반환됩니다.위에서 설명한 바와 같이 @Projection 주석은 필요 없습니다.

import org.springframework.beans.factory.annotation.Value;

public interface DeadlineTypeProjection {
    String getId();

    // can get code and or change name of getter below
    String getCode();

    // Points to the code attribute of entity class
    @Value(value = "#{target.code}")
    String getText();
}

DTO 클래스 - 기본 클래스에서 상속된 다음 속성을 재정의한 이유를 알 수 없습니다.JsonProperty는 REST 엔드 포인트로 반환되는 필드의 이름을 변경하는 방법의 예시입니다.

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;

@Data
@AllArgsConstructor
public class DeadlineType {
    String id;

    // Use this annotation if you need to change the name of the property that is passed back from controller
    // Needs to be called code to be used in Repository
    @JsonProperty(value = "text")
    String code;

}

엔티티 클래스

import lombok.Data;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Data
@Entity
@Table(name = "deadline_type")
public class ABDeadlineType {

    @Id
    private String id;
    private String code;
}

저장소 - 저장소가 JpaRepository를 확장합니다<ABDeadlineType, Long > 단, ID는 문자열이므로 아래 JpaRepository로 갱신됩니다.<ABDeadline유형, 문자열>

import com.example.demo.entity.ABDeadlineType;
import com.example.demo.projection.DeadlineTypeProjection;
import com.example.demo.transfer.DeadlineType;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;

public interface ABDeadlineTypeRepository extends JpaRepository<ABDeadlineType, String> {

    List<ABDeadlineType> findAll();

    List<DeadlineType> findAllDtoBy();

    List<DeadlineTypeProjection> findAllProjectionBy();

}

: 컨트롤러 - 저장소에 직접 액세스하여 코드를 단순화합니다.

@RequestMapping(value = "deadlinetype")
@RestController
public class DeadlineTypeController {

    private final ABDeadlineTypeRepository abDeadlineTypeRepository;

    @Autowired
    public DeadlineTypeController(ABDeadlineTypeRepository abDeadlineTypeRepository) {
        this.abDeadlineTypeRepository = abDeadlineTypeRepository;
    }

    @GetMapping(value = "/list")
    public ResponseEntity<List<ABDeadlineType>> list() {

        List<ABDeadlineType> types = abDeadlineTypeRepository.findAll();
        return ResponseEntity.ok(types);
    }

    @GetMapping(value = "/listdto")
    public ResponseEntity<List<DeadlineType>> listDto() {

        List<DeadlineType> types = abDeadlineTypeRepository.findAllDtoBy();
        return ResponseEntity.ok(types);
    }

    @GetMapping(value = "/listprojection")
    public ResponseEntity<List<DeadlineTypeProjection>> listProjection() {

        List<DeadlineTypeProjection> types = abDeadlineTypeRepository.findAllProjectionBy();
        return ResponseEntity.ok(types);
    }
}

도움이 되었으면 좋겠다

레스

최근 spring-data-jpa: 2.5.0에서도 같은 문제가 발생했습니다.

솔루션(@Query 주석이 없는 쿼리의 경우):

Class-Based Projection(DTO; 클래스 기반 투영)의 경우 문제는@NoArgsConstructorDTO 클래스에서요.그것을 움직이면 일이 잘 풀릴 것이다.

디버깅 중에 발견한 흥미로운 점:

인수 생성자가 아닌 경우,returnedType어떻게든 0 입력 속성을 사용하여 생성되었습니다.

쿼리가 실제로 생성되면JpaQueryCreator(spring-data-jpa)는 입력 속성의 수에 따라 커스텀구축이 필요한지 여부를 확인합니다.

입력 속성이 0이 아니므로 엔티티 인스턴스 전체가 반환됩니다.

https://github.com/spring-projects/spring-data-jpa/blob/main/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java#L169

마지막으로 엔티티 인스턴스에서 프로젝션 dto로 변환할 수 있는 변환기가 없기 때문에 결과가 반환될 때 대상 유형과 반환 유형이 일치하지 않습니다.에러가 발생했습니다.

https://github.com/spring-projects/spring-data-commons/blob/main/src/main/java/org/springframework/data/repository/query/ResultProcessor.java#L162

심플한 솔루션:

쿼리에 {nativeQuery=true}을(를) 사용합니다.

예를들면

  @Query(value = "select d.id,d.name,d.breed,d.origin from Dog d",nativeQuery = true)
        
    List<Dog> findALL();

했다고 나와 ABDeadlineType로로 합니다.DeadlineType저장소에서 다음 오브젝트가 반환되기 때문입니다.ABDeadlineTypespring-data-jpa됩니다.DeadlineType같은 타입을 저장소에서 반환한 후 모델 클래스로 변환하기 위한 중간 util 클래스를 가지고 있어야 합니다.

public interface ABDeadlineTypeRepository extends JpaRepository<ABDeadlineType, Long> {
    List<ABDeadlineType> findAllSummarizedBy();
}

테이블 이름이 모델 이름과 다른 경우 주석을 다음과 같이 변경해야 합니다.

@Entity
@Table(name = "table_name")
class WhateverNameYouWant {
    ...

단순히 @Entity 주석을 사용하는 것이 아니라

제가 이상했던 것은 변환하려는 클래스가 존재하지 않았다는 것입니다.이건 나한테 효과가 있었어.

Dto의 Projections and Classes에 대한 Interfaces for Dtos와 Projections를 사용하여 Dto 클래스에 투영을 매핑하고 있습니다.그러면 1개의 Dto 클래스는 Dto에 매핑할 수 있고 맛에 익숙한 투영을 많이 가질 수 있습니다.

그래들 실장 'model.modelmapper: modelmapper: 3.1.0'

   @Autowired
    private ModelMapper modelMapper;
    
List<UserDto> usersdto = repository.findUserByRoleName().stream().map(userprojection -> modelMapper.map(userprojection, UserDto.class))
                    .collect(Collectors.toList());

제 예상은 이렇습니다.

public interface UserProjection {
     String getId();    
     String getEmail(); 
}

내 dto는

@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserDto {
    private long id;
    private String firstName;
    private String lastName;
    private String phone;
    private String email;
}

커스텀 쿼리에서 필드를 가져올 수 있습니다.

을 '클래스 이름'으로 변경합니다.DeadlineType

확장 JpaRepository <class, type>

예를 들어 다음과 같습니다.

코드에서 쿼리를 배치한 저장소는 JpaRepository를 확장하여 클래스와 ID 타입이 <ABDeadline>입니다.[ Long ]를 입력합니다.그래서 ABDeadline을 반환할 것으로 예상합니다.데이터를 입력합니다.

public interface DeadlineTypeRepository extends JpaRepository<ABDeadlineType, Long> {
    List<DeadlineType> findAllSummarizedBy();
}

납기를 원하시면데이터를 입력합니다. 쿼리를 다음과 같은 저장소에 보관해야 합니다.

public interface DeadlineTypeRepository extends JpaRepository<DeadlineType, Long>

따라서 JpaRepository 내의 클래스 이름을 바꾸거나 다른 저장소에 쿼리를 배치합니다.그러면 매핑을 하거나 추가 코드를 작성할 필요가 없습니다.

저 같은 경우에는 효과가 있었어요.

이 문제를 해결하는 것은 코드 세그먼트입니다.이것은 이 예의 네이티브 쿼리입니다.

SELECT cifno,accnbr FROM institutedb.customerWHERE id = 100200

순서 1: 먼저 임의의 테이블과 관련된 엔티티 클래스를 만듭니다.

@Entity
@Table(name = "customer", schema = "institutedb")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class CustomerEntity {
    @Id
    private int id;
    private BigInteger accnbr;
    private String actdate;
    private int active;
}

스텝 2: 다음으로 프로젝션 인터페이스와 DTO 클래스를 청소합니다.keep remind interface getter 메서드에는 DTO 속성이 있습니다.

투영 인터페이스

   public interface CustomerProjection {
        String getCifno();
        String getAccnbr();
    }

DTO 클래스

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class CustomerDTO {

    private String cifno;
    private String accnbr;
}

순서 3:

다음으로 네이티브 쿼리를 삽입할 저장소를 만듭니다.

public interface CustomerModelReporsitoy  extends JpaRepository<CustomerEntity,String> {

    @Query(
            value = "SELECT cifno,accnbr FROM institutedb.customerWHERE id = 100200",
            nativeQuery = true
    )
    List<CustomerProjection> findCifByUserName();
}

순서 4:

테스트 목적으로 get 매핑을 만들고 repo 메서드에 액세스합니다.

@GetMapping("/custmodel")
    public String getCifModel(){

        ModelMapper modelMapper = new ModelMapper();

        List<CustomerProjection> collect = customerModelReporsitoy.findCifByUserName().stream().map(customerProjection -> modelMapper.map(customerProjection, CustomerProjection.class))
                .collect(Collectors.toList());

        collect.stream().forEach((cust)-> {
            System.out.println(cust.getCifno());
            System.out.println(cust.getAccnbr());
        });



        return "cif";
    }

목록 내의 엔티티가 아닌 DTO를 사용하는 경우 인터페이스를 생성하여 목록 내의 DTO로 대체하면 됩니다.

예:

@Query(value = "select id, age, name FROM Person WHERE age=?1", nativeQuery=true)
List<PersonView> getPersonsByAge(int age);

및 인터페이스

public interface PersonView {
    Long getId();
    Integer getAge();
    String getName();
}

언급URL : https://stackoverflow.com/questions/46083329/no-converter-found-capable-of-converting-from-type-to-type

반응형