Внешний ключ как часть составного первичного ключа и отношения ManyToOne в OpenJPA

Я хочу создать простую базу данных с OpenJPA 2.3.

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

TableB:
- id      PK
- name
- date

Где b_id в TableA ссылается на id в TableB (МНОГИЕ TableA строк на ОДНУ TableB строку). b_id является частью всего составного первичного ключа.

Класс 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
    @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:

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:

 @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
}

Когда я пытаюсь сохранить объект TableB, было выдано исключение:

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

Как это исправить?

Моя теория/объяснение: похоже, когда я пытаюсь сохранить объект TableB, каждый элемент rows должен быть сохранен. Но в TableA есть поле private TableB tableB (которое еще не сохранено), так что мы попадаем в бесконечную рекурсию ;).

ИЗМЕНИТЬ:

Полная трассировка (до изменений):

<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.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)

Полная трассировка, после изменений (когда я изменил тип tableB в TableA_PK на long):

<openjpa-2.3.0-r422266:1540826 nonfatal general error> org.apache.openjpa.persistence.PersistenceException: org.model.TableB cannot be cast to java.lang.Number
    at org.apache.openjpa.kernel.BrokerImpl.persistAll(BrokerImpl.java:2526)
    at org.apache.openjpa.kernel.SingleFieldManager.persist(SingleFieldManager.java:279)
    at org.apache.openjpa.kernel.StateManagerImpl.cascadePersist(StateManagerImpl.java:3081)
    at org.apache.openjpa.kernel.BrokerImpl.persistInternal(BrokerImpl.java:2703)
    at org.apache.openjpa.kernel.BrokerImpl.persist(BrokerImpl.java:2604)
    at org.apache.openjpa.kernel.BrokerImpl.persist(BrokerImpl.java:2587)
    at org.apache.openjpa.kernel.BrokerImpl.persist(BrokerImpl.java:2491)
    at org.apache.openjpa.kernel.DelegatingBroker.persist(DelegatingBroker.java:1077)
    at org.apache.openjpa.persistence.EntityManagerImpl.persist(EntityManagerImpl.java:716)
    at org.JpaTest.test(JpaTest.java:43)
    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: java.lang.ClassCastException: org.model.TableB cannot be cast to java.lang.Number
    at org.apache.openjpa.util.ApplicationIds$PrimaryKeyFieldManager.fetchLongField(ApplicationIds.java:669)
    at org.apache.openjpa.enhance.org$model$TableA$pcsubclass.pcCopyKeyFieldsToObjectId(Unknown Source)
    at org.apache.openjpa.enhance.PCRegistry.copyKeyFieldsToObjectId(PCRegistry.java:169)
    at org.apache.openjpa.util.ApplicationIds.fromPKValues(ApplicationIds.java:224)
    at org.apache.openjpa.enhance.ReflectingPersistenceCapable.pcNewObjectIdInstance(ReflectingPersistenceCapable.java:277)
    at org.apache.openjpa.util.ApplicationIds.create(ApplicationIds.java:427)
    at org.apache.openjpa.kernel.BrokerImpl.persistInternal(BrokerImpl.java:2675)
    at org.apache.openjpa.kernel.BrokerImpl.persistAll(BrokerImpl.java:2521)
    ... 32 more

Полная трассировка после изменений (когда я изменил тип tableB в TableA_PK на Long и тип id в TableB на Long):

