简体   繁体   中英

Mapping a Map<Entity, Entity> in JPA results in table is not present in this descriptor

I need to map a Java Map where the key and value are both JPA entities. I read in a JPA 2 book (Pro JPA 2) that this should be done with a @OneToMany or @ManyToMany annotation instead of ElementCollection since both are JPA Entities.

I can get a @ManyToMany annotation to work without a problem and with no attributes or anything. But as far as I understand, the relationship is a one-to-many. For any value entity, there can only be one entity of the class that holds the map (do I understand @OneToMany correctly here as it relates to a Map?).

Simply annotating the map with @OneToMany like so:

@OneToMany
private Map<Stage, ScoreCard> scoreCards;

results in this exception:

[EL Severe]: 2018-01-11 21:11:10.755--ServerSession(53937593)--Exception [EclipseLink-0] (Eclipse Persistence Services - 2.7.0.v20170811-d680af5): org.eclipse.persistence.exceptions.IntegrityException Descriptor

Exceptions:

Exception [EclipseLink-93] (Eclipse Persistence Services - 2.7.0.v20170811-d680af5): org.eclipse.persistence.exceptions.DescriptorException Exception Description: The table [SCORECARD] is not present in this descriptor. Descriptor: RelationalDescriptor(fi.ipsc_result_server.domain.ResultData.CompetitorResultData --> [DatabaseTable(COMPETITORRESULTDATA)])

Exception [EclipseLink-41] (Eclipse Persistence Services - 2.7.0.v20170811-d680af5): org.eclipse.persistence.exceptions.DescriptorException Exception Description: A non-read-only mapping must be defined for the sequence number field. Descriptor: RelationalDescriptor(fi.ipsc_result_server.domain.ResultData.CompetitorResultData --> [DatabaseTable(COMPETITORRESULTDATA)])

I have found many examples of JPA annotations for Map but none that would show how to annotate a map with entities as key and value.

Both classes are annotated with @Entity and have an Id and can be persisted without problem except for when trying to get this Map persisted, so the problem is with the annotation for the Map.

As a test, I wrote a test class that has a Map: package fi.ipsc_result_server.domain.ResultData;

@Entity
public class TestClass {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    Long id;

    @OneToMany
    @MapKeyJoinColumn(name="testKey")
    Map<TestKeyClass, TestValueClass> testMap;

[getters and setters]
}
package fi.ipsc_result_server.domain.ResultData;
@Entity
public class TestKeyClass {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    Long id;

[getter and setter for id]
}

package fi.ipsc_result_server.domain.ResultData;

@Entity
public class TestValueClass {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    Long id;

[getter and setter for id]
}

This results in the same exception:

Descriptor Exceptions:

Exception [EclipseLink-93] (Eclipse Persistence Services - 2.7.0.v20170811-d680af5): org.eclipse.persistence.exceptions.DescriptorException Exception Description: The table [TESTVALUECLASS] is not present in this descriptor. Descriptor: RelationalDescriptor(fi.ipsc_result_server.domain.ResultData.TestClass --> [DatabaseTable(TESTCLASS)])

Exception [EclipseLink-41] (Eclipse Persistence Services - 2.7.0.v20170811-d680af5): org.eclipse.persistence.exceptions.DescriptorException Exception Description: A non-read-only mapping must be defined for the sequence number field. Descriptor: RelationalDescriptor(fi.ipsc_result_server.domain.ResultData.TestClass --> [DatabaseTable(TESTCLASS)])

Map<Entity,Entity> can be used both with @OneToMany and @ManyToMany . You just have to add one additional annotation:

@OneToMany
@MapKeyJoinColumn(name="stage")
private Map<Stage, ScoreCard> scoreCards;

where name is:

The name of the foreign key column for the map key.

Update

You need to make sure that the value entity has the reference to the key entity:

public clas ScoreCard{

  @ManyToOne
  private Stage stage;
}

So I got this to work by adding, not a @MapKeyJoinColumn, but a @JoinColumn to the annotation on the Map. In the TestClass which I included at the end of my question, this works, the tables are created and I can persist an instance of TestClass, once I have persisted the instance of TestKeyClass since cascading apparently doesn't include the key value entities:

@OneToMany(cascade = CascadeType.PERSIST)
@JoinColumn(name="TESTCOLUMN")
Map<TestKeyClass, TestValueClass> testMap;

So. I have no idea why this works, why its required and why @MapKeyJoinColumn doesn't work. The above annotation generates a TESTVALUECLASS table with columns "TESTCOLUMN" (from the @JoinColumn annotation) and "testMap_KEY" column. Column "TESTCOLUMN" is the join column to TestClass and has the id of the TestClass entity (the class that has the map). "testMap_KEY" is the id of the TestKeyClass instance which serves as the key for the particular value. So I guess for some reason EclipseLink wasn't generating the required columns without the @JoinColumn annotation.

At one point I refreshed my admittedly meager knowledge of JPA by reading a bit and figuring I'm going to get a good handle of JPA during my current project. And as has previously been the case, I'm about as perplexed by some of JPA's aspects as always and just feel like "oh well, what ever, it works"... Oh well! Thanks for your help, Maciej!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM