简体   繁体   English

Hibernate / Spring数据未在OneToMany映射中获取集合

[英]Hibernate/Spring Data not fetching collection in OneToMany mapping

I have a one to many mapping in Hibernate that when I try to get it out of the database through a spring JPARepository, always gives me an empty collection. 我在Hibernate中有一个一对多的映射,当我尝试通过Spring JPARepository将其从数据库中移出时,总是给我一个空集合。

I have a User class that looks like this. 我有一个看起来像这样的User类。

@Indexed
@Entity
@Table(name="usr", indexes = {@Index(columnList = "email", unique = true)})
public class User{
    private Long userId;
    private String email;
    private String firstname;
    private String lastname;
    private String phone;
    private String passwordHash;

    private Set<PollOption> votes;
    private Set<FriendsList> friendsList; 

//no args constructor
    public User() {
        votes = new HashSet<>();
        friendsList = new HashSet<>();
    }

/**
 * Gets the value of id
 *
 * @return the value of id
 */
@Id
@GenericGenerator(name = "userautoinc", strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator",
          parameters = {
          @Parameter(name = "sequence_name", value = "userautoinc"),
          @Parameter(name = "optimizer", value = "hilo"),
          @Parameter(name = "initial_value", value = "1"),
          @Parameter(name = "increment_size", value = "1") }
)
@GeneratedValue(generator = "userautoinc")
@Column(name="userid")
    public Long getUserId() {
    return this.userId;
}

/**
 * Sets the value of id
 *
 * @param argId Value to assign to this.id
 */
public void setuserId(final Long argId) {
    this.userId = argId;
}

/**
 * Gets the value of email
 *
 * @return the value of email
 */
@Field
@NaturalId
@Column(name="email", nullable = false)
    public String getEmail() {
    return this.email;
}

/**
 * Sets the value of email
 *
 * @param argEmail Value to assign to this.email
 */
public void setEmail(final String argEmail) {
    this.email = argEmail;
}

/**
 * Gets the value of firstname
 *
 * @return the value of firstname
 */
@Field
@Column(name="firstname", nullable = false)
public String getFirstname() {
    return this.firstname;
}

/**
 * Sets the value of firstname
 *
 * @param argFirstname Value to assign to this.firstname
 */
public void setFirstname(final String argFirstname) {
    this.firstname = argFirstname;
}

/**
 * Gets the value of lastname
 *
 * @return the value of lastname
 */
@Field
@Column(name="lastname", nullable = false)
    public String getLastname() {
    return this.lastname;
}

/**
 * Sets the value of lastname
 *
 * @param argLastname Value to assign to this.lastname
 */
public void setLastname(final String argLastname) {
    this.lastname = argLastname;
}

/**
 * Gets the value of phone
 *
 * @return the value of phone
 */
@Column(name="phone", nullable = true)
public String getPhone() {
    return this.phone;
}

/**
 * Sets the value of phone
 *
 * @param argPhone Value to assign to this.phone
 */
public void setPhone(final String argPhone) {
    this.phone = argPhone;
}

/**
 * Gets the value of passwordHash
 *
 * @return the value of passwordHash
 */
@Column(name="passwordhash", nullable = false)
public String getPasswordHash() {
    return this.passwordHash;
}

/**
 * Sets the value of passwordHash
 *
 * @param argPasswordHash Value to assign to this.passwordHash
 */
public void setPasswordHash(final String argPasswordHash) {
    this.passwordHash = argPasswordHash;
}


@ManyToMany(mappedBy = "voters", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
public Set<PollOption> getVotes(){
    return this.votes;
}

public void setVotes(Set<PollOption> votes){
    this.votes = votes;
}

@OneToMany(mappedBy = "user", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
public Set<FriendsList> getFriendsList() {
    return this.friendsList;
}

public void setFriendsList(Set<FriendsList> friendList) {
    this.friendsList = friendsList;
}

//To String Method
@Override
public String toString() {
    return "User [userId=" + userId + ", email=" + email + ", firstname=" + firstname + ", lastname=" + lastname
    + ", phone=" + phone + ", passwordHash=" + passwordHash + "]";
}

}

My FriendsList Entity(really a junction table with extra columns) looks like this: 我的FriendsList实体(实际上是具有额外列的联结表)如下所示:

@Entity
@Table(name = "friendslist")  
public class FriendsList{


    private FriendsListPK friendsListPK;

    private User user;
    private User friend;
    private FriendsListStatus status;

    @Embeddable
    public static class FriendsListPK implements Serializable{

        private Long userId;
        private Long friendId;

        private FriendsListPK(){
        }

        private FriendsListPK(Long userId, Long friendId){
            this.userId = userId;
            this.friendId = friendId;
        }

        @JoinColumn(name = "userid", referencedColumnName = "userid")
        public Long getUserId(){
             return this.userId;
        }

        public void setUserId(Long userId){
            this.userId = userId;
        }

        @JoinColumn(name = "friendid", referencedColumnName = "userid")
        public Long getFriendId(){
             return this.friendId;
        }

        public void setFriendId(Long friendId){
            this.friendId = friendId;
        }

        @Override
        public boolean equals(Object o){
            boolean ret = true;
            if (o == null || this.getClass() != o.getClass()){ 
                ret = false;
            } else{
            FriendsListPK that = (FriendsListPK) o;
            ret = this.userId.equals(that.getUserId()) &&
            this.friendId.equals(that.getFriendId());
            }
            return ret;
        }

        @Override
        public int hashCode(){
            return Objects.hash(this.userId, this.friendId);
        }

    }

/**
 * Gets the value of friendsListPK
 *
 * @return the value of friendsListPK
 */
@EmbeddedId
public FriendsListPK getFriendsListPK() {
   return this.friendsListPK;
}

/**
 * Sets the value of friendsListPK
 *
 * @param argFriendsListPK Value to assign to this.friendsListPK
 */
public void setFriendsListPK(FriendsListPK argFriendsListPK) {
    this.friendsListPK = argFriendsListPK;
}

/**
 * Gets the value of status
 *
 * @return the value of status
 */
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "statusid", referencedColumnName = "statusid", nullable = false)
public FriendsListStatus getStatus() {
    return this.status;
}

/**
 * Sets the value of status
 *
 * @param argStatus Value to assign to this.status
 */
public void setStatus(FriendsListStatus argStatus) {
    this.status = argStatus;
}


/**
 * Gets the value of user
 *
 * @return the value of user
 */
@ManyToOne
@MapsId("userId")
public User getUser() {
    return this.user;
}

/**
 * Sets the value of user
 *
 * @param argUser Value to assign to this.user
 */
public void setUser(User argUser) {
    this.user = argUser;
}

/**
 * Gets the value of friend
 *
 * @return the value of friend
 */
@ManyToOne
@MapsId("friendId")
public User getFriend() {
    return this.friend;
}

/**
 * Sets the value of friend
 *
 * @param argFriend Value to assign to this.friend
 */
public void setFriend(User argFriend) {
    this.friend = argFriend;
}

}

And my User Repository is simply this: 而我的用户存储库就是这样的:

@Repository
public interface UserDao extends JpaRepository<User, Long>{
    public User findByEmail(String email);
}

Whenever I get a User and try to access its FriendsList through getFriendsList(), It always gives me an empty set. 每当我获得一个用户并尝试通过getFriendsList()访问其FriendsList时,它总是给我一个空集。 I've tried FetchType.EAGER, as well as Hibernate.Initialize and also initializing the old way with @Transactional and getFriendsList().size(). 我已经尝试过FetchType.EAGER以及Hibernate.Initialize并使用@Transactional和getFriendsList()。size()初始化旧方法。 I believe the fact that the result of getFreindsList() is a HashSet and not some sort of hibernate proxy set suggests that hibernate isn't even trying to lazy-initialize it? 我相信getFreindsList()的结果是HashSet而不是某种冬眠代理集的事实表明,冬眠甚至没有尝试延迟初始化它?

The SQL that hibernate generates is as follows: 休眠生成的SQL如下:

select user0_.userid as userid1_11_, user0_.email as email2_11_,  user0_.firstname as firstname3_11_, user0_.lastname as lastname4_11_, user0_.passwordhash as passwordhash5_11_, user0_.phone as phone6_11_ from usr user0_ where user0_.email =?;
select friendslis0_.user_userid as user_userid2_4_0_, friendslis0_.friend_userid as friend_userid1_4_0_, friendslis0_.friend_userid as friend_userid1_4_1_, friendslis0_.user_userid as user_userid2_4_1_, friendslis0_.statusid as statusid3_4_1_, user1_.userid as userid1_11_2_, user1_.email as email2_11_2_, user1_.firstname as firstname3_11_2_, user1_.lastname as lastname4_11_2_, user1_.passwordhash as passwordhash5_11_2_, user1_.phone as phone6_11_2_, friendslis2_.statusid as statusid1_5_3_, friendslis2_.statusname as statusname2_5_3_ from friendslist friendslis0_ inner join usr user1_ on friendslis0_.friend_userid=user1_.userid inner join friendsliststatus friendslis2_ on friendslis0_.statusid=friendslis2_.statusid where friendslis0_.user_userid=?;
select friendslis0_.user_userid as user_userid2_4_0_, friendslis0_.friend_userid as friend_userid1_4_0_, friendslis0_.friend_userid as friend_userid1_4_1_, friendslis0_.user_userid as user_userid2_4_1_, friendslis0_.statusid as statusid3_4_1_, user1_.userid as userid1_11_2_, user1_.email as email2_11_2_, user1_.firstname as firstname3_11_2_, user1_.lastname as lastname4_11_2_, user1_.passwordhash as passwordhash5_11_2_, user1_.phone as phone6_11_2_, friendslis2_.statusid as statusid1_5_3_, friendslis2_.statusname as statusname2_5_3_ from friendslist friendslis0_ inner join usr user1_ on friendslis0_.friend_userid=user1_.userid inner join friendsliststatus friendslis2_ on friendslis0_.statusid=friendslis2_.statusid where friendslis0_.user_userid=?;

As you can see, hibernate IS indeed trying to query the friendslist table, but the data isn't going into the set. 如您所见,休眠IS确实是在尝试查询friendslist表,但是数据并没有进入集合。 The only thing I see out of the ordinary is that we have 2 identical select statements into friendslist where we would normally expect 1. Perhaps the second one has a different userid and is overwriting the first one? 我与众不同的唯一发现是,我们有2个相同的select语句进入了我们通常期望的1个好友列表中。也许第二个语句具有不同的用户名,并且正在覆盖第一个? But I have no idea where the second select is coming from. 但是我不知道第二选择来自何处。

This seems to be a tricky one and my explanation would be following: 这似乎是一个棘手的问题,我的解释如下:

The FiendsList entity has a composite primary key which consists of userId and friendId column. FiendsList实体具有一个复合主键,该主键由userIdfriendId列组成。 Both of them reference the User.userId column: 它们都引用User.userId列:

