I'm having an m:n relationsship like here, except for that there's some information be to associated with the AuthorBook
entity. So I guess, I need no @JoinTable
at all and should create a class AuthorBook
consisting of one link to each of Author
and Book
and the additional information, right?
I tried it like
@Entity
public class AuthorBook {
@Id @ManyToOne Author author;
@Id @ManyToOne Book book;
@LobOrWhatever AdditionalInformation additionalInformation;
}
and I ran into problems (I can't recall exactly anymore). So I've introduced a @GeneratedId int id
and changed the references to @NaturalId
. This works mostly well, but funnily, it forces me to pass attached Author
and Book
to Session#byNaturalId
which makes no sense to me (I have the ids and the entities should be fetched together). I also tried
session.byNaturalId(AuthorBook.class)
.using("author.id", someAuthorId)
.using("book.id", someBookId)
.load();
but this was refused telling me that "author.id" is no natural id (which is actually true as the natural id is "author", but its "id" works equally well).
Looking at the web, I find everyone using ids instead of entities in such a table, so I wonder if I'm trying something impossible.... (or stupid).
UPD: Removed unnecessary @AssociationOverrides
. Added @ManyToOne(fetch = FetchType.LAZY)
to avoid StackOverflowException
on cascade
modifications.
I had similar problem recently.
I found my solution with @EmbededId
.
In my database there is a table Rating that provides many-to-many relation between User and Movie, so userId and movieId are foreign keys and form a composite primary key. And this table also contains some additional information.
Here my code, I hope it will help you:
Movie:
@Entity
@Table(name = "movie")
public class Movie {
@Id
@Column(name = "movie_id")
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "primaryKey.movie")
private List<Rating> ratings;
}
User:
@Entity
@Table(name = "imdb_user")
public class User {
@Id
@Column(name = "imdb_user_id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "primaryKey.user")
private List<Rating> ratings;
}
Rating:
@Entity
@Table(name = "rating")
public class Rating {
@EmbeddedId
private RatingId primaryKey = new RatingId();
@Column(name = "rating_value")
private Integer ratingValue;
}
Utility class that implements composite id for Rating:
@Embeddable
public class RatingId implements Serializable{
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "movie_id")
private Movie movie;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "imdb_user_id")
private User user;
/*Generated by IDEA*/
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
RatingId ratingId = (RatingId) o;
if (movie != null ? !movie.equals(ratingId.movie) : ratingId.movie != null) return false;
return !(user != null ? !user.equals(ratingId.user) : ratingId.user != null);
}
@Override
public int hashCode() {
int result = movie != null ? movie.hashCode() : 0;
result = 31 * result + (user != null ? user.hashCode() : 0);
return result;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With