Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Explicit Hibernate join table

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).

like image 574
maaartinus Avatar asked Sep 12 '15 22:09

maaartinus


1 Answers

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;
    }
}
like image 185
serhii Avatar answered Sep 27 '22 22:09

serhii