봄 JPA - "java.lang."잘못된 인수예외:투영 유형은 인터페이스여야 합니다!"(네이티브 쿼리 사용)
Oracle 데이터베이스에서 타임스탬프 날짜를 검색하려고 하는데 코드가 느려집니다.
자바.java.java잘못된 인수예외:투영 유형은 인터페이스여야 합니다!
원래 쿼리는 스프링 JPA 메소드나 JPQL을 사용하기에는 너무 복잡하기 때문에 네이티브 쿼리를 사용하려고 합니다.
제 코드는 아래 코드와 유사합니다(죄송하지만 회사 정책상 원본을 붙여넣을 수 없습니다).
엔티티:
@Getter
@Setter
@Entity(name = "USER")
public class User {
@Column(name = "USER_ID")
private Long userId;
@Column(name = "USER_NAME")
private String userName;
@Column(name = "CREATED_DATE")
private ZonedDateTime createdDate;
}
투영:
public interface UserProjection {
String getUserName();
ZonedDateTime getCreatedDate();
}
리포지토리:
@Repository
public interface UserRepository extends CrudRepository<User, Long> {
@Query(
value = " select userName as userName," +
" createdDate as createdDate" +
" from user as u " +
" where u.userName = :name",
nativeQuery = true
)
Optional<UserProjection> findUserByName(@Param("name") String name);
}
Spring Boot 2.1.3과 Hibernate 5.3.7을 사용하고 있습니다.
저는 매우 유사한 예상과 관련하여 동일한 문제가 있었습니다.
public interface RunSummary {
String getName();
ZonedDateTime getDate();
Long getVolume();
}
이유는 모르겠지만, 문제는ZonedDateTime
의 유형을 변경했습니다.getDate()
로.java.util.Date
그리고 예외는 사라졌습니다.트랜잭션 외부에서 Date를 ZoneDateTime으로 다시 변환했지만 다운스트림 코드는 영향을 받지 않았습니다.
프로젝션을 사용하지 않으면 ZoneDateTime이 즉시 작동합니다.그 사이에 해결책이 될 수 있을 것 같아서 답변으로 글을 올립니다.
Spring-Data-Commons 프로젝트의 이 버그에 따르면, 이것은 투영에서 선택적 필드에 대한 지원을 추가함으로써 발생한 회귀입니다. (분명히 다른 수정에 의해 실제로 발생한 것은 아닙니다. 왜냐하면 2020년에 다른 수정이 추가되었기 때문이고 이 질문/답변은 오래 전입니다.)그럼에도 불구하고 Spring-Boot 2.4.3에서 해결된 것으로 표시되었습니다.
기본적으로, 당신은 당신의 투영에 Java 8 시간 클래스를 사용할 수 없었고, 오직 이전 날짜 기반 클래스만 사용할 수 있었습니다.위에 올린 해결 방법은 2.4.3 이전의 Spring-Boot 버전에서 이 문제를 해결할 것입니다.
투영 인터페이스에서 메서드를 호출하면 스프링이 데이터베이스에서 수신한 값을 가져와 메서드가 반환하는 유형으로 변환합니다.이 작업은 다음 코드로 수행됩니다.
if (type.isCollectionLike() && !ClassUtils.isPrimitiveArray(rawType)) { //if1
return projectCollectionElements(asCollection(result), type);
} else if (type.isMap()) { //if2
return projectMapValues((Map<?, ?>) result, type);
} else if (conversionRequiredAndPossible(result, rawType)) { //if3
return conversionService.convert(result, rawType);
} else { //else
return getProjection(result, rawType);
}
의 경우에는getCreatedDate
원하는 방법java.time.ZonedDateTime
부터java.sql.Timestamp
그리고 그 이후로ZonedDateTime
컬렉션 또는 배열(if1)이 아니며 지도(if2)가 아니며 스프링에는 다음과 같은 변환기(if3)가 없습니다.Timestamp
로.ZonedDateTime
이 필드가 다른 중첩 투영(접미사)이라고 가정하면 이 필드는 해당되지 않으며 예외가 발생합니다.
두 가지 솔루션이 있습니다.
- 타임스탬프를 반환한 다음 수동으로 ZoneDateTime으로 변환
- 컨버터 생성 및 등록
public class TimestampToZonedDateTimeConverter implements Converter<Timestamp, ZonedDateTime> {
@Override
public ZonedDateTime convert(Timestamp timestamp) {
return ZonedDateTime.now(); //write your algorithm
}
}
@Configuration
public class ConverterConfig {
@EventListener(ApplicationReadyEvent.class)
public void config() {
DefaultConversionService conversionService = (DefaultConversionService) DefaultConversionService.getSharedInstance();
conversionService.addConverter(new TimestampToZonedDateTimeConverter());
}
}
Spring Boot 2.4.0 업데이트:
버전 2.4.0 이후 스프링은 새로운 기능을 생성합니다. DefaultConversionService
을 통해 얻는 대신 객체getSharedInstance
반사를 이용하는 것 외에는 그것을 얻는 적절한 방법을 알지 못합니다.
@Configuration
public class ConverterConfig implements WebMvcConfigurer {
@PostConstruct
public void config() throws NoSuchFieldException, ClassNotFoundException, IllegalAccessException {
Class<?> aClass = Class.forName("org.springframework.data.projection.ProxyProjectionFactory");
Field field = aClass.getDeclaredField("CONVERSION_SERVICE");
field.setAccessible(true);
GenericConversionService service = (GenericConversionService) field.get(null);
service.addConverter(new TimestampToZonedDateTimeConverter());
}
}
열 유형을 원하는 특성 유형에 매핑하기 위해 새 특성 변환기를 만들 수 있습니다.
@Component
public class OffsetDateTimeTypeConverter implements
AttributeConverter<OffsetDateTime, Timestamp> {
@Override
public Timestamp convertToDatabaseColumn(OffsetDateTime attribute) {
//your implementation
}
@Override
public OffsetDateTime convertToEntityAttribute(Timestamp dbData) {
return dbData == null ? null : dbData.toInstant().atOffset(ZoneOffset.UTC);
}
}
프로젝션에서는 아래와 같이 사용할 수 있습니다.이것은 컨버터를 호출하는 명시적인 방법입니다.못해서 가 없습니다.@Value
필요할 때마다 주석을 추가합니다.
@Value("#{@offsetDateTimeTypeConverter.convertToEntityAttribute(target.yourattributename)}")
OffsetDateTime getYourAttributeName();
Spring Boot v2.4.2에서도 동일한 문제가 있었습니다.
저는 저를 위해 이 추악한 해킹을 썼습니다.
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.EventListener;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.data.convert.Jsr310Converters;
import org.springframework.data.util.NullableWrapperConverters;
@Configuration
public class JpaConvertersConfig {
@EventListener(ApplicationReadyEvent.class)
public void config() throws Exception {
Class<?> aClass = Class.forName("org.springframework.data.projection.ProxyProjectionFactory");
Field field = aClass.getDeclaredField("CONVERSION_SERVICE");
field.setAccessible(true);
Field modifiers = Field.class.getDeclaredField("modifiers");
modifiers.setAccessible(true);
modifiers.setInt(field, field.getModifiers() & ~Modifier.FINAL);
DefaultConversionService sharedInstance = ((DefaultConversionService) DefaultConversionService.getSharedInstance());
field.set(null, sharedInstance);
Jsr310Converters.getConvertersToRegister().forEach(sharedInstance::addConverter);
NullableWrapperConverters.registerConvertersIn(sharedInstance);
}
}
은 선습니다를 했습니다.userId
로서 필드에.Long
도 본질로에 .UserProjection
getUserId
은 다음과 같습니다.String
Change 일치항목로므이변경않는지하▁which변.
String getUserId();
로.
Long getUserId();
나는 받지 못했습니다.interface
에서 지원되는지 여부를 확신하지 못하고 작동합니다.ZonedDateTime
비록 제 마음속에 지지하지 않을 이유는 없지만요.
이를 위해 저는 프로젝션과 함께 사용하는 클래스를 만들었습니다(물론 이 인터페이스는 구현할 수 있지만 단순성을 위해 생략했습니다).
@Getter
@Setter
@AllArgsConstructor
public class UserProjection {
private String userName;
private ZonedDateTime createdDate;
}
쿼리에서 다음과 같이 NEW 연산자를 사용하기 때문에 이를 위해서는 JPQL이 필요합니다.
@Query(value = " SELECT NEW org.example.UserProjection(U.userName, U.createdDate) "
+ " FROM USER U " // note that the entity name is "USER" in CAPS
+ " WHERE U.userName = :name ")
스프링 데이터 jpa의 문제는 일부 유형을 데이터베이스에서 Java 유형으로 변환할 수 없습니다.결과와 데이터베이스 반환 번호로 부울을 얻으려고 했을 때 거의 같은 문제가 있었습니다.
자세한 내용은 다음 사이트를 참조하십시오. https://github.com/spring-projects/spring-data-commons/issues/2223 https://github.com/spring-projects/spring-data-commons/issues/2290
언급URL : https://stackoverflow.com/questions/55247358/spring-jpa-java-lang-illegalargumentexception-projection-type-must-be-an-int
'programing' 카테고리의 다른 글
Firebase 프로젝트의 지원 이메일을 변경하려면 어떻게 해야 합니까? (0) | 2023.07.13 |
---|---|
라이브러리가 로드되지 않음: @rpath/FBLPromises.framework/FBLP는 iOS 13.3.1을 약속합니다. (0) | 2023.07.13 |
Oracle SQL Developer에서 저장되지 않은 SQL 쿼리 스크립트 복구 (0) | 2023.07.13 |
몽구스: "_doc"는 무슨 일입니까? (0) | 2023.07.13 |
두 날짜 범위 내에서 날짜를 얻는 방법은 무엇입니까? (0) | 2023.07.08 |