Spring JPA-具有示例/探针的findAll(),包括相关/已加入实体

时间:2019-06-04 14:53:30

标签: java spring hibernate jpa

如何使用Spring JPA的Query by Example不仅可以查询实体本身,还可以使用findAll()查询相关实体的属性?当我们在探针/示例实体上设置相关实体​​属性时,我们所有的尝试似乎都只是忽略它们。

文档指出:

  

属性说明符接受属性名称(例如名字和姓氏)。您可以通过将属性与点(address.city)链接在一起进行导航。您还可以使用匹配选项和区分大小写对其进行调整。

但是,没有任何示例显示链接应该如何工作,并且我们尝试使用链接没有成功。

设计示例

假定数据库结构具有多对多关系:

  • 表格:图书

    • id(PK,INT)
    • 标题(varchar)
    • ...
  • 表:类别

    • id(PK,INT)
    • 名称
    • ...
  • 表格:Book_Category

    • book_id
    • category_id

Book.java

@Data
@Entity
public class Book {
    public Book () {}
    public Book(String title, List<Category> categories) {
        this.title = title;
        this.categories = categories;
    }

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    @NotNull
    private String title;

    @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
    @JoinTable(
        name = "book_category",
        joinColumns = {@JoinColumn(name = "book_id")},
        inverseJoinColumns = {@JoinColumn(name = "category_id")}
    )
    private List<Category> categories;
}

BookRepository.java

@Repository
public class BookRepository extends JpaRepository<Book, long> {

}

Category.java

@Data
@Entity
public class Category {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    @NotNull
    private String name;
}

CategoryRepository.java

@Repository
public class CategoryRepository extends JpaRepository<Category, long> {

}

BookService.java

public class BookService {
    @Autowired
    private BookRepository bookRepository;

    @Autowired
    private CategoryRepository categoryRepository;

    public List<Book> findByExample(String title, String category) {
        ExampleMatcher matcher = ExampleMatcher.matchingAll()
            .withMatcher("title", match -> match.contains().ignoreCase()) 
// ### This is (probably?) the bit that's wrong - none of these made any difference        
            //.withMatcher("categories.id", match -> match.contains()) 
            //.withMatcher("categories.name", match -> match.contains().ignoreCase()) 
            //.withMatcher("categories", match -> match.contains()) 
// ###
            .withIgnoreNullValues() // ignore unset properties when finding
            .withIgnorePaths("id"); // ignore primitives as they default to 0

        List<Category> matchingCategories = categoryRepository.findAllByName(category);
        Example<Book> example = Example.of(new Book(
            title, matchingCategories), matcher);

        return bookRepository.findAll(example)
    }
}

调用BookService.findByExample(...)会根据标题正确过滤,但完全忽略该类别。 “真实的”示例更为复杂,但这提炼了我们遇到的问题。我们该如何过滤相关表和基础表?

1 个答案:

答案 0 :(得分:0)

这仅适用于ToOne关系,不适用于您的情况下的ToMany:

  

属性说明符接受属性名称(例如名字和   姓)。您可以通过将属性和点链接在一起来进行导航   (地址。城市)。您还可以使用匹配的选项和大小写对其进行调整   敏感性。

我还查看了Spring Data源代码,这里是:

for (SingularAttribute attribute : type.getSingularAttributes()) {

因此,它仅使用SingularAttribute之类的原语,String和ToOne关系。

所以我很害怕,但是您试图实现的目标似乎是不可能的。