<openjpa-2.3.0-r422266:1540826 nonfatal general error> org.apache.openjpa.persistence.PersistenceException: Can not set java.lang.Long field org.model.TableA_PK.tableB to org.model.TableB
    at org.apache.openjpa.kernel.BrokerImpl.persistAll(BrokerImpl.java:2526)
    at org.apache.openjpa.kernel.SingleFieldManager.persist(SingleFieldManager.java:279)
    at org.apache.openjpa.kernel.StateManagerImpl.cascadePersist(StateManagerImpl.java:3081)
    at org.apache.openjpa.kernel.BrokerImpl.persistInternal(BrokerImpl.java:2703)
    at org.apache.openjpa.kernel.BrokerImpl.persist(BrokerImpl.java:2604)
    at org.apache.openjpa.kernel.BrokerImpl.persist(BrokerImpl.java:2587)
    at org.apache.openjpa.kernel.BrokerImpl.persist(BrokerImpl.java:2491)
    at org.apache.openjpa.kernel.DelegatingBroker.persist(DelegatingBroker.java:1077)
    at org.apache.openjpa.persistence.EntityManagerImpl.persist(EntityManagerImpl.java:716)
    at org.JpaTest.test(JpaTest.java:49)
    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: java.lang.IllegalArgumentException: Can not set java.lang.Long field org.model.TableA_PK.tableB to org.model.TableB
    at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(Unknown Source)
    at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(Unknown Source)
    at sun.reflect.UnsafeObjectFieldAccessorImpl.set(Unknown Source)
    at java.lang.reflect.Field.set(Unknown Source)
    at org.apache.openjpa.enhance.Reflection.set(Reflection.java:538)
    at org.apache.openjpa.enhance.org$model$RowData$pcsubclass.pcCopyKeyFieldsToObjectId(Unknown Source)
    at org.apache.openjpa.enhance.PCRegistry.copyKeyFieldsToObjectId(PCRegistry.java:169)
    at org.apache.openjpa.util.ApplicationIds.fromPKValues(ApplicationIds.java:224)
    at org.apache.openjpa.enhance.ReflectingPersistenceCapable.pcNewObjectIdInstance(ReflectingPersistenceCapable.java:277)
    at org.apache.openjpa.util.ApplicationIds.create(ApplicationIds.java:427)
    at org.apache.openjpa.kernel.BrokerImpl.persistInternal(BrokerImpl.java:2675)
    at org.apache.openjpa.kernel.BrokerImpl.persistAll(BrokerImpl.java:2521)
    ... 32 more
Caused by: java.lang.IllegalArgumentException: Error while setting value org.model.TableB@982c7dfd of class org.model.TableB on field private java.lang.Long org.model.TableA_PK.tableB of instance org.model.TableA_PK@e426b053 by reflection.    
    at org.apache.openjpa.enhance.Reflection.wrapReflectionException(Reflection.java:334)
    at org.apache.openjpa.enhance.Reflection.set(Reflection.java:540)
    ... 39 more

ИЗМЕНИТЬ ПОСЛЕ ОТВЕТА @Gas

Я сделал так, как ты сказал. Но у меня проблема с сохранением всего объекта TableB, мой тестовый класс:

public class JpaTest {

