4

I'm having two entities Car and CarDescription where CarDescription is depending on another foreign key from the table Language.

What I' trying to accomplish is to have a HashMap in Car such that whenever I'm having a Car entity-object I am able to access all descriptions from the language id.

Entity Car.java

@Entity
@Table(name = "Car")
public class Car extends AbstractTimestampEntity implements Serializable {
    private static final long serialVersionUID = -5041816842632017838L;

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name = "ID", unique = true, nullable = false)
    private Long id;

    @OneToMany(mappedBy="car")
    @MapKeyColumn(name = "language_ID")
    // @MapKey(name = "language") // does not work either 
    private Map<Long, CarDescription> carDescription = new HashMap<>(0);
}

Entity CarDescription.java

@Entity
@Table( name="car_description",
        uniqueConstraints = {
            @UniqueConstraint(columnNames={"language_id", "name"}) 
        }
)
public class CarDescription extends AbstractTimestampEntity implements Serializable {
    private static final long serialVersionUID = 2840651722666001938L;

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name = "ID", unique = true, nullable = false)
    private Long id;

    @NotNull
    @ManyToOne
    private Car car;

    @NotNull
    @OneToOne
    private Language language;

    // ..
}

Entity Language.java

@Entity
public class Language implements Serializable {
    private static final long serialVersionUID = 3968717758435500381L;

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name="ID")
    private Long id;

    // ..
}

The problem I am having is that the mapping gives me a map from each CarDescription.id to CarDescription.

How can I accomplish a correct mapping?

Vlad Mihalcea
  • 142,745
  • 71
  • 566
  • 911
Stefan Falk
  • 23,898
  • 50
  • 191
  • 378
  • Why does `@MapKey(name = "language")` not suit you? – Dragan Bozanovic Jul 05 '15 at 22:24
  • @DraganBozanovic It's the same.. I'm always getting a map `CarDescription.id -> CarDescription` but what I want is `Language.id -> CarDescription`. I don't know but `@MapKey(name = "language")` does not work.. :/ – Stefan Falk Jul 05 '15 at 22:33
  • It seems to be ignored. Are imports good, is it an annotation from `javax.persistence`? – Dragan Bozanovic Jul 05 '15 at 22:46
  • @DraganBozanovic Yes, it's from `javax.persistence`. Another thing I've tried is `@MapKeyColumn (name = "language_ID")` but that didn't work either but in that case the map is just empty .. No idea what's not working here – Stefan Falk Jul 05 '15 at 22:53
  • You tried with `@MapKeyColumn` together with `@MapKey` or without it? – Dragan Bozanovic Jul 05 '15 at 22:58
  • @DraganBozanovic without it. Would I have to use both? I tried it due to the [documentation](https://docs.jboss.org/hibernate/orm/3.6/reference/en-US/html/collections.html#collections-indexed) that says: "*@MapKeyColumn if the map key is a basic type. If you don't specify the column name, the name of the property followed by underscore followed by KEY is used (for example orders_KEY).*" – Stefan Falk Jul 05 '15 at 22:58
  • No, I don't think so. – Dragan Bozanovic Jul 05 '15 at 22:59
  • @DraganBozanovic Hmm.. I just don't get it. It should work that way .. – Stefan Falk Jul 05 '15 at 23:00
  • Your one-to-many association is wrong, it should be `mappedBy = "car"`. Then it should work with @MapKeyColumn. – Dragan Bozanovic Jul 05 '15 at 23:10
  • @DraganBozanovic I've now tried `@OneToMany(cascade=CascadeType.ALL, mappedBy = "car") @MapKeyColumn (name = "language_ID")` but the map is here still empty :/ – Stefan Falk Jul 05 '15 at 23:18

1 Answers1

4

In CarDescription you need to add the languageId property:

@Column(name = "language_id", insertable = false, updatable = false)
private Long languageId;

@NotNull
@OneToOne
@JoinColumn(name = "language_id")
private Language language;

public void setLanguage(Language language) {
    this.languageId = language.getId();
    this.language = language;
} 

Then you can use it in the Car entity like this:

@OneToMany(mappedBy="car")
@MapKey(name = "languageId")
private Map<Long, CarDescription> carDescription = new HashMap<>(0);
Vlad Mihalcea
  • 142,745
  • 71
  • 566
  • 911
  • Hi! Thanks for stopping by! :) Well, I've tried that but now I'm getting `org.hibernate.NonUniqueObjectException` "*a different object with the same identifier value was already associated with the session*" on `CarDescription`. Can't get around it :/ – Stefan Falk Jul 06 '15 at 06:57
  • That's something completely unrelated to the current question. That can happen when you call `Session.saveOrUpdate(entity)` when the entity is already associated to the current Session. You need to review your persistence logic thoroughly. – Vlad Mihalcea Jul 06 '15 at 07:00
  • It seems that the problem does come from `@MapsId`. The creation statement is now giving me `primary key (language_ID)` which won't work. Is there no other way to create this mapping? – Stefan Falk Jul 06 '15 at 07:41
  • Right, that only works for identifiers. I changed the answer to using `insertable = false, updatable = false`. Check the updated version. – Vlad Mihalcea Jul 06 '15 at 07:43
  • Awesome! Thank you very much I don't know if I would have solved that ever by myself! Hibernate has a ridiculously flat learning curve.. ^^ – Stefan Falk Jul 06 '15 at 07:58
  • Great trick! However, why did it not work with `@MapKeyColumn`? – Dragan Bozanovic Jul 06 '15 at 10:30
  • It should have worked with `@MapKeyColumn` as you suggested, so it must have failed because of some Hibernate bug. Could you fill a JIRA issue? – Vlad Mihalcea Jul 06 '15 at 10:40
  • Yes, I will try to reproduce it in a test case and create a jira issue. Thanks! – Dragan Bozanovic Jul 06 '15 at 10:57
  • Shouldn't `@MapKeyJoinColumn ` be used? See https://stackoverflow.com/questions/25439813/difference-between-mapkey-mapkeycolumn-and-mapkeyjoincolumn-in-jpa-and-hiber#25447741 – bgraves Mar 18 '19 at 13:21