【问题标题】:Querying an entity by values in an ElementCollections using Spring Data JpaRepositories使用 Spring Data JpaRepositories 通过 ElementCollections 中的值查询实体
【发布时间】:2019-02-15 15:08:50
【问题描述】:

是否可以使用查询方法根据存储在附加的@ElementCollection 中的值来查询 JpaRepository 中的实体?

设置

我的 Spring Boot 2 / Spring 5 应用程序有一个实体 (Artwork) 可以附加任意元数据。 元数据是通过使用@ElementCollection 和@CollectionTable 的简单键值映射实现的,因为元数据只是纯文本键/值对并且不存在于其实体范围之外。

实体如下所示:

@Entity
@Table(name = "artwork")
public class Artwork implements Serializable {
    @Id
    @GeneratedValue(strategy = IDENTITY)
    private Long id;

    // more propperties 

    @ElementCollection
    @MapKeyColumn(name = "name")
    @Column(name = "value")
    @CollectionTable(name = "artwork_metadata", joinColumns = @JoinColumn(name = "artwork_id"))
    private Map<String, String> metadata = new HashMap<>();

    // more code
}

问题

我有两个用例,我需要根据元数据映射中存储的信息查询实体。

  • 一个用例是查找所有具有许可证的Artworks(具有metadata.name = license,无论值列中的内容是什么)
  • 另一个是查找一位特定艺术家的所有实体(具有精确的键/值对 metadata.name = artist 和 metadata.value = someName)

我尝试使用下划线符号来进行手动属性路径描述;但我的数据类型是一个地图,而不是真正的具有字段name/value 的对象。

public interface ArtworkRepository extends JpaRepository<Artwork, Long> {
    List<Artwork> findAllByReleaseDateAfter(Instant after);

    // Not working
    List<Artwork> findAllByMetadata_NameAndMetadata_value 

一般来说查询似乎是可能的。但我在 StackOverflow 上找到的大多数答案都是关于 searching in Lists,而不是地图。

所以我的问题是

如何使用查询方法根据存储在附加的@ElementCollection 中的值查询 JpaRepository 中的实体?或者我是否需要使用 JPA 的一对多映射在对象级别转换这种关系。

更新

  • 由于我们在艺术家表中有数万条记录,我想在数据库级别而不是在应用程序中进行过滤。
  • 通过元数据进行查询将变得普遍,因此引入hasLicense 之类的辅助标志或将艺术家移至主要实体不是一种选择。

谢谢。

【问题讨论】:

  • 获取应用级别的所有过滤器?
  • 这不会有效率,因为我们有几万条记录。
  • 考虑JPA主要适用于CRUD。如果您的标准可以根据未来的需求而发展,其他方法可能会变得更合适(例如,用于元数据的 ElasticSearch + 用于实体的 DB)。如果您在 JPA 实体中引入“hasLicense”列,则可以解决第一个用例,并且获取速度会很快,但是在元数据中搜索匹配项 - 如果您想将其保存在数据库中,这应该是带有 FK 的单独表到主要实体。
  • 谢谢,但我不打算过早优化。元数据位于带有外键的单独表中,如上所述,我想根据此信息进行查询。

标签: java spring spring-data-jpa spring-data


【解决方案1】:

JPA 2.0 允许使用 KEY() 和 VALUE() 来引用 JPQL 中基于映射的 @ElementCollection 中的键和值。

通常,您可以使用它们来解决您的问题。但是,正如this 中提到的,对于VALUES(),Hibernate 似乎是一种奇怪的行为……幸运的是,它有workaround。

无论如何,您的问题可以通过以下@Query 解决(假设您使用的是Hibernate。如果您使用其他提供程序,您可以尝试VALUES())。 :

public interface ArtworkRepository extends JpaRepository<Artwork, Long> {

    @Query(value = "select a from Artwork a join a.metadata meta where (KEY(meta) = :name)")
    public List<Artwork> findArtworkByMetadata(@Param("name") String name);

    @Query(value = "select a from Artwork a join a.metadata meta where (KEY(meta) = :name and meta = :value)")
    public List<Artwork> findArtworkByMetadata(@Param("name") String name, @Param("value") String value);

}

要查找所有具有许可证的艺术品:

artworkRepository.findArtworkByMetadata("license");

要查找特定艺术家的所有艺术品:

artworkRepository.findArtworkByMetadata("artist" , "someName");

【讨论】:

  • 谢谢,这看起来很有希望。我假设,VALUES() 在(KEY(meta) = :name and meta = :value) 中是可选的?目前我遇到了一些惰性初始化问题,但是生成的 sql 看起来不错。
  • 在 (KEY(meta) = :name and meta = :value) 中,我没有使用 VALUES(),因为正如我提到的那样,它在 Hibernate 中存在问题。
猜你喜欢
  • 2019-01-07
  • 2015-08-07
  • 2015-02-21
  • 2021-02-13
  • 2021-03-17
  • 2016-05-08
  • 1970-01-01
  • 2015-03-20
  • 1970-01-01
相关资源
最近更新 更多