Exception when adding a new object to two different relationships

classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

Exception when adding a new object to two different relationships

Hugi Thordarson
Hi all,
I'm encountering what I believe to be a bug, that someone more familiar with Cayenne's internals than me might want to take a look at :)

In short: If I create a new DataObject, add it to two different relationships on another object and then attempt to commit the whole thing—it fails.

Consider the following code:

ObjectContext oc = createContext();
Person person = oc.newObject( Person.class );
Address address = oc.newObject( Address.class );
person.addToAddresses( address );
person.setLatestAddress( address );

oc.commitChanges();

This will result in the following exception:

Exception in thread "main" org.apache.cayenne.CayenneRuntimeException: [v.4.1.M1-SNAPSHOT Aug 31 2017 12:03:09] Can't extract a master key. Missing key (id), master ID (<ObjectId:Person, TEMP:00000058A5520119>)
        at org.apache.cayenne.access.DataDomainSyncBucket$PropagatedValueFactory.get(DataDomainSyncBucket.java:290)
        at org.apache.cayenne.query.BatchQueryRow.getValue(BatchQueryRow.java:64)
        at org.apache.cayenne.query.InsertBatchQuery$1.getValue(InsertBatchQuery.java:63)
        at org.apache.cayenne.access.translator.batch.InsertBatchTranslator.doUpdateBindings(InsertBatchTranslator.java:121)
        at org.apache.cayenne.access.translator.batch.DefaultBatchTranslator.updateBindings(DefaultBatchTranslator.java:78)
        at org.apache.cayenne.access.jdbc.BatchAction.runAsIndividualQueries(BatchAction.java:163)
        at org.apache.cayenne.access.jdbc.BatchAction.performAction(BatchAction.java:87)
        at org.apache.cayenne.access.DataNodeQueryAction.runQuery(DataNodeQueryAction.java:97)
        at org.apache.cayenne.access.DataNode.performQueries(DataNode.java:293)
        at org.apache.cayenne.access.DataDomainFlushAction.runQueries(DataDomainFlushAction.java:233)
        at org.apache.cayenne.access.DataDomainFlushAction.flush(DataDomainFlushAction.java:153)
        at org.apache.cayenne.access.DataDomain.onSyncFlush(DataDomain.java:633)
        at org.apache.cayenne.access.DataDomain.onSyncNoFilters(DataDomain.java:603)
        at org.apache.cayenne.access.DataDomain$DataDomainSyncFilterChain.onSync(DataDomain.java:764)
        at org.apache.cayenne.tx.TransactionFilter$1.perform(TransactionFilter.java:73)
        at org.apache.cayenne.tx.TransactionFilter$1.perform(TransactionFilter.java:70)
        at org.apache.cayenne.tx.DefaultTransactionManager.performInTransaction(DefaultTransactionManager.java:87)
        at org.apache.cayenne.tx.DefaultTransactionManager.performInLocalTransaction(DefaultTransactionManager.java:59)
        at org.apache.cayenne.tx.DefaultTransactionManager.performInTransaction(DefaultTransactionManager.java:52)
        at org.apache.cayenne.tx.DefaultTransactionManager.performInTransaction(DefaultTransactionManager.java:40)
        at org.apache.cayenne.tx.TransactionFilter.onSync(TransactionFilter.java:70)
        at org.apache.cayenne.access.DataDomain$DataDomainSyncFilterChain.onSync(DataDomain.java:764)
        at org.apache.cayenne.access.DataDomain.onSync(DataDomain.java:590)
        at org.apache.cayenne.access.DataContext.flushToParent(DataContext.java:742)
        at org.apache.cayenne.access.DataContext.commitChanges(DataContext.java:691)
        at pktest.Main.main(Main.java:28)



If someone would like to have a look, I've created a project that replicates the problem—just build it and run pktest.Main

https://github.com/hugith/pktest <https://github.com/hugith/pktest>

Cheers,
- hugi
Reply | Threaded
Open this post in threaded view
|

Re: Exception when adding a new object to two different relationships

Amedeo Mantica
I see an untitledRel in the Model, it looks like to be a stub inverse relationship... have you tried to remove it ?

