【问题标题】:Subselect to retrieve righthand-side of in-statement using CriteriaBuilder (JPA)子选择以使用 CriteriaBuilder (JPA) 检索语句的右侧
【发布时间】:2011-06-12 16:23:11
【问题描述】:

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

@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。到目前为止一切都很好。基本查询效果很好。

现在我想为时间线选择所有推文,这意味着所有用户的推文,他们在当前登录用户的订阅列表中。我想到了一个使用子选择的解决方案,所以我的 sql 看起来像:

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

此查询运行良好,但我不知道如何使用 CriteriaBuilder 和泛型将其写下来。我什至没有得到一段可能在其他时候失败的可编译代码,因为我不确定子查询的类型应该是什么。 我现在正在搜索很多示例,但它们大多只是使用原始类型或使用子查询来检索 in-clause 左侧的元素。

也许我只见树木不见森林,但我真的很困惑。 :(

【问题讨论】:

    标签: java jpa jpa-2.0


    【解决方案1】:

    在 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 模型的标准,也许这可以帮助您入门。

    【讨论】:

    • 嗨,Arjan,感谢您的建议,它帮助我重新思考了这个问题,我找到了一个更简单的解决方案,因为我认识到我已经在我的用户对象中获得了我想要其时间线的所有订阅者的列表展示。非常感谢你,但最终让我发疯的是,我没有找到如何使用 QueryBuilder 来制定我的查询。但是,我也无法制定你的! ^^ 所以我认为我必须努力理解这些表达方式。
    • 一般来说,JPQL(以及它的 Criterias)遵循 SQL 的精神。但由于 JPQL 不公开关联(耦合)表,并且不允许您连接任意表中的任意列,因此您有时必须颠倒您对方法的想法。
    • 对我来说,有趣的事情不是为我的问题找到解决方案,我想知道如何使用 Criteria API 来做到这一点,因为我想了解如何使用它。您使用联接的方法是我错过的事实之一,它帮助我找到了正确的解决方案,谢谢! :)
    【解决方案2】:

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

    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 只是能够 比较一个对象中的对象 equal() 但不在 in() 内 陈述。但我没有得到 有帮助的例外,所有这些都是 生成的是一个错误的查询(eclipselink & derby):(
    • in() 语句 取 in 的左侧 声明,价值声明采取 可能性清单, 措辞让我很困惑。
    • 我不能简单地访问 ID 在所有订阅中,我必须使用 加入。如果你认为这很清楚 关于表结构,但我 只是想着就错过了 我的对象。

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

    【讨论】:

      【解决方案3】:

      【讨论】:

      • 感谢您的链接,当涉及到有趣的部分时,它终于从输入查询切换到非输入查询!但它告诉我,我只是错误地使用了 in() 语句。所以再次感谢:)
      猜你喜欢
      • 2015-01-30
      • 2019-04-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-03-28
      • 2014-03-14
      • 1970-01-01
      相关资源
      最近更新 更多