    @JoinColumn(name = "userid", referencedColumnName = "userid")
    public Long getUserId(){
         return this.userId;
    }

    @JoinColumn(name = "friendid", referencedColumnName = "userid")
    public Long getFriendId(){
         return this.friendId;
    }

On the User entity however you only put the mapping to the FiendList.user field: 但是,在User实体上,您仅将映射放在FiendList.user字段中:

@OneToMany(mappedBy = "user", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
public Set<FriendsList> getFriendsList() {
    return this.friendsList;
}

The thing is that is not enough for hibernate to know what to do as it needs two column in order to properly fetch the FriendsList entity (as it has composite pk). 问题是休眠不足以让休眠状态知道要做什么,因为它需要两列才能正确获取FriendsList实体(因为它具有复合pk)。

You may need to perform an additional query once you get the User entity in order to get the FriendList by passing the retrieved User.userId value as part of the FriendListPk : 您可能需要你一旦为了得到让用户实体执行附加查询FriendList通过将检索到的User.userId值作为的一部分FriendListPk

@Repository
public interface FriendListDao extends JpaRepository<FriendList, FriendListPK>{
    ... // use one of the find methods to get by pk.
}

Oops, silly me, turns out I was just missing an "s" in the setter. 糟糕,我很傻,原来我在二传手中只是想念一个“ s”。

public void setFriendsList(Set<FriendsList> friendList) {
    this.friendsList = friendsList;
}

the "friendlist" argurment should in fact be "friendslist". 实际上,“朋友列表”应该是“朋友列表”。 Hibernate was fetching the data, but when it tried to assign it to the field, the existing instance variable was silently being assigned to itself rather than the argument of the setter. Hibernate正在获取数据,但是当它试图将其分配给字段时,现有的实例变量将被默默地分配给自己而不是分配器的参数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM