6

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?

jnr
  • 790
  • 1
  • 7
  • 9
  • 1
    Similar setup works fine for me. Can you show your testing code? – axtavt Feb 22 '11 at 11:00
  • Do you use Hibernate 3.6.1 or something else? – jnr Feb 22 '11 at 11:30
  • I test it in the following way: insert some data manually to the database, then just call in junit test something like entityManager.find(Question.class, 463L); – jnr Feb 22 '11 at 11:56

3 Answers3

3

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.

rico
  • 1,843
  • 2
  • 24
  • 41
1

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

Felipe Oliveira
  • 1,041
  • 9
  • 9
  • No, I don't use second-level cache, the problem arise when there is nothing in session (just started), when there are no Language instances in the session. – jnr Mar 23 '11 at 08:12
1

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

ThzChz
  • 125
  • 2
  • 8