Amedeo

> On 8 Sep 2017, at 14:05, Hugi Thordarson <[hidden email]> wrote:
>
> Hi all,
> I'm encountering what I believe to be a bug, that someone more familiar with Cayenne's internals than me might want to take a look at :)
>
> In short: If I create a new DataObject, add it to two different relationships on another object and then attempt to commit the whole thing—it fails.
>
> Consider the following code:
>
> ObjectContext oc = createContext();
> Person person = oc.newObject( Person.class );
> Address address = oc.newObject( Address.class );
> person.addToAddresses( address );
> person.setLatestAddress( address );
>
> oc.commitChanges();
>
> This will result in the following exception:
>
> Exception in thread "main" org.apache.cayenne.CayenneRuntimeException: [v.4.1.M1-SNAPSHOT Aug 31 2017 12:03:09] Can't extract a master key. Missing key (id), master ID (<ObjectId:Person, TEMP:00000058A5520119>)
> at org.apache.cayenne.access.DataDomainSyncBucket$PropagatedValueFactory.get(DataDomainSyncBucket.java:290)
> at org.apache.cayenne.query.BatchQueryRow.getValue(BatchQueryRow.java:64)
> at org.apache.cayenne.query.InsertBatchQuery$1.getValue(InsertBatchQuery.java:63)
> at org.apache.cayenne.access.translator.batch.InsertBatchTranslator.doUpdateBindings(InsertBatchTranslator.java:121)
> at org.apache.cayenne.access.translator.batch.DefaultBatchTranslator.updateBindings(DefaultBatchTranslator.java:78)
> at org.apache.cayenne.access.jdbc.BatchAction.runAsIndividualQueries(BatchAction.java:163)
> at org.apache.cayenne.access.jdbc.BatchAction.performAction(BatchAction.java:87)
> at org.apache.cayenne.access.DataNodeQueryAction.runQuery(DataNodeQueryAction.java:97)
> at org.apache.cayenne.access.DataNode.performQueries(DataNode.java:293)
> at org.apache.cayenne.access.DataDomainFlushAction.runQueries(DataDomainFlushAction.java:233)
> at org.apache.cayenne.access.DataDomainFlushAction.flush(DataDomainFlushAction.java:153)
> at org.apache.cayenne.access.DataDomain.onSyncFlush(DataDomain.java:633)
> at org.apache.cayenne.access.DataDomain.onSyncNoFilters(DataDomain.java:603)
> at org.apache.cayenne.access.DataDomain$DataDomainSyncFilterChain.onSync(DataDomain.java:764)
> at org.apache.cayenne.tx.TransactionFilter$1.perform(TransactionFilter.java:73)
> at org.apache.cayenne.tx.TransactionFilter$1.perform(TransactionFilter.java:70)
> at org.apache.cayenne.tx.DefaultTransactionManager.performInTransaction(DefaultTransactionManager.java:87)
> at org.apache.cayenne.tx.DefaultTransactionManager.performInLocalTransaction(DefaultTransactionManager.java:59)
> at org.apache.cayenne.tx.DefaultTransactionManager.performInTransaction(DefaultTransactionManager.java:52)
> at org.apache.cayenne.tx.DefaultTransactionManager.performInTransaction(DefaultTransactionManager.java:40)
> at org.apache.cayenne.tx.TransactionFilter.onSync(TransactionFilter.java:70)
> at org.apache.cayenne.access.DataDomain$DataDomainSyncFilterChain.onSync(DataDomain.java:764)
> at org.apache.cayenne.access.DataDomain.onSync(DataDomain.java:590)
> at org.apache.cayenne.access.DataContext.flushToParent(DataContext.java:742)
> at org.apache.cayenne.access.DataContext.commitChanges(DataContext.java:691)
> at pktest.Main.main(Main.java:28)
>
>
>
> If someone would like to have a look, I've created a project that replicates the problem—just build it and run pktest.Main
>
> https://github.com/hugith/pktest <https://github.com/hugith/pktest>
>
> Cheers,
> - hugi

Reply | Threaded
Open this post in threaded view
|

Re: Exception when adding a new object to two different relationships

