使用CriteriaBuilder(JPA)进行子选择以检索语句的右侧

时间:2011-06-12 16:23:11

标签: java jpa jpa-2.0

我正在玩JPA和其他Java EE 6的东西,但现在我面临使用标准Builder的类型查询的一些问题。 我的poc的商业案例是一个推特克隆,所以我的用户有一个订阅者和订阅列表,我得到了推文。

@Entity
public class Tweet {
    @Id @GeneratedValue(strategy=GenerationType.AUTO)
    private Long id;

    @ManyToOne
    private TwitterUser author;

    @ManyToOne
    private TwitterUser receiver;

    @Temporal(TemporalType.TIMESTAMP) 
    private Date datetime; 

    private String message;
}


@Entity
public class TwitterUser {
    @Id @GeneratedValue(strategy=GenerationType.AUTO)
    private Long id;

    private String username;

    private String displayname;

    private String password;

    private String email;

    @Temporal(TemporalType.TIMESTAMP)
    private Date since;

    @ManyToMany(cascade=CascadeType.ALL)
    private List<TwitterUser> subscriptions;

    @ManyToMany(mappedBy = "subscriptions")
    private List<TwitterUser> subscribers;
}

JPA生成的数据模型由3个表组成:Tweet,TwitterUser和TwitterUser_TwitterUser。到目前为止,一切都很好。基本查询确实有效。

现在我想为时间轴选择所有推文,这意味着所有用户推文都在当前登录用户的订阅列表中。我通过使用subselect来考虑解决方案,所以我的sql看起来像:

select * from TWEET where author_id in (
    select subscriptions_ID from TWITTERUSER_TWITTERUSER where subscribers_ID = ?loggedInUserId);

此查询效果很好,但我不知道如何使用CriteriaBuilder和泛型将其写下来。我甚至没有得到一个可编译的代码,在其他一些方面可能会失败,因为我不确定子查询的类型应该是什么。 我现在正在搜索大量的例子,但它们大多只是使用原始类型或使用子查询来检索子句中左侧的元素。

也许我想念树木,但我真的很困惑。 :(

3 个答案:

答案 0 :(得分:3)

在JPQL中,表达这种情况的一种方式类似于以下内容:

SELECT
    tweet
FROM
    Tweet tweet JOIN tweet.author author
WHERE
    EXISTS (
        SELECT 
               user 
         FROM 
                TwitterUser user JOIN user.subscriptions subscriber
         WHERE 
                user = :loggedinUser AND
                subscriber = author
    )

作为几乎直接遵循JPQL模型的Criterias,也许这会让你开始。

答案 1 :(得分:1)

再次感谢你们帮助我们。我终于设法找到了一个输入的解决方案,它看起来如下:

public List<Tweet> findAllForSubscriber(TwitterUser user) {
    // Select tweets
    CriteriaBuilder cb = em.getCriteriaBuilder();
    CriteriaQuery<Tweet> cq = cb.createQuery(Tweet.class);
    Root<Tweet> tweet = cq.from(Tweet.class);
    cq.select(Tweet);

    // Select subscribers of user
    Subquery<Long> sq = cq.subquery(Long.class);
    Root<TwitterUser> twitterUser = sq.from(TwitterUser.class);
    Join<TwitterUser, TwitterUser> subscriptions = twitterUser.join(TwitterUser_.subscriptions);
    sq.select(subscriptions.get(TwitterUser_.id));
    sq.where(cb.equal(twitterUser, user));

    // Where authorId in list of subscribers
    cq.where(cb.in(tweet.get(Tweet_.author).get(TwitterUser_.id)).value(sq));

    //
    return em.createQuery(cq).getResultList();
}

我的缺点是:

  • Criteria API就是这样 做一个对象的比较 等于()但不在in()中 声明。但我没有得到 一切都是有用的例外 生成的是一个错误的查询(eclipselink&amp; derby):(
  • in()语句 取左边的 声明,值声明 可能性列表, 措辞让我困惑。
  • 我无法简单地访问ID 在所有订阅中,我必须使用 加入。如果你想的话,这很清楚 关于桌子结构,但我 只是在思考时错过了 我的对象。

P.S。:我的解决方案完全没有选择用户自己的推文! ^^

答案 2 :(得分:0)