Hibernate Criteria查询:获取具有m..n关系的对象列表,其中子表没有特定属性

时间:2011-05-10 09:32:49

标签: java sql hibernate

我有以下表格

CREATE TABLE "COMPANIES" (
    "ID" NUMBER NOT NULL ,
    "NAME" VARCHAR2 (100) NOT NULL  UNIQUE
) 
/

CREATE TABLE "COMPANIESROLES" (
    "ID" NUMBER NOT NULL ,
    "COMPANYID" NUMBER NOT NULL ,
    "ROLENAME" VARCHAR2 (30) NOT NULL
) 
/

CREATE TABLE "ROLES" (
    "NAME" VARCHAR2 (30) NOT NULL
) 
/

这种结构代表了许多公司以及每家公司所允许的角色。对于这些表,有相应的Hibernate对象:

public class Company implements Serializable {

    private Long id;
    private String name;
    private Set<Role> companyRoles;

    //(getters and setters omitted for readability)
}

public class Role implements Serializable {

        private String name;

        //(getters and setters omitted for readability)
}

找出所有使用Hibernate Criteria API具有特定角色的公司都没问题:

Session session = this.sessionFactory.getCurrentSession();
Criteria criteria = session.createCriteria(Company.class);
criterion = Restrictions.eq("companyRoles.name", "ADMIN");
criteria.add(criterion);
List<Company> companyList = criteria.list();

Hibernate将其转换为SQL查询(大约)

SELECT *
FROM   companies this_
      inner join companyroles cr2_
         ON this_.id = cr2_.companyid
       inner join roles role1_
         ON cr2_.rolename = role1_.NAME
WHERE role1_.NAME = 'ADMIN'  

现在问题是:如何反转查询,即找出所有没有“ADMIN”角色映射的公司?如果我只是尝试通过设置

来反转标准
criterion = Restrictions.ne("companyRoles.name", "ADMIN");

(不等于而不是等于),Hibernate创建一个像这样的查询

SELECT *
FROM   companies this_
      inner join companyroles cr2_
         ON this_.id = cr2_.companyid
       inner join roles role1_
         ON cr2_.rolename = role1_.NAME
WHERE role1_.NAME != 'ADMIN'  

显然,这不会产生所需的输出,因为列表仍然包含具有“ADMIN”角色的公司,只要公司至少有一个其他角色。

我想要的是一个公司列表,它们没有“ADMIN”角色。作为一个额外的限制,如果可能的话,只需修改Criterion对象就可以做到这一点(这是因为标准是作为内部框架的一部分自动构建的,并且不可能在那里进行更大的更改)。 当Criteria对象包含其他附加标准时,解决方案也应该有效。

这是怎么做的,或者是吗?

1 个答案:

答案 0 :(得分:3)

您需要一个子查询(DetachedCriteria)。

DetachedCriteria sub = DetachedCriteria.forClass(Company.class);
criterion = Restrictions.eq("companyRoles.name", "ADMIN");
sub.add(criterion);
sub.setProjection(Projections.property("id"));
Criteria criteria = session.createCriteria(Company.class);
criteria.add(Property.forName("id").notIn(sub));
List<Company> companyList = criteria.list();

这样的事情应该这样做。