Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
684 views
in Technique[技术] by (71.8m points)

java - Foreign key as a part of composite primary key and ManyToOne relationship in OpenJPA

I want to create simple database with OpenJPA 2.3.

TableA:
- f_id    PK
- item    PK
- release PK
- b_id    PK
- field1
- field2

TableB:
- id      PK
- name
- date

Where b_id in TableA references to id in TableB (MANY TableA rows to ONE TableB row). b_id is a part of entire composite primary key.

TableA class:

@Entity 
@IdClass(TableA_PK.class)
public class TableA implements Serializable {
    @Id
    private int fId;
    @Id
    private String item;
    @Id
    private String release;
    @Id
    @ManyToOne
    @PrimaryKeyJoinColumn(name="b_id", referencedColumnName="id")
    private TableB tableB;
    @Column
    private String field1;
    @Column
    private String field2;

    public TableA() {}
    //getters, setters, equals, hashCode methods
}

TableA primary key class:

public class TableA_PK implements Serializable {
    private int fId;
    private String item;
    private String release;
    private TableB tableB;

    public TableA_PK() {}
    //getters, setters, equals, hashCode methods
}

TableB class:

 @Entity 
 public class TableB implements Serializable {
    @Id
    @GeneratedValue
    private long id;
    @Column
    private String name;
    @Column
    private Date date;

    @OneToMany(mappedBy="tableB", fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
    private List<TableA> rows;

    public TableB() {}
    //getters, setters, equals, hashCode methods
}

When I try to persist TableB object Exception had been thrown:

org.apache.openjpa.persistence.ArgumentException: Attempt to map "model.TableA.tableB" failed: the owning entity is not mapped.

How to fix this?

My theory/explanation: It looks like, when I try to persist TableB object every element of rows must be persisted. But in TableA there is a field private TableB tableB (which is not persisted yet), so we fall into infinity recursion ;).

EDIT:

Full Trace (before changes):

<openjpa-2.3.0-r422266:1540826 fatal user error> org.apache.openjpa.persistence.ArgumentException: Errors encountered while resolving metadata.  See nested exceptions for details.
    at org.apache.openjpa.meta.MetaDataRepository.resolve(MetaDataRepository.java:675)
    at org.apache.openjpa.meta.MetaDataRepository.getMetaDataInternal(MetaDataRepository.java:418)
    at org.apache.openjpa.meta.MetaDataRepository.getMetaData(MetaDataRepository.java:389)
    at org.apache.openjpa.jdbc.meta.MappingRepository.getMapping(MappingRepository.java:354)
    at org.apache.openjpa.jdbc.meta.MappingTool.getMapping(MappingTool.java:682)
    at org.apache.openjpa.jdbc.meta.MappingTool.buildSchema(MappingTool.java:754)
    at org.apache.openjpa.jdbc.meta.MappingTool.run(MappingTool.java:652)
    at org.apache.openjpa.jdbc.kernel.JDBCBrokerFactory.synchronizeMappings(JDBCBrokerFactory.java:154)
    at org.apache.openjpa.jdbc.kernel.JDBCBrokerFactory.synchronizeMappings(JDBCBrokerFactory.java:164)
    at org.apache.openjpa.jdbc.kernel.JDBCBrokerFactory.newBrokerImpl(JDBCBrokerFactory.java:122)
    at org.apache.openjpa.kernel.AbstractBrokerFactory.newBroker(AbstractBrokerFactory.java:209)
    at org.apache.openjpa.kernel.DelegatingBrokerFactory.newBroker(DelegatingBrokerFactory.java:155)
    at org.apache.openjpa.persistence.EntityManagerFactoryImpl.createEntityManager(EntityManagerFactoryImpl.java:226)
    at org.apache.openjpa.persistence.EntityManagerFactoryImpl.createEntityManager(EntityManagerFactoryImpl.java:153)
    at org.apache.openjpa.persistence.EntityManagerFactoryImpl.createEntityManager(EntityManagerFactoryImpl.java:59)
    at org.JpaTest.test(JpaTest.java:40)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: <openjpa-2.3.0-r422266:1540826 fatal user error> org.apache.openjpa.persistence.ArgumentException: The id class specified by type "class org.model.TableA" does not match the primary key fields of the class.  Make sure your identity class has the same primary keys as your persistent type, including pk field types. Mismatched property: "tableB"
    at org.apache.openjpa.meta.ClassMetaData.validateAppIdClassPKs(ClassMetaData.java:2225)
    at org.apache.openjpa.meta.ClassMetaData.validateAppIdClass(ClassMetaData.java:2099)
    at org.apache.openjpa.meta.ClassMetaData.validateIdentity(ClassMetaData.java:2035)
    at org.apache.openjpa.meta.ClassMetaData.validateMeta(ClassMetaData.java:1947)
    at org.apache.openjpa.meta.ClassMetaData.resolve(ClassMetaData.java:1808)
    at org.apache.openjpa.meta.MetaDataRepository.processBuffer(MetaDataRepository.java:829)
    at org.apache.openjpa.meta.MetaDataRepository.resolveMeta(MetaDataRepository.java:726)
    at org.apache.openjpa.meta.MetaDataRepository.resolve(MetaDataRepository.java:650)
    ... 38 more