Hugi Thordarson
Hi Amadeo,
thanks for the pointer. No, the reverse relationship doesn't matter (and removing it doesn't help). I have now removed it in the github repo.

And this is reproducible in different projects, I just created this smalll project to serve as an isolated runnable example.

- hugi


> On 8 Sep 2017, at 14:20, Amedeo Mantica <[hidden email]> wrote:
>
> I see an untitledRel in the Model, it looks like to be a stub inverse relationship... have you tried to remove it ?
>
> Amedeo
>
>> On 8 Sep 2017, at 14:05, Hugi Thordarson <[hidden email]> wrote:
>>
>> Hi all,
>> I'm encountering what I believe to be a bug, that someone more familiar with Cayenne's internals than me might want to take a look at :)
>>
>> In short: If I create a new DataObject, add it to two different relationships on another object and then attempt to commit the whole thing—it fails.
>>
>> Consider the following code:
>>
>> ObjectContext oc = createContext();
>> Person person = oc.newObject( Person.class );
>> Address address = oc.newObject( Address.class );
>> person.addToAddresses( address );
>> person.setLatestAddress( address );
>>
>> oc.commitChanges();
>>
>> This will result in the following exception:
>>
>> Exception in thread "main" org.apache.cayenne.CayenneRuntimeException: [v.4.1.M1-SNAPSHOT Aug 31 2017 12:03:09] Can't extract a master key. Missing key (id), master ID (<ObjectId:Person, TEMP:00000058A5520119>)
>> at org.apache.cayenne.access.DataDomainSyncBucket$PropagatedValueFactory.get(DataDomainSyncBucket.java:290)
>> at org.apache.cayenne.query.BatchQueryRow.getValue(BatchQueryRow.java:64)
>> at org.apache.cayenne.query.InsertBatchQuery$1.getValue(InsertBatchQuery.java:63)
>> at org.apache.cayenne.access.translator.batch.InsertBatchTranslator.doUpdateBindings(InsertBatchTranslator.java:121)
>> at org.apache.cayenne.access.translator.batch.DefaultBatchTranslator.updateBindings(DefaultBatchTranslator.java:78)
>> at org.apache.cayenne.access.jdbc.BatchAction.runAsIndividualQueries(BatchAction.java:163)
>> at org.apache.cayenne.access.jdbc.BatchAction.performAction(BatchAction.java:87)
>> at org.apache.cayenne.access.DataNodeQueryAction.runQuery(DataNodeQueryAction.java:97)
>> at org.apache.cayenne.access.DataNode.performQueries(DataNode.java:293)
>> at org.apache.cayenne.access.DataDomainFlushAction.runQueries(DataDomainFlushAction.java:233)
>> at org.apache.cayenne.access.DataDomainFlushAction.flush(DataDomainFlushAction.java:153)
>> at org.apache.cayenne.access.DataDomain.onSyncFlush(DataDomain.java:633)
>> at org.apache.cayenne.access.DataDomain.onSyncNoFilters(DataDomain.java:603)
>> at org.apache.cayenne.access.DataDomain$DataDomainSyncFilterChain.onSync(DataDomain.java:764)
>> at org.apache.cayenne.tx.TransactionFilter$1.perform(TransactionFilter.java:73)
>> at org.apache.cayenne.tx.TransactionFilter$1.perform(TransactionFilter.java:70)
>> at org.apache.cayenne.tx.DefaultTransactionManager.performInTransaction(DefaultTransactionManager.java:87)
>> at org.apache.cayenne.tx.DefaultTransactionManager.performInLocalTransaction(DefaultTransactionManager.java:59)
>> at org.apache.cayenne.tx.DefaultTransactionManager.performInTransaction(DefaultTransactionManager.java:52)
>> at org.apache.cayenne.tx.DefaultTransactionManager.performInTransaction(DefaultTransactionManager.java:40)
>> at org.apache.cayenne.tx.TransactionFilter.onSync(TransactionFilter.java:70)
>> at org.apache.cayenne.access.DataDomain$DataDomainSyncFilterChain.onSync(DataDomain.java:764)
>> at org.apache.cayenne.access.DataDomain.onSync(DataDomain.java:590)
>> at org.apache.cayenne.access.DataContext.flushToParent(DataContext.java:742)
>> at org.apache.cayenne.access.DataContext.commitChanges(DataContext.java:691)
>> at pktest.Main.main(Main.java:28)
>>
>>
>>
>> If someone would like to have a look, I've created a project that replicates the problem—just build it and run pktest.Main
>>
>> https://github.com/hugith/pktest <https://github.com/hugith/pktest>
>>
>> Cheers,
>> - hugi
>

