Jus*_*ion 7 java hibernate spring-data-jpa kotlin spring-boot
在我的存储库方法中使用 List 类型的参数时,我遇到不稳定的错误。该问题似乎与 Kotlin 列表和 Java 之间的互操作性有关。当我运行单元测试时,它随机失败,我怀疑这与 Kotlin 中 listOf() 返回的 MutableList 有关。更多信息可以在 StackOverflow 帖子中找到:listOf() returns MutableList。
这是我的单元测试和存储库方法的示例:
@Test
fun `should return recipes with the specified author, locale and categories`() {
println("AuthorID: " + author.id)
recipeRepo.findRecipesBy(
locale = LanguageSelection.ENGLISH,
authorIds = arrayListOf(44),
)
// it throws before reaching asserts
}
Run Code Online (Sandbox Code Playgroud)
存储库:
interface RecipeRepository : JpaRepository<Recipe, Long> {
@Query("SELECT r FROM Recipe r WHERE "
+ "(:authorIds is null or r.author.id in (:authorIds)) "
+ "and (:recipeIds is null or r.id in (:recipeIds)) "
+ "and (:minPrice is null or r.estimatedPrice >= :minPrice) "
+ "and (:maxPrice is null or r.estimatedPrice <= :maxPrice) "
+ "and ( cast(:beforeDate as timestamp) is null or r.createdAt < cast(:beforeDate as timestamp)) "
+ "and ( cast(:afterDate as timestamp) is null or r.createdAt > cast(:afterDate as timestamp)) "
+ "and (:minLikeCount is null or r.likeCount >= :minLikeCount) "
+ "and (:categoryIds is null or exists(select rc from r.categories rc where rc.id in (:categoryIds))) "
)
fun findRecipesBy(
@Param("authorIds") authorIds: ArrayList<Long>? = null,
@Param("recipeIds") recipeIds: ArrayList<Long>? = null,
@Param("minPrice") minPrice: Double? = null,
@Param("maxPrice") maxPrice: Double? = null,
@Param("beforeDate") beforeDate: Date? = null,
@Param("afterDate") afterDate: Date? = null,
@Param("minLikeCount") minLikeCount: Int? = null,
@Param("categoryIds") categoryIds: ArrayList<Long>? = null,
sort: Sort = Sort.by(Sort.Direction.ASC, "createdAt"),
): List<Recipe>
}
Run Code Online (Sandbox Code Playgroud)
错误:
Parameter value [[44]] did not match expected type [BasicSqmPathSource(id : Long) ]
org.springframework.dao.InvalidDataAccessApiUsageException: Parameter value [[44]] did not match expected type [BasicSqmPathSource(id : Long) ]
at app//org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:371)
at app//org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:235)
at app//org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:550)
at app//org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61)
at app//org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:242)
at app//org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:152)
at app//org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at app//org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:134)
at app//org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at app//org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97)
at app//org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at app//org.springframework.data.repository.core.support.MethodInvocationValidator.invoke(MethodInvocationValidator.java:94)
at app//org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at app//org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:218)
at app/jdk.proxy3/jdk.proxy3.$Proxy189.findRecipesBy(Unknown Source)
at app//com.fittastetic.fittastetic_backend.shared.recipe.repository.RecipeRepository$DefaultImpls.findRecipesBy$default(RecipeRepository.kt:38)
...
Caused by: org.hibernate.type.descriptor.java.CoercionException: Cannot coerce value `[44]` [java.util.ArrayList] as Long
at app//org.hibernate.type.descriptor.java.LongJavaType.coerce(LongJavaType.java:155)
at app//org.hibernate.type.descriptor.java.LongJavaType.coerce(LongJavaType.java:24)
at app//org.hibernate.query.internal.QueryParameterBindingImpl.coerce(QueryParameterBindingImpl.java:144)
at app//org.hibernate.query.internal.QueryParameterBindingImpl.setBindValue(QueryParameterBindingImpl.java:111)
... 141 more
Run Code Online (Sandbox Code Playgroud)
由于 Hibernate 团队在 HBN6 中完全重新设计了查询模型,因此预计某些东西已经停止工作,特别是对于像您这样的情况,它实际上看起来像是黑客/技巧。这里我建议到HBN论坛提问。
但是,确实存在一些解决方法:
I. 实现此类查询的惯用方法是使用 jpa 规范,例如:
public interface RecipeRepository extends JpaRepository<Recipe, Long>, JpaSpecificationExecutor<Recipe> {
default List<Recipe> findRecipesBy(List<Long> authorIds, List<Long> recipeIds) {
Specification<Recipe> specification = (root, cq, cb) -> {
List<Predicate> predicates = new ArrayList<>();
if (authorIds != null && !authorIds.isEmpty()) {
predicates.add(root.get("author").get("id").in(authorIds));
}
if (recipeIds != null && !recipeIds.isEmpty()) {
predicates.add(root.get("id").in(recipeIds));
}
return cb.and(predicates.toArray(new Predicate[0]));
};
return findAll(specification);
}
}
Run Code Online (Sandbox Code Playgroud)
二. 技巧coalesce- 似乎仅适用于 PostgreSQL:
@Query("SELECT r FROM Recipe r WHERE "
+ "(coalesce(:authorIds) is null or r.author.id in (:authorIds)) "
+ "and (coalesce(:recipeIds) is null or r.id in (:recipeIds)) "
+ "and (:minPrice is null or r.estimatedPrice >= :minPrice) "
+ "and (:maxPrice is null or r.estimatedPrice <= :maxPrice) "
+ "and ( cast(:beforeDate as timestamp) is null or r.createdAt < cast(:beforeDate as timestamp)) "
+ "and ( cast(:afterDate as timestamp) is null or r.createdAt > cast(:afterDate as timestamp)) "
+ "and (:minLikeCount is null or r.likeCount >= :minLikeCount) "
+ "and (coalesce(:categoryIds) is null or exists(select rc from r.categories rc where rc.id in (:categoryIds))) "
)
Run Code Online (Sandbox Code Playgroud)
三.SpEL 的技巧 - 将 null 检查委托给 SpEL:
@Query("SELECT r FROM Recipe r WHERE "
+ "(:#{#authorIds == null} = true or r.author.id in (:authorIds)) "
+ "and (:#{#recipeIds == null} = true or r.id in (:recipeIds)) "
+ "and (:minPrice is null or r.estimatedPrice >= :minPrice) "
+ "and (:maxPrice is null or r.estimatedPrice <= :maxPrice) "
+ "and ( cast(:beforeDate as timestamp) is null or r.createdAt < cast(:beforeDate as timestamp)) "
+ "and ( cast(:afterDate as timestamp) is null or r.createdAt > cast(:afterDate as timestamp)) "
+ "and (:minLikeCount is null or r.likeCount >= :minLikeCount) "
+ "and (:#{#categoryIds == null} = true or exists(select rc from r.categories rc where rc.id in (:categoryIds))) "
)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
4097 次 |
| 最近记录: |