【问题标题】:Hibernate criteria filter by related entity按相关实体过滤休眠条件
【发布时间】:2013-01-03 13:22:57
【问题描述】:

我有一个包含相关实体集合的实体。

public class Student{

@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JoinColumn(name = "COURSE_STUDENT_ID" , insertable=false,updatable=false)
private Set <Course> courses;

我想按课程名称和学生班级 ID 过滤学生。现在我已经弄清楚了如何按类 id 过滤,但我不知道如何按 courseId 过滤,因为 Student 实体有一组课程并且表是相关的。我已经阅读了一些文章,但没有代码与我已经阅读的文章相匹配。

CriteriaBuilder criteriaBuilder = persistenceStore.createCriteriaBuilder();
CriteriaQuery<Object> criteria = criteriaBuilder.createQuery();
Root<Student> root = criteria.from(Student.class);
List<Predicate> params = new ArrayList<Predicate>();

params.add(criteriaBuilder.equal(root.get("classId"),classId));

Predicate[] predicates = new Predicate[params.size()];
params.toArray(predicates);
criteria.select(root);
criteria.where(criteriaBuilder.and(predicates));
Query query = persistenceStore.createQuery(criteria);
List<Student> resultList = query.getResultList();

【问题讨论】:

  • 欢迎来到stackoverflow。感谢您提出的第一个问题。
  • 不确定标准方式,但是,当我有类似的需求时,我使用查询语言,有关想法,请参见此处:en.wikibooks.org/wiki/Java_Persistence/JPQL(因为语法使用了 SQL 中的基本想法,所以很容易为我使用)。
  • 我明白@PeterButkovic。但我的情况是过滤器是动态的。所以我不能硬编码查询。所以,如果用户想按类过滤,我添加 params.add (criteriaBuilder.equal(root.get("classId"),classId)); classId 没有问题,因为它是 Student 实体本身的属性。当用户想要通过我必须通过相关集合实体获取的 courseName 进行过滤时,就会出现问题。

标签: java hibernate jakarta-ee criteria-api


【解决方案1】:

类型不匹配:无法从 cascadetype 转换为 cascadetype[]

答案是:

导入 javax.persistence.CascadeType;

导入这一行

【讨论】:

    【解决方案2】:

    首先,您的实体中存在错误:JoinColumn 注释适用于关系相反侧的实体,在您的情况下为 Course

    所以,如果 Course 实体有一个属性 student,那么 Student 有一个类似的属性:

    @OneToMany(cascade = CascadeType.ALL, mappedBy = "student")
    private Set<Course> courses;
    

    并且在 Course 实体中你有(这里它还指出在 db 表 course 有一个名为“student”的字段:

    @JoinColumn(name = "student", referencedColumnName = "id")
    @ManyToOne(optional = false)
    private Student student;
    

    阅读this link 了解有关如何映射实体关系的入门级说明。

    关于 Criteria Query,由于您要检索 StudentS 的列表,您可以以更安全的方式定义您的 CriteriaQuery:

    CriteriaQuery<Student> criteria = criteriaBuilder.createQuery(Student.class);
    

    关于这个问题,你必须以这种方式加入表格:

    SetJoin<Student, Course> courses = root.join("courses");
    

    或者,使用 MetaModel:

    SetJoin<Student, Course> courses = root.join(Student_.courses);
    

    (如果 OneToMany 属性被定义为 ListCollection,您将不得不使用相应的 ListJoinCollectionJoin 类)。

    您可以在课程上应用所需的Predicate 条件(假设课程实体有一个名为courseName 的字符串属性):

    Predicate p = criteriaBuilder.equal(courses.get("courseName"), "name-to-look-for");
    

    或者,使用元模型:

    Predicate p = criteriaBuilder.equal(courses.get(Course_.courseName), "name-to-look-for");
    

    最后,为了正确连接谓词列表,您可以使用(至少)两种技术:

    Predicate p1 = ...;
    Predicate p2 = ...;
    
    criteria.where(criteriaBuilder.and(p1, p2));
    

    List<Predicate> conditions = new ArrayList<Predicate> ();
    conditions.add(p1);
    conditions.add(p2);
    criteria.where(conditions.toArray(new Predicate[] {}));
    

    另请参阅此出色的article

    【讨论】:

    • Student_是Metamodel generator生成的类。关于您遇到的错误,它显示 OfferDetails 和 OfferDestinationMapping: 这些是您项目的类吗?你忘了什么吗?相应地编辑您的问题
    • 是的,这些是项目的详细信息。 SetJoin 课程 = root.join("courses");给我错误,类型不匹配:无法从 Join 转换为 SetJoin 即使我更改为 'CriteriaQuery criteria = criteriaBuilder.createQuery(Student.class);' SetJoin 课程 = root.join(Student_.courses);我不能使用这个,不能识别末尾有下划​​线的学生。我应该为此添加任何库吗?
    • SetJoin courses = root.join("courses");现在正在工作...感谢 perissf。你也会研究元模型生成器。 :)
    • 我已经编辑了我的答案。在添加更多 cmets 之前,请仔细阅读我提到的 3 个链接。
    猜你喜欢
    • 1970-01-01
    • 2017-06-08
    • 1970-01-01
    • 2015-03-10
    • 1970-01-01
    • 2012-01-14
    • 1970-01-01
    • 1970-01-01
    • 2011-02-27
    相关资源
    最近更新 更多