【问题标题】:JPA - How to query by joined entities?JPA - 如何通过连接实体查询?
【发布时间】:2019-04-12 22:54:13
【问题描述】:

我编写了以下查询以在 Spring JpaRespository 中使用:

private const val SEARCH  =
                "SELECT vf " + 
                "FROM VideoFile vf " +
                "LEFT JOIN vf.stars st " +
                "LEFT JOIN vf.categories c " +
                "LEFT JOIN vf.series se " +
                "WHERE (:searchText IS NULL OR (" +
                "vf.fileName LIKE :searchText OR vf.displayName LIKE :searchText OR vf.description LIKE :searchText)) " +
                "AND (:seriesId IS NULL OR se.seriesId = :seriesId) " +
                "AND (:starId IS NULL OR st.starId = :starId) " +
                "AND (:categoryId IS NULL OR c.categoryId = :categoryId)"

这是基于我之前测试过的 SQL 查询,但使用 JPQL 编写。我将在下面包含我的实体(Kotlin),以便我将要解释的内容更有意义:

@Entity
@Table(name = "video_files")
data class VideoFile(
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        var fileId: Long = 0,
        @Column(unique = true)
        var fileName: String = "",
        var displayName: String = "",
        var description: String = "",
        var lastModified: LocalDateTime = DEFAULT_TIMESTAMP,
        @JsonIgnore
        var lastScanTimestamp: LocalDateTime = DEFAULT_TIMESTAMP,
        @Column(columnDefinition = "int default 0")
        var viewCount: Int = 0,

        @ManyToMany (fetch = FetchType.EAGER)
        @JoinTable(name = "file_categories",
                joinColumns = [JoinColumn(name = "file_id")],
                inverseJoinColumns = [JoinColumn(name = "category_id")])
        var categories: Set<Category> = HashSet(),

        @ManyToMany (fetch = FetchType.EAGER)
        @JoinTable(name = "file_series",
                joinColumns = [JoinColumn(name = "file_id")],
                inverseJoinColumns = [JoinColumn(name = "series_id")])
        var series: Set<Series> = HashSet(),

        @ManyToMany (fetch = FetchType.EAGER)
        @JoinTable(name = "file_stars",
                joinColumns = [JoinColumn(name = "file_id")],
                inverseJoinColumns = [JoinColumn(name = "star_id")])
        var stars: Set<Star> = HashSet()
)

因此,我的 VideoFile 实体与 Category、Series 和 Star 实体之间存在多对多关系。我正在编写的查询是尝试获取与某些类别、系列、明星等连接的所有 VideoFile。

我现在的查询返回重复记录,我相信是因为左连接。因为 VideoFile 与多个类别匹配,所以我多次返回相同的记录。

我精通 SQL,但我还是 JPQL 的新手。我希望能得到一些帮助来重构它,以便它只按加入的实体过滤 VideoFiles,而不是返回额外的记录。

【问题讨论】:

    标签: spring jpa kotlin


    【解决方案1】:

    首先,在查询中检查传递变量的“可空性”可能不是一个好主意,老实说,我不知道 JPQL 是否支持类似的东西。

    另外,由于您使用的是 JPQL,请确保您将该查询作为 JPQL 查询执行,并且您不需要连接实体(至少对于简单的左连接不需要),因为它们已经模型中相关。

    所以你的查询应该是这样的:

    private const val SEARCH  =
                    "SELECT vf " + 
                    "FROM VideoFile vf " +
                    "WHERE (vf.categories.id =:categoryId) " +
                    "AND (vf.fileName LIKE :searchText OR vf.displayName LIKE :searchText OR vf.description LIKE :searchText) " +
                    "AND (vf.series.id = :seriesId)  " +
                    "AND (vf.stars.id = :starId) "   
    

    只需确保类别实体中的 id 属性命名为“id”即可。
    同样,请确保将类别中的名称属性命名为“名称”等......你明白了。

    最后,您应该尝试使用空值的不同方法,例如,我想您永远不需要检查实体的 id 属性是否存在空值。

    因此,执行此操作的一种方法是使用 StringBuilder 并检查 Java/Kotlin 级别是否参数为空,如果为空,并且您确定要获取字段的空条目,则添加生成器的额外字符串,即OR vf.categories.name IS NULL

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-28
      • 2019-01-07
      相关资源
      最近更新 更多