Reply | Threaded
Open this post in threaded view
|

Re: Exception when adding a new object to two different relationships

Nikita Timofeev
It's circular relationships with PKs generated by DB. This case Cayenne
can't handle, as IDs become available only after insert and insert is
impossible without these values as they are used for FKs.
IIRC there was a discussion about this case somewhere, and solution will be
really hard, something like insert first data without FK then update only
FKs.
Workaround is to split creation of objects and their linkage (you can wrap
this in transaction to have atomicity).

On Fri, Sep 8, 2017 at 5:30 PM, Hugi Thordarson <[hidden email]> wrote:

> Hi Amadeo,
> thanks for the pointer. No, the reverse relationship doesn't matter (and
> removing it doesn't help). I have now removed it in the github repo.
>
> And this is reproducible in different projects, I just created this smalll
> project to serve as an isolated runnable example.
>
> - hugi
>
>
> > On 8 Sep 2017, at 14:20, Amedeo Mantica <[hidden email]> wrote:
> >
> > I see an untitledRel in the Model, it looks like to be a stub inverse
> relationship... have you tried to remove it ?
> >
> > Amedeo
> >
> >> On 8 Sep 2017, at 14:05, Hugi Thordarson <[hidden email]> wrote:
> >>
> >> Hi all,
> >> I'm encountering what I believe to be a bug, that someone more familiar
> with Cayenne's internals than me might want to take a look at :)
> >>
> >> In short: If I create a new DataObject, add it to two different
> relationships on another object and then attempt to commit the whole
> thing—it fails.
> >>
> >> Consider the following code:
> >>
> >> ObjectContext oc = createContext();
> >> Person person = oc.newObject( Person.class );
> >> Address address = oc.newObject( Address.class );
> >> person.addToAddresses( address );
> >> person.setLatestAddress( address );
> >>
> >> oc.commitChanges();
> >>
> >> This will result in the following exception:
> >>
> >> Exception in thread "main" org.apache.cayenne.CayenneRuntimeException:
> [v.4.1.M1-SNAPSHOT Aug 31 2017 12:03:09] Can't extract a master key.
> Missing key (id), master ID (<ObjectId:Person, TEMP:00000058A5520119>)
> >>      at org.apache.cayenne.access.DataDomainSyncBucket$
> PropagatedValueFactory.get(DataDomainSyncBucket.java:290)
> >>      at org.apache.cayenne.query.BatchQueryRow.getValue(
> BatchQueryRow.java:64)
> >>      at org.apache.cayenne.query.InsertBatchQuery$1.getValue(
> InsertBatchQuery.java:63)
> >>      at org.apache.cayenne.access.translator.batch.
> InsertBatchTranslator.doUpdateBindings(InsertBatchTranslator.java:121)
> >>      at org.apache.cayenne.access.translator.batch.
> DefaultBatchTranslator.updateBindings(DefaultBatchTranslator.java:78)
> >>      at org.apache.cayenne.access.jdbc.BatchAction.
> runAsIndividualQueries(BatchAction.java:163)
> >>      at org.apache.cayenne.access.jdbc.BatchAction.
> performAction(BatchAction.java:87)
> >>      at org.apache.cayenne.access.DataNodeQueryAction.runQuery(
> DataNodeQueryAction.java:97)
> >>      at org.apache.cayenne.access.DataNode.performQueries(
> DataNode.java:293)
> >>      at org.apache.cayenne.access.DataDomainFlushAction.runQueries(
> DataDomainFlushAction.java:233)
> >>      at org.apache.cayenne.access.DataDomainFlushAction.flush(
> DataDomainFlushAction.java:153)
> >>      at org.apache.cayenne.access.DataDomain.onSyncFlush(
> DataDomain.java:633)
> >>      at org.apache.cayenne.access.DataDomain.onSyncNoFilters(
> DataDomain.java:603)
> >>      at org.apache.cayenne.access.DataDomain$DataDomainSyncFilterChain.
> onSync(DataDomain.java:764)
> >>      at org.apache.cayenne.tx.TransactionFilter$1.perform(
> TransactionFilter.java:73)
> >>      at org.apache.cayenne.tx.TransactionFilter$1.perform(
> TransactionFilter.java:70)
> >>      at org.apache.cayenne.tx.DefaultTransactionManager.
> performInTransaction(DefaultTransactionManager.java:87)
> >>      at org.apache.cayenne.tx.DefaultTransactionManager.
> performInLocalTransaction(DefaultTransactionManager.java:59)
> >>      at org.apache.cayenne.tx.DefaultTransactionManager.
> performInTransaction(DefaultTransactionManager.java:52)
> >>      at org.apache.cayenne.tx.DefaultTransactionManager.
> performInTransaction(DefaultTransactionManager.java:40)
> >>      at org.apache.cayenne.tx.TransactionFilter.onSync(
> TransactionFilter.java:70)
> >>      at org.apache.cayenne.access.DataDomain$DataDomainSyncFilterChain.
> onSync(DataDomain.java:764)
> >>      at org.apache.cayenne.access.DataDomain.onSync(DataDomain.
> java:590)
> >>      at org.apache.cayenne.access.DataContext.flushToParent(
> DataContext.java:742)
> >>      at org.apache.cayenne.access.DataContext.commitChanges(
> DataContext.java:691)
> >>      at pktest.Main.main(Main.java:28)
> >>
> >>
> >>
> >> If someone would like to have a look, I've created a project that
> replicates the problem—just build it and run pktest.Main
> >>
> >> https://github.com/hugith/pktest <https://github.com/hugith/pktest>
> >>
> >> Cheers,
> >> - hugi
> >
>
>


