Fáb*_*hos 6 java spring hibernate spring-data-jpa spring-boot
I'm trying to retrieve a timestamp date from an oracle database but the code is throwing:
java.lang.IllegalArgumentException: Projection type must be an interface!
I'm trying to use native query because the original query is way to complex to use Spring JPA methods or JPQL.
My code is similar to this one below (Sorry, can't paste the original one due to company policy).
Entity:
@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;
}
Run Code Online (Sandbox Code Playgroud)
投影:
public interface UserProjection {
String getUserName();
ZonedDateTime getCreatedDate();
}
Run Code Online (Sandbox Code Playgroud)
仓库:
@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);
}
Run Code Online (Sandbox Code Playgroud)
我正在使用Spring Boot 2.1.3和Hibernate 5.3.7。
Rod*_*eas 17
我有一个非常相似的投影同样的问题:
public interface RunSummary {
String getName();
ZonedDateTime getDate();
Long getVolume();
}
Run Code Online (Sandbox Code Playgroud)
我不知道为什么,但问题出在ZonedDateTime. 我将 的类型切换getDate()为java.util.Date,异常消失了。在事务之外,我将 Date 转换回 ZonedDateTime 并且我的下游代码不受影响。
我不知道为什么这是一个问题;如果我不使用投影,则 ZonedDateTime 开箱即用。我同时将此作为答案发布,因为它应该能够作为一种解决方法。
根据Spring-Data-Commons 项目上的这个错误,这是由于在投影中添加对可选字段的支持而导致的回归。(显然,这实际上并不是由其他修复引起的——因为该其他修复是在 2020 年添加的,并且这个问题/答案早于它。)无论如何,它已在 Spring-Boot 2.4.3 中标记为已解决。
基本上,您不能在投影中使用任何 Java 8 时间类,只能使用较旧的基于日期的类。我在上面发布的解决方法将解决 2.4.3 之前的 Spring-Boot 版本中的问题。
Nic*_*nia 10
当您从投影接口调用方法时,spring 会获取它从数据库接收到的值并将其转换为该方法返回的类型。这是通过以下代码完成的:
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);
}
Run Code Online (Sandbox Code Playgroud)
在getCreatedDate方法的情况下,您想java.time.ZonedDateTime从java.sql.Timestamp. 并且由于ZonedDateTime不是集合或数组(if1),不是映射(if2)并且 spring 没有注册转换器(if3) from Timestampto ZonedDateTime,它假设该字段是另一个嵌套投影(else),那么这不是这种情况,你会得到一个例外。
有两种解决方案:
public class TimestampToZonedDateTimeConverter implements Converter<Timestamp, ZonedDateTime> {
@Override
public ZonedDateTime convert(Timestamp timestamp) {
return ZonedDateTime.now(); //write your algorithm
}
}
Run Code Online (Sandbox Code Playgroud)
@Configuration
public class ConverterConfig {
@EventListener(ApplicationReadyEvent.class)
public void config() {
DefaultConversionService conversionService = (DefaultConversionService) DefaultConversionService.getSharedInstance();
conversionService.addConverter(new TimestampToZonedDateTimeConverter());
}
}
Run Code Online (Sandbox Code Playgroud)
由于版本 2.4.0 spring创建了一个新 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());
}
}
Run Code Online (Sandbox Code Playgroud)
您已将userId字段声明为Long实体中的字段,但UserProjection getUserId方法返回类型为String。这是不匹配的所以改变
String getUserId();
到
Long getUserId();
| 归档时间: |
|
| 查看次数: |
779 次 |
| 最近记录: |