Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Key & value column name overrides when mapping java.util.Map with JPA annotations

I am looking at the different ways of annotating maps using Hibernate 4.1.9 & JPA annotations.

If I want to store a Map where the key is an attribute of the entity value the mark up looks like this

    @OneToMany(mappedBy = "deptById", targetEntity = com.demo.impls.Employee.class)
    @MapKey(name = "entityId")
    private Map<Long, Employee> employeesById;

Note the above mark up does not create a join table but the Map is returned via a query at run-time, so the Map is dynamic and you do not have to add element into the map in Java for them to be returned by the query.

Now I want the contents of the Map to reflect what the application has added into the Map rather than performing a dynamic query.

There are 4 varieties of Map I want to store

    private Map<String, String> map0;
    private Map<String, Entity> map1;
    private Map<Entity, String> map2;
    private Map<Entity, Entity> map3;

In these cases there is NO relationship between the key & and value nor is there any relationship to the holding Entity. I have to be able to specify the name of the join table was well as the column names for the key & value.

I have tried the following

@Entity
public class Department {
    @ElementCollection
    @CollectionTable(name = "TEST_MAP0")
    @Column(name="value")
    @MapKeyColumn(name="Key")
    private Map<String, String> map0;

    @ElementCollection(targetClass = com.demo.bb.impls.Employee.class)
    @CollectionTable(name = "TEST_MAP1")
    @Column(name="value")
    @MapKeyColumn(name="Key")
    private Map<String, Employee> map1;

    @ElementCollection
    @MapKeyClass(value = com.demo.bb.impls.Employee.class)
    @CollectionTable(name = "TEST_MAP2")
    @Column(name="value")
    @MapKeyColumn(name="Key")
    private Map<Employee, String> map2;

    @ElementCollection(targetClass = com.demo.bb.impls.ParkingSpace.class)
    @MapKeyClass(value = com.demo.bb.impls.Employee.class)
    @CollectionTable(name = "TEST_MAP3")
    @Column(name="value")
    @MapKeyColumn(name="Key")
    private Map<Employee, ParkingSpace> map3;

Case 0 Map works fine & the generated join table has columns DEPARTMENT, VALUE, KEY

The other three cases work in as much as you can store data in the tables & in Java interrogate the tables with the relevant keys/values & get back the expected results - i.e. it does handle storing Entities using @ElementCollection

But the column name overrides using @Column(name="value") & @MapKeyColumn(name="key") are ignored when the key or value is an Entity.

I have tried using @ManyToMany annotations as follows

    @ManyToMany(targetEntity = com.demo.bb.impls.Employee.class)
    @JoinTable(name = "TEST_MAP1_B")
    @Column(name="value")
    @MapKeyColumn(name="Key")
    private Map<String, Employee> map1_B;

    @ManyToMany(targetEntity = com.demo.bb.impls.ParkingSpace.class)
    @MapKeyClass(value = com.demo.bb.impls.Employee.class)
    @JoinTable(name = "TEST_MAP3_B")
    @Column(name="value")
    @MapKeyColumn(name="Key")
    private Map<Employee, ParkingSpace> map3_B;

But again the key & value column names overrides are ignored. Does anybody know of a way to enforce these column name overrides.

Thanks in advance...

UPDATE....After looking at the response from @wypieprz I think I know the correct annotation to allow you to specify column names for the value & the key when the Map is keyed by a basic with an entity value.

By using the following

    @ManyToMany(targetEntity = com.demo.bb.impls.Employee.class)
    @JoinTable(name = "TEST_MAP1", inverseJoinColumns=@JoinColumn(name="VALUE"))
    @MapKeyColumn(name="KEY")
    private Map<String, Employee> map1;

Using the inverseJoinColumn I can specify the value column name.

But If the key is an Entity I have not found a way to specify the key column name. As the doc say @MapKeyColumn "specifies the mapping for the key column of a map whose map key is a basic type"

I am also not sure of the annotations to use when the key is an Entity & the value is a basic. Using ManyToMany just does not work & I think I may have to use ElementCollection but again I cannot find a way to specify the key column name.

UPDATE 2... Thanks to Peter Halicky for a solution.

In summary to name all 3 columns on each of the cases you need to do something like this.

@ElementCollection
@CollectionTable(name = "TEST_MAP0", joinColumns = @JoinColumn(name = "DEPARTMENT"))
@Column(name = "value")
@MapKeyColumn(name = "key")
private Map<String, String> map0;

@ManyToMany(targetEntity = com.hibernate.elephants.Employee.class)
@JoinTable(name = "TEST_MAP1", joinColumns = @JoinColumn(name = "DEPARTMENT"), inverseJoinColumns = @JoinColumn(name = "value"))
@MapKeyColumn(name = "key")
private Map<String, Employee> map1;

@ElementCollection
@CollectionTable(name = "TEST_MAP2", joinColumns = @JoinColumn(name = "DEPARTMENT"))
@MapKeyClass(value = com.hibernate.elephants.Employee.class)
@MapKeyJoinColumn(name = "key")
@Column(name = "value")
private Map<Employee, String> map2;

@ManyToMany(targetEntity = com.hibernate.elephants.ParkingSpace.class)
@JoinTable(name = "TEST_MAP3", joinColumns = @JoinColumn(name = "DEPARTMENT"), inverseJoinColumns = @JoinColumn(name = "value"))
@MapKeyClass(value = com.hibernate.elephants.Employee.class)
@MapKeyJoinColumn(name="key")
private Map<Employee, com.hibernate.elephants.ParkingSpace> map3;

Note two cases are specified as ElementCollection but the two cases where the value is another Entity need to use ManyToMany.

like image 501
James Clayton Avatar asked Jun 10 '14 10:06

James Clayton


People also ask

What is called key?

1a : a usually metal instrument by which the bolt of a lock is turned. b : any of various devices having the form or function of such a key a key for winding a clock. 2a : a means of gaining or preventing entrance, possession, or control. b : an instrumental or deciding factor the key to her success.

What does the term key in mean?

phrasal verb. If you key something in, you put information into a computer or you give the computer a particular instruction by typing the information or instruction on the keyboard. Brian keyed in his personal code. [

IS key married Kpop?

THIS JUST IN: Cosmopolitan Magazine have just released the first photos of Global We Got Married couple: SHINee's Key and his model wife's Arisa Yagi together.

Is the key or is key?

"Is the key" implies that it is the only, or most important, factor. "Is key" means it's important.


1 Answers

I'm using an entity as a key to a Map, as below. Using the @MapKeyJoinColumn annotation I could specify the name of the column that is the key of the map. This worked for me on Hibernate, not sure what other JPA implementations will do, but it surely is worth trying.

@ElementCollection
@CollectionTable(name="breed_descriptions", joinColumns={ @JoinColumn(name="breed") })
@Column(name="description")
@MapKeyJoinColumn(name="language")
private Map<Language, String> descriptions = new HashMap<>();
like image 183
Peter H Avatar answered Oct 01 '22 23:10

Peter H