Yaroslav Lozynskyi
Yaroslav Lozynskyi

Reputation: 800

JPA 2.0 Hibernate @OneToMany + @MapKeyJoinColumn

OneToMany + MapKeyJoinColumn doesn't work for me, please suggest what I'm doing wrong.

I'm using JPA 2.0 + Hibernate 3.6.1 And want to map following tables:


Question to Statement to Language relationship


To Classes:



@Entity
public class Question {
    // id and other fields

    @OneToMany(mappedBy="question", cascade = CascadeType.ALL)
    @MapKeyJoinColumn(name="language_id")
    private Map<Language, Statement> statements =
        new HashMap<Language, Statement>();
}

@Entity
public class Statement {
    @Id
    private Long id;

    @ManyToOne
    @JoinColumn(name = "language_id", nullable = true)
    private Language language;

    @ManyToOne
    @JoinColumn(name = "question_id", nullable = false)
    private Question question;

    @Column(name = "message")
    private String message;
}

@Entity
public class Language {
    @Id
    private Long id;

    @Column(name = "name")
    private String name;
}

But it doesn't work. EntityManager persists it correctly, but when I retrieve Question, in it's statements map there is only one language-to-null entry. Please, help


Edit1: Strange, but when I preload all Languages, like this:


String sql = "select l from Language l";
List languages = entityManager.createQuery(sql, Language.class).getResultList();

then it works!
Does anybody know how to make hibernate automatically load all objects of certain class?

Upvotes: 6

Views: 7311

Answers (3)

rico
rico

Reputation: 1935

In fact, the id property in your class Statement must be a composite primary key. You do not need a statement_id in your table Statement. The primary key is composed of both language_id and question_id.

Try that :

    @Entity
    public class Statement implements Serializable {

        private static final long serialVersionUID = 1L;

        @EmbeddedId
        private StatementId id = new StatementId();

        @Column(name = "message")
        private String message;
    }

    @Embeddable
    private static class StatementId implements Serializable {

        private static final long serialVersionUID = 1L;

        StatementId(){}

        @ManyToOne
        private Question product;

        @ManyToOne
        private Language language;

    }

And do not forget the fetchType in Question in order to avoid LazyInitializationExceptions.

public class Question {

    @OneToMany(mappedBy="id.question", fetch=FetchType.EAGER)
    @MapKeyJoinColumn(name="language_id")
    private Map<Language, Statement> statements = new HashMap<Language, Statement>();
}

Hope it will help.

Upvotes: 3

ThzChz
ThzChz

Reputation: 125

Try calling statements.size() to make sure the data are fetched or you could try defining the fetch type of your relationship to EAGER : @OneToMany(mappedBy="question", cascade = CascadeType.ALL, fetch=FetchType.EAGER)

You might want to read this : http://en.wikibooks.org/wiki/Java_Persistence/Relationships#Eager_Join_Fetching

Upvotes: 1

Felipe Oliveira
Felipe Oliveira

Reputation: 1041

Are you using second-level caching? Is your object already part of the session (first-level caching)? Try doing a session clear()

Upvotes: 1

Related Questions