    @Test
    public void test() {
        TableB tb = new TableB();
        tb.setName("testing");
        tb.setDate(new Date());

        TableA ta1 = new TableA();
        ta1.setFId(1);
        ta1.setItem("item1");
        ta1.setRelease("release1");
        ta1.setField1("f1");
        ta1.setField2("f2");

        TableA ta2 = new TableA();
        ta2.setFId(2);
        ta2.setItem("item2");
        ta2.setRelease("release2");
        ta2.setField1("F1");
        ta2.setField2("F2");

        List<TableA> alist = new ArrayList<TableA>();
        alist.add(ta1);
        alist.add(ta2);

        tb.setRows(alist);

        EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("rd-jpa");
        EntityManager em = entityManagerFactory.createEntityManager();
        EntityTransaction userTransaction = em.getTransaction();
        userTransaction.begin();
        em.persist(tb);
        userTransaction.commit();
        em.clear();
        em.close();
    }

У меня есть исключение:

org.apache.openjpa.persistence.RollbackException: The transaction has been rolled back.  See the nested exceptions for details on the errors that occurred.
(...)
Caused by: org.apache.openjpa.lib.jdbc.ReportingSQLException: ERROR: null value in column "b_id" violates not-null constraint

Почему? Я думал, что сначала будет сохраняться tb, а потом каждый элемент в rows будет знать о полученном TableB.id (благодаря полю tableB в классе TableA). Что не так с этим?

Если я добавлю две строки в Test перед сохранением:

ta1.setTableB(tb);
ta2.setTableB(tb);

это исключение будет брошено:

Caused by: java.lang.ClassCastException: org.model.TableB cannot be cast to java.lang.Number

(Мне нужно самому установить поле tableB в TableA?)

Как сохранить объект TableB?

РЕДАКТИРОВАНИЕ 3: файл persistence.xml

<?xml version="1.0" encoding="UTF-8" ?>
<persistence xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
    version="2.0" xmlns="http://java.sun.com/xml/ns/persistence">
    <persistence-unit name="rd-jpa" transaction-type="RESOURCE_LOCAL">
        <provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>
        <class>org.model.TableA</class>
        <class>org.model.TableB</class>
        <properties>
            <property name="openjpa.ConnectionURL"
                value="jdbc:postgresql://localhost:5432/mydb" />
            <property name="openjpa.ConnectionDriverName" value="org.postgresql.Driver" />
            <property name="openjpa.ConnectionUserName" value="postgres" />
            <property name="openjpa.ConnectionPassword" value="postgres" />
            <property name="openjpa.DynamicEnhancementAgent" value="true" />
            <property name="openjpa.RuntimeUnenhancedClasses" value="supported" />
            <property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema(ForeignKeys=true)" />
        </properties>
    </persistence-unit>
</persistence>

person It'sMe    schedule 28.07.2014    source источник
comment
Я считаю, что в TableA_PK вместо «TableB tableB» вам нужен только первичный ключ из tableB, а не весь объект — «private long tableB;»   -  person Gas    schedule 29.07.2014
comment
Завтра на работе проверю :) Не подскажете, как именно это должно выглядеть? В TableA_PK нужно поменять private TableB tableB на private long id и все? Как насчет TableA? Никаких изменений не требуется?   -  person It'sMe    schedule 29.07.2014
comment
Проверьте 2.4.1.3 Примеры производных идентификаторов в спецификации JPA 2.0. Атрибут в ключевом классе должен совпадать с именем атрибута, используемого в @Id, поэтому это tableB, а не id, а тип должен соответствовать типу первичного ключа TableB в вашем случае, поэтому я предлагаю private long tableB. Если не получится, будем искать что-то другое ;-). Сейчас нет времени тестировать. Никаких изменений в таблице А.   -  person Gas    schedule 29.07.2014
comment
Это не работает, @Gas. Я добавил полную трассировку исключения   -  person It'sMe    schedule 29.07.2014


Ответы (1)


Это работает для меня для следующего кода. Изменена длинная таблицаB в TableA_PK и @JoinColumn в 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;

ТАБЛИЦА_ПК

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

ТАБЛИЦАB

@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

ОБНОВЛЕНИЕ после комментария
К сожалению, сгенерированное значение не поддерживается для производных ключей и неизвестно до вставки элемента. Поэтому вам нужно сначала сохранить элемент tableB, а затем добавить строки. Проверьте следующий код:

    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
person Gas    schedule 29.07.2014
comment
Хорошо, спасибо. Но, возможно, я неправильно заполняю поля объектов TableA и TableB. Пожалуйста, посмотрите на мой 2-й постскриптум к посту - person It'sMe; 29.07.2014
comment
Спасибо за помощь, но это не работает. Я создаю классы сущностей и тестовый класс точно так же, как указано выше, и у меня все еще есть Caused by: java.lang.ClassCastException: org.model.TableB cannot be cast to java.lang.Number. Я добавляю свой файл persistence.xml. Я думаю о реорганизации моей схемы базы данных. - person It'sMe; 29.07.2014
comment
Какова полная трассировка стека для этого: Вызвано: java.lang.ClassCastException ? - person Gas; 29.07.2014
comment
для удобства чтения я добавил новый вопрос: stackoverflow.com/questions/25020393/ со всеми источниками - person It'sMe; 29.07.2014