【问题标题】:Hibernate One To Many criteria doesn't work休眠一对多条件不起作用
【发布时间】:2016-04-27 12:41:59
【问题描述】:

我有以下实体:

@Entity
@Table(name = "author")
public class Author implements Serializable {
private static final long serialVersionUID = 12345L;

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "author_id")
private int authorId;

@Column(name = "author_bio")
private String authorBio;

@Column(name = "author_email")
private String authorEmail;

@Column(name = "author_favourite_section")
private String authorFavouriteSection;

@Column(name = "author_password")
private String authorPassword;

@Column(name = "author_username")
private String authorUsername;

@OneToOne(mappedBy = "author", fetch = FetchType.LAZY)
private Blog blog;

@OneToMany(mappedBy = "author", fetch = FetchType.LAZY)
private List<Post> posts;

// getters and setters

@Entity
@Table(name = "blog")
public class Blog implements Serializable {
private static final long serialVersionUID = 1L;

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "blog_id")
private int blogId;

@Column(name = "blog_title")
private String blogTitle;

@OneToOne(optional = false, fetch = FetchType.LAZY, cascade =  CascadeType.ALL)
@JoinColumn(name = "blog_author_id", unique = true)
private Author author;

@OneToMany(mappedBy = "blog", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<Post> posts;

// getters and setters

@Entity
@Table(name = "post")
public class Post implements Serializable {
private static final long serialVersionUID = 1L;

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "post_id")
private int postId;

@Column(name = "post_subject")
private String postSubject;

@Column(name = "post_body")
private String postBody;

@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinColumn(name = "blog_id")
private Blog blog;

@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinColumn(name = "post_author_id")
private Author author;

@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name = "post_tag", joinColumns = {
        @JoinColumn(name = "post_id", nullable = false, updatable = false)},
        inverseJoinColumns = {@JoinColumn(name = "tag_id",
                nullable = false, updatable = false)})
private Set<Tag> tags = new HashSet<Tag>();

// getters and setters

@Entity
@Table(name = "tag")
public class Tag implements Serializable {
private static final long serialVersionUID = 1L;

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "tag_id")
private int tagId;

@Column(name = "tag_name")
private String tagName;

@ManyToMany(mappedBy = "tags", fetch = FetchType.LAZY)
private Set<Post> posts = new HashSet<Post>();

// getters and setters  

以下数据在db中呈现:

author-blog-post-tag-AND-post_tag-tables

要实现的主要目标是:查找所有发表过包含适当标签的帖子的作者。

我可以使用 SQL 查询来做到这一点:

SELECT  a.author_id, a.author_bio, p.post_id, p.post_subject, t.tag_id, t.tag_name from author a
join blog b
on a.author_id = b.blog_author_id
join post p
on p.post_author_id = a.author_id
join post_tag pt
on p.post_id = pt.post_id
join tag t
on t.tag_id = pt.tag_id
where t.tag_name in ('Football', 'Basketball')

并返回正确的结果以及作者、过滤的帖子和标签。

但我需要使用休眠来完成。

所以使用休眠我想找到所有写过包含适当标签的帖子的作者。 并且所有那些只有那些包含指定标签的帖子(见上文 - '足球','篮球')的作者都必须被退回。

我写了这段代码:

final DetachedCriteria authorCriteria = DetachedCriteria.forClass(Author.class, "author");
authorCriteria.createAlias("author.posts", "post");
authorCriteria.createAlias("post.tags", "tag");
Criterion football = Restrictions.eq("tag.tagName", "Football");
Criterion basketball = Restrictions.eq("tag.tagName", "Basketball");
authorCriteria.add(Restrictions.or(football, basketball));
authorCriteria
.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
final List<Author> result = (List<Author>)getConfiguredHibernateTemplate().findByCriteria(authorCriteria);

我预计会收到:

作者(author_id = 54)只有一个帖子(post_id = 26),并且这个帖子包含两个标签('足球'和'篮球'),因为我使用上面的 SQL 查询收到它。

但实际结果是我收到了 Author(author_id = 54),他的所有帖子都显示在数据库中(错误和问题在这里!!!)并且每个帖子都包含所有标签,这些标签也显示在数据库中。

intellij-idea-debug-result

Hibernate 生成了以下查询:

