having this Entities:
User.java:
@Entity
@Data
@NoArgsConstructor
public class User {
@Id @GeneratedValue
private int id;
private String username;
private String about;
@OneToMany(mappedBy = "owner", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
private Map<User, Friendship> friendships = new HashMap<>();
@OneToMany(mappedBy = "author", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
private Collection<Post> posts = new ArrayList<>();
public User(String username) {
this.username = username;
}
public void addFriend(User friend){
Friendship friendship = new Friendship();
friendship.setOwner(this);
friendship.setFriend(friend);
this.friendships.put(friend, friendship);
}
public void addPost(Post post){
post.setAuthor(this);
this.posts.add(post);
}
}
Friendship.java:
@Entity
@Getter
@Setter
@NoArgsConstructor
public class Friendship {
@EmbeddedId
private FriendshipId key = new FriendshipId();
private String level;
@ManyToOne
@MapsId("ownerId")
private User owner;
@ManyToOne
@MapsId("friendId")
private User friend;
}
FriendshipId.java:
@Embeddable
public class FriendshipId implements Serializable {
private int ownerId;
private int friendId;
}
UserRepository.java:
public interface UserRepository extends JpaRepository<User, Integer> {
public User findByUsername(String username);
}
and finaly DemoApplication.java:
@Bean
public CommandLineRunner dataLoader(UserRepository userRepo, FriendshipRepository friendshipRepo){
return new CommandLineRunner() {
@Override
public void run(String... args) throws Exception {
User f1 = new User("friend1");
User f2 = new User("friend2");
User u1 = new User("user1");
u1.addFriend(f1);
u1.addFriend(f2);
userRepo.save(u1);
User fetchedUser = userRepo.findByUsername("user1");
System.out.println(fetchedUser);
System.out.println(fetchedUser.getFriendships().get(f1));
}
};
}
After the userRepo.save(u1)
operation, the tables are as follows:
mysql> select * from user;
+----+-------+----------+
| id | about | username |
+----+-------+----------+
| 1 | NULL | user1 |
| 2 | NULL | friend1 |
| 3 | NULL | friend2 |
+----+-------+----------+
select * from friendship;
+-------+-----------+----------+-----------------+
| level | friend_id | owner_id | friendships_key |
+-------+-----------+----------+-----------------+
| NULL | 2 | 1 | 2 |
| NULL | 3 | 1 | 3 |
+-------+-----------+----------+-----------------+
As you can see all friends were saved. However this statement:
System.out.println(fetchedUser.getFriendships().get(f1));
returns null
. Even though the fetchedUser
has the Map of friends fetched:
System.out.println(fetchedUser);
prints:
User(id=1, username=user1, about=null, friendships={User(id=2, username=friend1, about=null, friendships={}, posts=[])=com.example.demo.model.Friendship@152581e8, User(id=3, username=friend2, about=null, friendships={}, posts=[])=com.example.demo.model.Friendship@58a5d38}, posts=[])
So why does the friend f1
couldn't be fetched (more precisely is null
), when the Map friendships
is fully fetched (all friends are fetched, as you could see from the above statement) ?
PS:
I have deleted the @Data
lombok annotation (just added @Getter
,@Setter
and @NoArgsConstrutor`) and overrided the equalsAndHashCode myself:
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof User)) return false;
User user = (User) o;
return id == user.id && Objects.equals(username, user.username) && Objects.equals(about, user.about) && Objects.equals(friendships, user.friendships) && Objects.equals(posts, user.posts);
}
@Override
public int hashCode() {
return Objects.hash(id, username, about, friendships, posts);
}
Or in other words, the equals()
method uses all fields of the User
class.