NestedThrowables:
<openjpa-2.3.0-r422266:1540826 fatal user error> org.apache.openjpa.persistence.ArgumentException: Attempt to map "org.model.TableA.tableB" failed: the owning entity is not mapped.
    at org.apache.openjpa.jdbc.meta.MappingInfo.assertTable(MappingInfo.java:628)
    at org.apache.openjpa.jdbc.meta.MappingInfo.createForeignKey(MappingInfo.java:1080)
    at org.apache.openjpa.jdbc.meta.ValueMappingInfo.getTypeJoin(ValueMappingInfo.java:115)
    at org.apache.openjpa.jdbc.meta.ValueMappingInfo.getTypeJoin(ValueMappingInfo.java:92)
    at org.apache.openjpa.jdbc.meta.strats.RelationFieldStrategy.map(RelationFieldStrategy.java:166)
    at org.apache.openjpa.jdbc.meta.FieldMapping.setStrategy(FieldMapping.java:146)
    at org.apache.openjpa.jdbc.meta.RuntimeStrategyInstaller.installStrategy(RuntimeStrategyInstaller.java:82)
    at org.apache.openjpa.jdbc.meta.FieldMapping.resolveMapping(FieldMapping.java:496)
    at org.apache.openjpa.jdbc.meta.FieldMapping.resolve(FieldMapping.java:461)
    at org.apache.openjpa.jdbc.meta.strats.RelationToManyInverseKeyFieldStrategy.map(RelationToManyInverseKeyFieldStrategy.java:135)
    at org.apache.openjpa.jdbc.meta.strats.RelationCollectionInverseKeyFieldStrategy.map(RelationCollectionInverseKeyFieldStrategy.java:94)
    at org.apache.openjpa.jdbc.meta.FieldMapping.setStrategy(FieldMapping.java:146)
    at org.apache.openjpa.jdbc.meta.RuntimeStrategyInstaller.installStrategy(RuntimeStrategyInstaller.java:82)
    at org.apache.openjpa.jdbc.meta.FieldMapping.resolveMapping(FieldMapping.java:496)
    at org.apache.openjpa.jdbc.meta.FieldMapping.resolve(FieldMapping.java:461)
    at org.apache.openjpa.jdbc.meta.ClassMapping.resolveMapping(ClassMapping.java:854)
    at org.apache.openjpa.meta.ClassMetaData.resolve(ClassMetaData.java:1811)
    at org.apache.openjpa.meta.MetaDataRepository.processBuffer(MetaDataRepository.java:829)
    at org.apache.openjpa.meta.MetaDataRepository.resolveMapping(MetaDataRepository.java:784)
    at org.apache.openjpa.meta.MetaDataRepository.resolve(MetaDataRepository.java:664)
    at org.apache.openjpa.meta.MetaDataRepository.getMetaDataInternal(MetaDataRepository.java:418)
    at org.apache.openjpa.meta.MetaDataRepository.getMetaData(MetaDataRepository.java:389)
    at org.apache.openjpa.jdbc.meta.MappingRepository.getMapping(MappingRepository.java:354)
    at org.apache.openjpa.jdbc.meta.MappingTool.getMapping(MappingTool.java:682)
    at org.apache.openjpa.jdbc.meta.MappingTool.buildSchema(MappingTool.java:754)
    at org.apache.openjpa.jdbc.meta.MappingTool.run(MappingTool.java:652)
    at org.apache.openjpa.jdbc.kernel.JDBCBrokerFactory.synchronizeMappings(JDBCBrokerFactory.java:154)
    at org.apache.openjpa.jdbc.kernel.JDBCBrokerFactory.synchronizeMappings(JDBCBrokerFactory.java:164)
    at org.apache.openjpa.jdbc.kernel.JDBCBrokerFactory.newBrokerImpl(JDBCBrokerFactory.java:122)
    at org.apache.openjpa.kernel.AbstractBrokerFactory.newBroker(AbstractBrokerFactory.java:209)
    at org.apache.openjpa.kernel.DelegatingBrokerFactory.newBroker(DelegatingBrokerFactory.java:155)
    at org.apache.openjpa.persistence.EntityManagerFactoryImpl.createEntityManager(EntityManagerFactoryImpl.java:226)
    at org.apache.openjpa.persistence.EntityManagerFactoryImpl.createEntityManager(EntityManagerFactoryImpl.java:153)
    at org.apache.openjpa.persistence.EntityManagerFactoryImpl.createEntityManager(EntityManagerFactoryImpl.java:59)
    at org.JpaTest.test(JpaTest.java:40)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
    at org.eclipse.jdt.in

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