--
Best regards,
Nikita Timofeev
Reply | Threaded
Open this post in threaded view
|

Re: Exception when adding a new object to two different relationships

Hugi Thordarson
Hi NIkita,
thanks for the answer! I understand what the problem is and we decided to go back to sequences for PK generation for now.

Cheers,
- hugi



> It's circular relationships with PKs generated by DB. This case Cayenne
> can't handle, as IDs become available only after insert and insert is
> impossible without these values as they are used for FKs.
> IIRC there was a discussion about this case somewhere, and solution will be
> really hard, something like insert first data without FK then update only
> FKs.
> Workaround is to split creation of objects and their linkage (you can wrap
> this in transaction to have atomicity).
>
> On Fri, Sep 8, 2017 at 5:30 PM, Hugi Thordarson <[hidden email]> wrote:
>
>> Hi Amadeo,
>> thanks for the pointer. No, the reverse relationship doesn't matter (and
>> removing it doesn't help). I have now removed it in the github repo.
>>
>> And this is reproducible in different projects, I just created this smalll
>> project to serve as an isolated runnable example.
>>
>> - hugi
>>
>>
>>> On 8 Sep 2017, at 14:20, Amedeo Mantica <[hidden email]> wrote:
>>>
>>> I see an untitledRel in the Model, it looks like to be a stub inverse
>> relationship... have you tried to remove it ?
>>>
>>> Amedeo
>>>
>>>> On 8 Sep 2017, at 14:05, Hugi Thordarson <[hidden email]> wrote:
>>>>
>>>> Hi all,
>>>> I'm encountering what I believe to be a bug, that someone more familiar
>> with Cayenne's internals than me might want to take a look at :)
>>>>
>>>> In short: If I create a new DataObject, add it to two different
>> relationships on another object and then attempt to commit the whole
>> thing—it fails.
>>>>
>>>> Consider the following code:
>>>>
>>>> ObjectContext oc = createContext();
>>>> Person person = oc.newObject( Person.class );
>>>> Address address = oc.newObject( Address.class );
>>>> person.addToAddresses( address );
>>>> person.setLatestAddress( address );
>>>>
>>>> oc.commitChanges();
>>>>
>>>> This will result in the following exception:
>>>>
>>>> Exception in thread "main" org.apache.cayenne.CayenneRuntimeException:
>> [v.4.1.M1-SNAPSHOT Aug 31 2017 12:03:09] Can't extract a master key.
>> Missing key (id), master ID (<ObjectId:Person, TEMP:00000058A5520119>)
>>>>     at org.apache.cayenne.access.DataDomainSyncBucket$
>> PropagatedValueFactory.get(DataDomainSyncBucket.java:290)
>>>>     at org.apache.cayenne.query.BatchQueryRow.getValue(
>> BatchQueryRow.java:64)
>>>>     at org.apache.cayenne.query.InsertBatchQuery$1.getValue(
>> InsertBatchQuery.java:63)
>>>>     at org.apache.cayenne.access.translator.batch.
>> InsertBatchTranslator.doUpdateBindings(InsertBatchTranslator.java:121)
>>>>     at org.apache.cayenne.access.translator.batch.
>> DefaultBatchTranslator.updateBindings(DefaultBatchTranslator.java:78)
>>>>     at org.apache.cayenne.access.jdbc.BatchAction.
>> runAsIndividualQueries(BatchAction.java:163)
>>>>     at org.apache.cayenne.access.jdbc.BatchAction.
>> performAction(BatchAction.java:87)
>>>>     at org.apache.cayenne.access.DataNodeQueryAction.runQuery(
>> DataNodeQueryAction.java:97)
>>>>     at org.apache.cayenne.access.DataNode.performQueries(
>> DataNode.java:293)
>>>>     at org.apache.cayenne.access.DataDomainFlushAction.runQueries(
>> DataDomainFlushAction.java:233)
>>>>     at org.apache.cayenne.access.DataDomainFlushAction.flush(
>> DataDomainFlushAction.java:153)
>>>>     at org.apache.cayenne.access.DataDomain.onSyncFlush(
>> DataDomain.java:633)
>>>>     at org.apache.cayenne.access.DataDomain.onSyncNoFilters(
>> DataDomain.java:603)
>>>>     at org.apache.cayenne.access.DataDomain$DataDomainSyncFilterChain.
>> onSync(DataDomain.java:764)
>>>>     at org.apache.cayenne.tx.TransactionFilter$1.perform(
>> TransactionFilter.java:73)
>>>>     at org.apache.cayenne.tx.TransactionFilter$1.perform(
>> TransactionFilter.java:70)
>>>>     at org.apache.cayenne.tx.DefaultTransactionManager.
>> performInTransaction(DefaultTransactionManager.java:87)
>>>>     at org.apache.cayenne.tx.DefaultTransactionManager.
>> performInLocalTransaction(DefaultTransactionManager.java:59)
>>>>     at org.apache.cayenne.tx.DefaultTransactionManager.
>> performInTransaction(DefaultTransactionManager.java:52)
>>>>     at org.apache.cayenne.tx.DefaultTransactionManager.
>> performInTransaction(DefaultTransactionManager.java:40)
>>>>     at org.apache.cayenne.tx.TransactionFilter.onSync(
>> TransactionFilter.java:70)
>>>>     at org.apache.cayenne.access.DataDomain$DataDomainSyncFilterChain.
>> onSync(DataDomain.java:764)
>>>>     at org.apache.cayenne.access.DataDomain.onSync(DataDomain.
>> java:590)
>>>>     at org.apache.cayenne.access.DataContext.flushToParent(
>> DataContext.java:742)
>>>>     at org.apache.cayenne.access.DataContext.commitChanges(
>> DataContext.java:691)
>>>>     at pktest.Main.main(Main.java:28)
>>>>
>>>>
>>>>
>>>> If someone would like to have a look, I've created a project that
>> replicates the problem—just build it and run pktest.Main
>>>>
>>>> https://github.com/hugith/pktest <https://github.com/hugith/pktest>
>>>>
>>>> Cheers,
>>>> - hugi
>>>
>>
>>
>
>
> --
> Best regards,
> Nikita Timofeev