select this_.author_id as author_i1_0_2_, this_.author_bio as author_b2_0_2_, this_.author_email as author_e3_0_2_, this_.author_favourite_section as author_f4_0_2_, this_.author_password as author_p5_0_2_, this_.author_username as author_u6_0_2_, post1_.post_id as post_id1_2_0_, post1_.post_author_id as post_aut4_2_0_, post1_.blog_id as blog_id5_2_0_, post1_.post_body as post_bod2_2_0_, post1_.post_subject as post_sub3_2_0_, tags5_.post_id as post_id1_2_, tag2_.tag_id as tag_id2_3_, tag2_.tag_id as tag_id1_4_1_, tag2_.tag_name as tag_name2_4_1_ from author this_ inner join post post1_ on this_.author_id=post1_.post_author_id inner join post_tag tags5_ on post1_.post_id=tags5_.post_id inner join tag tag2_ on tags5_.tag_id=tag2_.tag_id where (tag2_.tag_name=? or tag2_.tag_name=?)

select blog0_.blog_id as blog_id1_1_0_, blog0_.blog_author_id as blog_aut3_1_0_, blog0_.blog_title as blog_tit2_1_0_ from blog blog0_ where blog0_.blog_author_id=?

select posts0_.post_author_id as post_aut4_0_0_, posts0_.post_id as post_id1_2_0_, posts0_.post_id as post_id1_2_1_, posts0_.post_author_id as post_aut4_2_1_, posts0_.blog_id as blog_id5_2_1_, posts0_.post_body as post_bod2_2_1_, posts0_.post_subject as post_sub3_2_1_ from post posts0_ where posts0_.post_author_id=?

如何使用 hibernate 实现预期且正确过滤的结果?

【问题讨论】:

    标签: java hibernate


    【解决方案1】:

    您要求作者撰写有关足球或 BaketBall 的博客:

    DetachedCriteria.forClass(Author.class, "author");
    

    碰巧这位作者还写了一些关于其他事情的博客。所以你得到了你所要求的。在您的 sql 语句中,您要求进行投影,而在 hibernate 中,您要求 ORM 使用其 posts 集合获取对象(author)。

    【讨论】:

    • 嗨弗兰克,感谢您阅读并回答我的问题。我真的很感激你花费的时间。我要解决的主要目标(问题/问题)是 - 让所有写过包含适当标签的帖子的作者。结果应该返回所有这些作者,并且只返回那些包含传递给休眠的标签(足球或篮球)的帖子。我不需要拥有所有帖子的作者。对于过滤其他帖子的特定作者,只应返回包含适当标签的帖子。你知道如何在休眠中实现这一点吗?
    • 我也尝试使用 org.hibernate.annotations.FilterDef 来做,但看起来无法得到预期的结果。
    • 然后您可以选择 Post 作为您的根实体,而不是作者或创建 projection
    • 我在下面添加了答案
    【解决方案2】:

    我尝试使用投影( authorCriteria.setResultTransformer(CriteriaSpecification.PROJECTION )

    final DetachedCriteria authorCriteria = DetachedCriteria.forClass(Author.class, "author");
        authorCriteria.createAlias("author.posts", "post");
        authorCriteria.createAlias("post.tags", "tag");
        final Criterion football = Restrictions.eq("tag.tagName", "Football");
        final Criterion basketball = Restrictions.eq("tag.tagName", "Basketball");
        authorCriteria.add(Restrictions.or(football, basketball));
        authorCriteria.setResultTransformer(CriteriaSpecification.PROJECTION);
        final List<Author> result = (List<Author>) getConfiguredHibernateTemplate().findByCriteria(authorCriteria);
    

    以及我在调试器中看到的以下结果:

    好的,这是正确的,我可以分析这些数据并将它们排序到作者->帖子列表->带有特定帖子的标签。但这是代码中的额外工作。 我认为也许休眠有更优雅的方式来返回我需要或不需要的过滤数据。如果没有办法,那么我对hibernate感到失望。那么使用一些spring jdbc模板或者mybatis之类的东西比较方便。

    使用方法“然后您可以选择 Post 作为您的根实体”涉及对 db 的额外查询。为什么我们需要做这么多额外的工作?如果 hibernate 不能运行我想要的查询并以我喜欢的方式返回结果,那么它看起来不够灵活和有用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-17
      • 2016-12-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-22
      相关资源
      最近更新 更多