【问题标题】:How do I perform an outer join with a JPA CriteriaBuilder query?如何使用 JPA CriteriaBuilder 查询执行外部联接?
【发布时间】:2013-09-18 15:43:09
【问题描述】:

我正在使用 JPA 2.0、Hibernate 4.1.0.Final 和 MySQL 5.5.27。我想构建一个 JPA 查询,每行将返回两个实体,并且我想执行右外连接。这两个实体是:

@Entity
@Table(name = "user",
    uniqueConstraints = { @UniqueConstraint(columnNames = { "USER_NAME" }) }
)
public class User implements Comparable<User>, Serializable
{
    ...
    @Column(name = "first_name")
    @NotNull
    /* the first name of the User */
    private String firstName;

和

@Entity
@Table(name="code_user",
    uniqueConstraints = { 
        @UniqueConstraint(columnNames = { "CODE_ID", "USER_ID" }) }
)
public class CodeUser 
{

    @Id
    @NotNull
    @GeneratedValue(generator = "uuid-strategy")
    @Column(name = "ID")
    private String id;

    @ManyToOne
    @JoinColumn(name = "CODE_ID", nullable = false, updatable = true)
    private Code code;

    @ManyToOne
    @JoinColumn(name = "USER_ID", nullable = false, updatable = true)
    private User user;

到目前为止,这是我的 JPA,但问题是正在执行内部连接,并且每行仅返回一个实体(用户对象)...

    final CriteriaBuilder builder = m_entityManager.getCriteriaBuilder();
    final CriteriaQuery<User> criteria = builder.createQuery(User.class);
    final Root<User> user = criteria.from(User.class);

    final Root<CodeUser> codeUser = criteria.from(CodeUser.class);
    final Join<CodeUser, User> joinUser = codeUser.join(CodeUser_.user);
    criteria.select(joinUser);
    …
    return criteria.where(builder.and(preds.toArray(new Predicate[preds.size()])));

如何执行外部联接以返回所有用户并返回任何匹配的 CodeUser 记录?不能向我的用户实体添加额外的成员字段。

【问题讨论】:

    标签: hibernate jpa join jpa-2.0 outer-join


    【解决方案1】:

    你必须检查Hibernate是否支持Right Join,因为根据documentation,JPA提供者不需要支持RIGHT JOIN:

    右外连接和右外提取连接不需要是 Java Persistence 2.0 中支持。使用 RIGHT 连接的应用程序 类型将不可移植。

    所以,所以,如果你发现没有休眠版本,我看到的唯一解决方案是:

    解决方案 1. 将 LAZY 字段添加到 User 实体

    @OneToMany(mappedBy="TODO", fetch=FetchType.LAZY)
    private Collection<CodeUser> codeUsers;
    

    然后使用 LEFT JOIN 从 User 导航到 CodeUsers。

    解决方案 2。 进行两个不同的查询:第一个获取所有用户,第二个获取相应的 CodeUser。

    【讨论】:

    • 对于解决方案 1:保持字段 codeUsers 私有并且不要为其添加访问器,如果您想保持相同的业务模型。为了避免性能/内存问题,这可能很重要。
    【解决方案2】:

    您可以执行以下操作以创建RIGHT JOIN:

    Join<CodeUser, User> joinUser = rootCodeUser.join(CodeUser_.user, JoinType.RIGHT);
    //now you can add your conditions on the CodeUser
    

    另外,为了选择更多实体,请尝试这样做:

    CriteriaQuery<Object[]> criteria = builder.createQuery( Object[].class );
    Root<CodeUser> codeUserRoot = criteria.from( CodeUser.class );
    Join<CodeUser, User> joinUser = rootUser.join(CodeUser_.user, JoinType.RIGHT);
    
    criteria.select( builder.array( joinUser, codeUserRoot ) );
    //now you can add you conditions
    

    PS:我在您的代码中发现的一个问题是它需要两个根,而不是一个根。

    【讨论】:

    • 嗨,谢谢,但不能将成员字段(在您的答案中,“codeUsers”)添加到我的用户实体。
    • 更改了示例,因此不需要新字段并进行 RIGHT JOIN。
    • 感谢您更新您的答案。我尝试了您的建议,但在 JoinType.RIGHT 行上出现“java.lang.UnsupportedOperationException:不支持 RIGHT JOIN”异常。这个问题的另一个限制是我必须使用 Hibernate。我会尝试升级版本,但我认为这就是 Hibernate 的全部内容。
    • 自 2010 年起不再支持:hibernate.atlassian.net/browse/JPA-2
    【解决方案3】:

    使用交叉连接

        final CriteriaBuilder builder = m_entityManager.getCriteriaBuilder();
        final CriteriaQuery<User> criteria = builder.createQuery(User.class);
        final Root<User> user = criteria.from(User.class);
    
        final Root<CodeUser> codeUser = criteria.from(CodeUser.class);
        criteria.select(codeUser);
        preds.add(builder.equal(codeUser.get("USER_ID"), user.get("ID")))
    
        …
        return criteria.where(builder.and(preds.toArray(new Predicate[preds.size()])));
    

    【讨论】:

    • 我建议添加一些关于代码到底在做什么的更多信息
    猜你喜欢
    • 1970-01-01
    • 2012-01-30
    • 1970-01-01
    • 1970-01-01
    • 2021-02-23
    • 1970-01-01
    • 2021-06-23
    • 1970-01-01
    • 2014-03-28
    相关资源
    最近更新 更多