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