【问题标题】:JPA Join on multiple columnsJPA加入多列
【发布时间】:2013-06-26 02:31:42
【问题描述】:

我有实体 User、Organization 和 GrantedRole。它们的关系是 GrantedRole 定义了用户在组织中的角色。所有三个实体都从它们的超类继承了一个字段“id”(用@Id 注释)。组织没有对 GrantedRole 或 User 的引用,这些关系是单向的。

@Entity
@Table(name = "role_assignments", uniqueConstraints = @UniqueConstraint(columnNames = {
    "user_id", "organization_id" }))
public class GrantedRole extends DomainEntity implements GrantedAuthority {

    public static enum Role {
        ROLE_MODM_USER, ROLE_MODM_ORGADMIN, ROLE_MODM_ADMIN
    }

    private User user;
    private Role role;
    private Organization organization;

    public static enum Role {
        ROLE_MODM_USER, ROLE_MODM_ORGADMIN, ROLE_MODM_ADMIN
    }

    @ManyToOne
    @NotNull
    public User getUser() {
        return user;
    }

    @NotNull
    @Enumerated(EnumType.STRING)
    public Role getRole() {
        return role;
    }

    @ManyToOne
    public Organization getOrganization() {
        return organization;
    }

    // Some setters
}

@Entity
@Table(name = "modmUsers")
public class User extends DomainEntity implements UserDetails {
    // Some other fields

    private Set<GrantedRole> roles = new HashSet<GrantedRole>();
    private Set<Organization> organizations = new HashSet<Organization>();

    @OneToMany(fetch = FetchType.EAGER, mappedBy = "user")
    public Set<GrantedRole> getRoles() {
        return roles;
    }

    @ManyToMany(fetch = FetchType.EAGER)
    public Set<Organization> getOrganizations() {
        return organizations;
    }

    // Some setters
}

现在我想使用 JPA 标准来查找用户在其中具有角色 ROLE_MODM_ORGADMIN 的组织。但是生成的查询在单个列上连接,这会导致 GrantedRole 中的行重复,因为 User.id 在 GrantedRole 中不是唯一的,Organization.id 也不是唯一的。两者的结合是独一无二的。

我查找组织的代码:

public List<Organization> findOrganizationsByOrgAdminUser(String 
    CriteriaBuilder cb = entityManager.
    CriteriaQuery<Organization> query = cb.createQuery(Organization.
    Root<User> root = query.from(User.class);
    SetJoin<User, Organization> joinOrg = root.joinSet("organizations");
    SetJoin<User, GrantedRole> joinRoles = root.joinSet("roles");

    Predicate p1 = cb.equal(root.get("id"), userId);
    Predicate p2 = cb.equal(joinRoles.get("role"), Role.ROLE_MODM_ORGADMIN);

    query.select(joinOrg);
    query.where(cb.and(p1, p2));

    return entityManager.createQuery(query).getResultList();
}

生成的查询是:

SELECT
    orgs.* 
FROM
    modmUsers users
INNER JOIN
    modmUsers_organizations u_o 
        ON users.id=u_o.modmUsers_id 
INNER JOIN
    organizations orgs
        ON u_o.organization_id=orgs.id 
INNER JOIN
    role_assignments roles 
        ON users.id=roles.user_id 
WHERE
    users.id=? 
    and roles.role=?

我想要的查询是:

SELECT
    orgs.* 
FROM
    modmUsers users
INNER JOIN
    modmUsers_organizations u_o 
        ON users.id=u_o.modmUsers_id 
INNER JOIN
    organizations orgs
        ON u_o.organization_id=orgs.id 
INNER JOIN
    role_assignments roles 
        ON users.id=roles.user_id 
        AND orgs.id=roles.organization_id //how to do this???
WHERE
    users.id=? 
    and roles.role=?

【问题讨论】:

    标签: java jpa inner-join


    【解决方案1】:

    好的,所以我的同事帮我解决了这个问题:

    SELECT
        orgs.* 
    FROM
        modmUsers users
    INNER JOIN
        modmUsers_organizations u_o 
            ON users.id=u_o.modmUsers_id 
    INNER JOIN
        organizations orgs
            ON u_o.organization_id=orgs.id 
    INNER JOIN
        role_assignments roles 
            ON users.id=roles.user_id 
    WHERE
        users.id=? 
        AND roles.role=?
        AND orgs.id=roles.organization_id
    

    这是我的代码的简单扩展:

    public List<Organization> findOrganizationsByOrgAdminUser(String 
        CriteriaBuilder cb = entityManager.
        CriteriaQuery<Organization> query = cb.createQuery(Organization.
        Root<User> root = query.from(User.class);
        SetJoin<User, Organization> joinOrg = root.joinSet("organizations");
        SetJoin<User, GrantedRole> joinRoles = root.joinSet("roles");
    
        Predicate p1 = cb.equal(root.get("id"), userId);
        Predicate p2 = cb.equal(joinRoles.get("role"), Role.ROLE_MODM_ORGADMIN);
        Predicate p3 = cb.equal(joinOrg.get("id"), joinRoles.get("organization"));
    
        query.select(joinOrg);
        query.where(cb.and(p1, p2, p3));
    
        return entityManager.createQuery(query).getResultList();
    }
    

    【讨论】:

    • where 子句指定是连接所有 100 万条记录,然后使用 where 过滤,因为在连接中使用 AND 运算符将过滤表然后连接。那么,有没有办法在 ON 子句行中给出 AND。像普通的sql一样支持它如果我错了告诉我
    猜你喜欢
    • 2022-01-02
    • 2021-10-19
    • 2016-07-07
    • 2013-10-28
    • 1970-01-01
    • 2011-12-22
    • 1970-01-01
    • 2021-07-28
    • 2011-02-14
    相关资源
    最近更新 更多