Trouble with Hibernate Mapping

Exception in thread “main” org.hibernate.exception.ConstraintViolationException: Duplicate entry.

This bug arose when I was attempting to map two entities having a parent-child relationship with @OneToMany mapping. In short, there were two entities, WishList and Movie. A WishList can have one or more movies, so it was intuitive to me to use @OneToMany mapping over the movie field. However, the code crashed at the point of saving the same movie into multiple wish lists, complaining that the movie was already present and assigned to a wish list.

WishList

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Entity
@Table(name = "WishLists")
public class WishList {

private int wishListId;
private List<Movie> movies;
...

public WishList() { }

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
public int getWishListId() {
return wishListId;
}

@OneToMany(cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)
public List<Movie> getMovies() {
return movies;
}

...

}

Movie

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Entity
@Table(name = "Movies")
public class Movie {

private int movieId;
...

public Movie() { }

@Id
public int getMovieId() {
return movieId;
}

...

}

It turned out that Hibernate treats the “Many” part of @OneToMany an unique entity in the join table, meaning that once movie 1 is saved to wish list 1, movie 1 should no longer being saved to another wish list. An example of this would be a phone and phone call history. Each phone logs its unique phone call history. If you try to save the phone call history to a different phone, Hibernate will complain about “duplicate key constraint” because a history should be unique to individual phone. In my case, since different wish lists should be able to share a same movie, I should’ve used the @ManyToMany mapping and supply a @JoinTable explicitly to make a “movie” public to every wish list.

WishList

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@Entity
@Table(name = "WishLists")
public class WishList {

private int wishListId;
private List<Movie> movies;
...

public WishList() { }

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
public int getWishListId() {
return wishListId;
}

@ManyToMany(cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)
@JoinTable(
name = "WishListMovies",
joinColumns = {@JoinColumn(name = "wishListId")},
inverseJoinColumns = {@JoinColumn(name = "movieId")}
)
public List<Movie> getMovies() {
return movies;
}

...

}

Movie

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Entity
@Table(name = "Movies")
public class Movie {

private int movieId;
...

public Movie() { }

@Id
public int getMovieId() {
return movieId;
}

...

}