This works for me for the following code. Changed the long tableB in TableA_PK and @JoinColumn in TableA.

TABLEA

@Entity 
@IdClass(TableA_PK.class)
public class TableA implements Serializable {
    @Id
    private int fId;
    @Id
    private String item;
    @Id
    private String release;
    @Id
    @ManyToOne
    @JoinColumn(name="b_id")
    private TableB tableB;
    @Column
    private String field1;
    @Column
    private String field2;

TABLEA_PK

public class TableA_PK implements Serializable {
    private int fId;
    private String item;
    private String release;
    private long tableB;

    public TableA_PK() {}
    //getters, setters, equals, hashCode methods

TABLEB

@Entity 
public class TableB implements Serializable {
   @Id
   @GeneratedValue
   private long id;
   @Column
   private String name;
   @Column
   private Date date;

   @OneToMany(mappedBy="tableB")
   private List<TableA> rows;

   public TableB() {}
   //getters, setters, equals, hashCode methods

UPDATE after comment
Unfortunately generated value is not supported for derived keys and its not known before item is inserted. So you need first to persist tableB item and then add rows. Check the following code:

    TableB tableb = new TableB();
    tableb.setDate(new Date());
    tableb.setName("tableb2");
    em.persist(tableb);  // fills tableb id
    System.out.println(tableb); 
    TableA tableA = new TableA();
    tableA.setfId((int) new Date().getTime());
    tableA.setField1("field1");
    tableA.setField2("field2");
    tableA.setItem("item2");
    tableA.setRelease("1");
    tableA.setTableB(tableb);
    ArrayList<TableA> rows = new ArrayList<TableA>();
    rows.add(tableA);
    tableb.setRows(rows);
    em.merge(tableb);  // inserts tablea objects, you could also just persist tableA items

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...