如何使用hibernate查询具有嵌套对象的对象,该嵌套对象具有嵌套的对象集合

时间:2016-05-02 06:32:04

标签: java hibernate

使用Hibernate,我需要在MySQL数据库中查询Post实体,该实体与Poll实体具有一对一的关系,Poll实体与Answer实体具有一对多的关系。我需要Post对象包含Poll对象及其Poll对象以包含其Answer对象。这是基本的课程设置:

更新 Post表必须没有主键列。这是浪费数据。我需要能够使用user_id列从数据库中获取Post对象。使用user_id列获取Post对象是唯一可行的方法,因此拥有主键列对我来说毫无意义。因此,如果您要提供能够深入了解解决 我的 问题的解决方案的答案,请牢记这些规范。

职位类别:

@Entity
@Table(name="user_feed")
public class Post implements Serializable {
    //id for the user that is meant to receive the post
    //*post object is taken from a table that will contain
    //*posts for many different users
    @Id
    @Column(name="user_id")
    private long mUserId;

    //poll id
    @Id
    @Column(name="poll_id")
    private long mPollId;

    //boolean that indicates whether this post is a repost
    @Column(name="is_repost")
    private boolean mIsRepost;

    //date the post was created
    @Column(name="date_created")
    private Date mDateCreated;

    //the poll this post contains
    @OneToOne
    @JoinColumn(name="poll_id")
    private Poll mPoll;

民意调查班:

@Entity
@Table(name="poll")
public class Poll implements Serializable{

    //the poll's id
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name="id")
    private long mId;

    //id of the user who created the poll
    @Column(name="user_id")
    private long mUserId;

    //the text of the poll's question
    @Column(name="question")
    private String mQuestion;

    //the date the poll was created
    @Column(name="date_created")
    private Date mDateCreated;

    //the answer objects for this poll
    @OneToMany
    @JoinColumn(name="id")
    private List<Answer> mAnswers;

答案类:

@Entity
@Table(name="answer")
public class Answer implements Serializable {

    //id for a particular answer
    //*this is not a necessary value for the application logic, but
    //*Hibernate forces me to designate an @Id annotation for every
    //*entity, so I created this field and the associated column in
    //*the database
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name="id")
    private long mId;

    //the answer's text
    @Column(name="answer_text")
    private String mAnswer;

    //the id of the poll to which this answer pertains to
    @Column(name="poll_id")
    private long mPollId;

***我对这张桌子的ID感到困惑。每个答案都有一个主键没有意义,但Hibernate在类中需要某种@Id注释,所以我决定只是为了Hibernate在表中创建一个主键列。它从未使用过。我想摆脱它,但实际上没有任何东西使得一个答案与另一个答案在同一个民意调查中是唯一的,除了他们的文本 - 这对于应用程序逻辑来说并不是必需的。

查询我提出:不起作用

这个查询真的只是我测试,看看我是否可以获得一个包含所有嵌套对象的Post对象。我知道如果能得到一个,那么收藏就不会太多了 - 但是我甚至得不到一个。

Session session = HibernateUtilities.openSession();
        session.beginTransaction();

        //29 is a post meant for a particular user and 47 is the id of the    
        //poll that should be contained in the post
        Post post = (Post)session.get(Post.class, new Post(29, 47));

        session.getTransaction().commit();
        session.close();

        //suppose to return the post in JSON format to a client, but it
        //doesn't work when I create the one-to-many relationship between       
        //the poll and it's answers. It only works without the relationship;   
        //which I've defined in the Poll class
        return mGson.toJson(post);

2 个答案:

答案 0 :(得分:0)

您不应该将关系的主键作为自己的字段(例如,您不需要Post.mPoll和Post.mPollId,如果需要,只需使用Post.mPoll.getId())。如果我要解决你的问题,我默认情况下(我们可以讨论Post后来没有id)使用下面的对象模型(为简洁起见省略了getter,但我会在所有字段中使用它们。)

@Entity
public class Post {

    @Id
    @GeneratedValue
    private long id;

    @OneToOne
    private Poll poll;

}

@Entity
public class Poll {

    @Id
    @GeneratedValue
    private long id;

    @OneToMany
    private List<Answer> answers;

}

@Entity
public class Answer {

