Spring JPA:如何禁用某些方法的查询创建

9

我有一个自定义的存储库,如下所示(用 Kotlin 编写):

interface FooRepository : JpaRepository<Foo, Int> {
    fun findByFoo(foo: String): List<Foo>

    fun findByBar(bar: String): List<Foo> {
        //custom implementation
    }
}

data class Foo(var id: Int, var foo: String, var bar: String)

两种方法都符合JPA存储库的命名约定,但我想自己实现第二种方法(FooRepository.findByBar)。如何防止JPA为其创建查询?请注意,我的自定义实现涉及计算逻辑,因此允许自定义查询的@Query注释不符合我的要求。此外,在实际情况中,这是必要且合理的,因此,如果您试图提出“更好”的设计模式,如将实现放置在服务层中等,请不要发表您的答案或评论。

1
如果您想禁用查询创建,则可以打破Spring的命名约定,将方法命名为searchByBarfetchByBar,而不是findByBar - Nikolai Shevchenko
4
谢谢你的解决方案,@NikolaiShevchenko,但可惜它没有起作用。当我将其重命名为“fetchByBar”时,我仍然收到了一个“QueryCreationException”错误,显示:“在类型'Foo'中找不到属性'fetchByBar'!” - 0x269
也许这个链接 https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.customize-base-repository 可以提供一个解决方案?在那里创建一个自定义方法,它仍然具有一些上下文(至少是SimpleJpaRepository的上下文),并且在接口中通过签名“引用”时使用?虽然我没有尝试过,但我不喜欢它,因为它会为整个应用程序强制实施一个单一的基本存储库。我面临着与原始问题相同的情况,可能只会采用*CustomImpl方法并在那里重新创建基本功能。 - icyerasor
1
OP,@icyerasor,我发了另一个答案在 Kotlin 中可行,允许任意方法名,并且不需要另一个接口,这样你就不会失去上下文。 - Bruno Medeiros
3个回答

1
如果你想让你的存储库自定义方法只是委托给另一个方法,那么在接口中编写一个非抽象方法是正确的做法。
然而,在Kotlin中,你需要添加"-Xjvm-default=all"编译参数,否则Spring会认为你的方法没有实现体,并且初始化将失败。
我今天在一个Spring/Kotlin项目中遇到了同样的问题,这似乎是一个很简单的用例,我想知道为什么没有更多的人抱怨。
事实证明,这是Kotlin默认处理接口中非抽象方法的一个特殊情况!
大家推荐的解决方案是创建一个单独的接口和实现来处理自定义方法,但如果你最终想要委托给一个自动生成的方法,这种方法并不真正有效。
由于我需要我的findByBar等效方法进行委托,所以我首先尝试在接口方法中写入一个体,这在Kotlin中完全支持,并最终委托给一个自动生成的方法。令我惊讶的是,Spring仍然崩溃,说我需要添加一个@Query注解。
然后,在看到其他一些人成功地在Java中使用了default方法之后(例如12),我也尝试了一下。我在Java中创建了相同的东西,并在接口中使用了一个default方法,结果是成功的!
由于Kotlin必须和Java一样强大,所以只是时间问题,直到我发现我只需要使用-Xjvm-default=all这个编译器参数(完整问题历史请查看这里),这样我的非抽象方法就会正确生成为一个default接口方法。

你救了我的一天 <3。对我来说,这个方法非常有效。这应该是被接受的答案。 - Hermann.Gruber

0

您可以简单地创建另一个名为“FooRepositoryCustom”的接口,并创建一个实现类(FooRepositoryCustomImpl)来对该FooRepositoryCustomImpl类进行实现,然后将FooRepositoryCustom接口扩展到您的FooRepository接口。

FooRepositoryCustom:

public interface FooRepositoryCustom {
    fun findByBar(bar: String): List<Foo>;
}

FooRepositoryCustomImpl:

public class FooRepositoryCustomImpl implements FooRepositoryCustom {
    @Override
    public fun findByBar(bar: String): List<Foo> {
          //your implementation goes here
    }
}

FooRepository:

interface FooRepository : JpaRepository<Foo, Int>, FooRepositoryCustom {

}

5
谢谢您的回答。这个解决方案是有效的,但是我的自实现方法会失去JpaRepository<Foo, Int>的上下文,这并不是我想要的。 - 0x269

0

最佳实践:正如Heshan所建议的那样,您可以将自定义接口添加到存储库接口中。您的实现将被加载(文件名带有“Impl”后缀),并且它将覆盖同名的默认方法。

扩展JpaRepository:为什么要扩展JpaRepository接口?如果这样做,Spring将尝试自行创建存储库实例。为了防止这种情况,请使用@NoRepositoryBean注释您的存储库接口

@NoRepositoryBean
public interface FooRepositoryCustom extends JpaRepository<Foo, Int> {
}

参考:我正在尝试使用Spring Data Elasticsearch来做类似的事情 - 覆盖现有的存储库方法。我遵循了他们的文档,请参见https://docs.spring.io/spring-data/elasticsearch/docs/4.1.15/reference/html/#repositories.custom-implementations


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接