    @Id
    @GeneratedValue
    private long id;

}

从那里开始,看看它分崩离析的地方。如果您希望实体没有任何ID,那么您可以使用@ Embedded,@ Embeddable和@ElementCollection注释。

@Embeddable最初用于嵌入“价值”对象(例如货币,日期,邮政地址等),因此这些对象不需要主键,并且完全归所有者所有。

使用@Embedded注释引用可嵌入对象(例如,如果是一对一的话,您的用户将拥有对@Embeddable帖子的@Embedded引用。)

要引用可嵌入对象的集合,请使用@ElementCollection批注。但是,@ ElementCollection的成员是不可变的(不能在数据库中修改它们,必须从集合中删除它并添加新实例)并且不能延迟加载。考虑到Post对象的复杂性,我个人不会将其作为嵌入式类(你可能希望有一天能够编辑帖子吗?)但是如果你想要它应该工作。

我说应该因为我从来没有一个可嵌入的类引用其他非可嵌入的实体(例如你对Poll的引用)。尝试一下这些东西,如果它们不起作用,那么请准确发布出错的地方。

答案 1 :(得分:0)

自己解决了。以下代码中的所有注释都指定了我对问题中提供的代码所做的更改,并解释了我为什么要这样做。

职位类别:

@Entity
@Table(name="user_feed")
public class Post implements Serializable {
    @Id
    @Column(name="user_id")
    private long mUserId;

    //removed long mPollId
    //hibernate is capable of getting the foreign key for a post's
    //poll_id column from its poll object -- mPoll
    //so i don't have to have a separate field for the id of this post's
    //poll

    @Column(name="is_repost")
    private boolean mIsRepost;

    @Column(name="date_created")
    private Date mDateCreated;

    //made this field part of the composite id instead of long mPollId
    //pretty much the same composite key as before just had to alter
    //my implementation of Post.equals(Object) to use this poll's id
    //instead of this class's mPollId field
    //implementing your own .equals(Object) method is necessary when
    //creating composite keys as i do with multiple @Id annotations
    //i think you also have to implement your own .hashCode() method too
    //but the word hash scares me, so I didn't do it
    //the code works, so i'm just gonna let it rock
    @OneToOne
    @JoinColumn(name="poll_id")
    private Poll mPoll;

民意调查班:

@Entity
@Table(name="poll")
public class Poll implements Serializable{

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name="id")
    private long mId;

    @Column(name="user_id")
    private long mUserId;

    @Column(name="question")
    private String mQuestion;

    @Column(name="date_created")
    private Date mDateCreated;

    //removed @JoinColumn -- not completely sure about why it wasn't
    //helping, but many of the examples similar to my use case didn't
    //use it so I got rid of it
    //added mappedBy variable -- still not really sure what it does
    //but it works
    //and added FetchType.EAGER so everytime a Poll object is loaded
    //the answers it's associated with are loaded too
    @OneToMany(mappedBy="mPoll", fetch=FetchType.EAGER)
    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.REMOVE})
    private List<Answer> mAnswers;

答案类:

@Entity
@Table(name="answer")
public class Answer implements Serializable {

    //turns out having a primary key on the answer table is actually useful
    //for the application logic. would you look at that
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name="id")
    private long mId;

    @Column(name="answer_text")
    private String mAnswer;

    //got rid of long mPollId
    //it was for the same reason i did in the Post class
    //hibernate does the work for me with the mPoll object and the
    //annotations i've provided on it

    //made the relationship between a poll and its answers bidirectional
    //not entirely sure how adding the below annotations to the new
    //Poll field fixed my problems, but it did
    //i imagine it somehow tells hibernate that the primary key
    //for the below object is the foreign key represented by poll_id in the
    //database table for this entity
    //and making insertable=true enables hibernate to insert that foreign
    //key into the appropriate column in the database when this entity
    //is saved
    //updatable seemed to be necessary
    //hibernate complained when it wasn't there
    //and nullable was in the helpful examples i found so it was copy and
    //pasted along with the rest of the helpful stuff here
    //this field can't be nullable anyways so semantically, it makes sense
    //for it to be there
    @ManyToOne
    @JoinColumn(name="poll_id", nullable = false, insertable=true, updatable=false)
    private Poll mPoll;

最终功能查询:确实有效

Session session = HibernateUtilities.openSession();
        session.beginTransaction();


        List<Post> usersFeed = session.createQuery("select p from Post p where p.mUserId = :userId")
                  .setString("userId", userId)
                  .list();


        session.getTransaction().commit();
        session.close();