Cascade Update with children with @Id set

287 views
Skip to first unread message

Ivan Reffatti

unread,
Apr 1, 2017, 7:12:46 PM4/1/17
to Ebean ORM
Hi,

I'm trying to update cascade an object that has an @OneToMany with cascade type ALL where collection children values have @Id set, but Ebean tries to update these values and not insert. Is this the expected behavior? Shouldn't Ebean check if register exists and case not, insert it?

In my scenario I HAVE to send these list with objects with @Id set.

Thanks,
Ivan A. Reffatti

Rob Bygrave

unread,
Apr 2, 2017, 3:14:04 AM4/2/17
to ebean@googlegroups
> Is this the expected behavior?

You don't say if it is a stateless update or if the beans were previously fetched.  Yes, it sounds like it is expected behavior.  You need to provide more information or code example to know though.  It sounds like a stateless update so values with @Id values set are deemed updates in that case.


> Shouldn't Ebean check if register exists and case not, insert it?

I don't understand what "check if register exists" means.  Hit the DB?  No, if you want that just fetch the object graph in first, apply changes and then save().  We use stateless update to avoid the extra queries to the DB.


Note that you always have the ability to control exact behavior per transaction via turning off cascade persist and traversing the graph yourself (and calling insert or update etc).  So there is always that option.


> In my scenario I HAVE to send these list with objects with @Id set.

Do those beans always have their @Id set or just for this use case?




--

---
You received this message because you are subscribed to the Google Groups "Ebean ORM" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ebean+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Gustavo Bilert

unread,
Apr 2, 2017, 11:59:08 AM4/2/17
to Ebean ORM
Hi Rob,

I work with Ivan, so let me answer your questions and show what I have so far:


> You don't say if it is a stateless update or if the beans were previously fetched.

Yes, we were using stateless update, now I am trying to use save(), but EBean apparently was not detecting the changes to the collection.

dbObject.getCultureLocations().add(cultureLocation);
ebeanServer
().getBeanState(dbObject).isDirty(); // returns false

> Hit the DB?  No, if you want that just fetch the object graph in first, apply changes and then save().

I am trying to do that. The apply changes part was not trivial, then I found the BeanDescriptor.merge() and I am trying to make it work with an adapted version of it (sample code in anex).

Now the problem is: EBean is trying to update the new children instead of inserting it.

I have found this code in DefaultPersister:

if (publish) {
     
// insert if it is a new bean (as publish created it)
      type
= entityBean._ebean_getIntercept().isNew() ? Type.INSERT : Type.UPDATE;
   
} else {
     
// determine Insert or Update based on bean state and insert flag
      type
= desc.isInsertMode(entityBean._ebean_getIntercept(), insertMode) ? Type.INSERT : Type.UPDATE;
   
}Digite o código aqui...

The publish behavior is what I wished for, but is is returning insertMode = false -> UPDATE.


> Do those beans always have their @Id set or just for this use case?

Always, the ids are generated by the client.


> Note that you always have the ability to control exact behavior per transaction via turning off cascade persist and traversing the graph yourself (and calling insert or update etc).  So there is always that option.

I will try to do that for today, but after would be better to get the save working.
To unsubscribe from this group and stop receiving emails from it, send an email to ebean+un...@googlegroups.com.
SveCascadeNewChildrenSample.zip

Gustavo Bilert

unread,
Apr 2, 2017, 6:50:09 PM4/2/17
to Ebean ORM
Hi Rob,

Now I have an updated version of my "code" (hack).
I am sending it with the "final" version and the entities that were missing on the previous example (packages need to be changed).

Also, thank you for having answered on a Sunday.

Cheers, Gustavo
SveCascadeNewChildrenSample2.zip

Rob Bygrave

unread,
Apr 3, 2017, 3:52:33 AM4/3/17
to ebean@googlegroups
> Always, the ids are generated by the client.

So in short this is the 'issue' (why it does not work out of the box).  Stateless update needs to detect if a child bean should be inserted or updated (when it cascades to the children) and it can't do it at the moment with this scenario where the ids are generated by the client.

So that won't work out of the box at the moment.




> ebeanServer().getBeanState(dbObject).isDirty(); // returns false

Note that this is correct in that context, Ebean cascade checks the dirty state of the collections as well (and that dirty collection state is not reflected onto the bean isDirty()).




> The publish behavior is what I wished for

Yes. It wasn't designed for this specific use case but might a good way to 'merge' 2 object graphs. 

The other option is to fetch the Ids from the DB and use those to determine inserts and updates required.  This would mean only fetching the id values rather than full beans so should be cheaper to execute compared with a 'merge'.


> transaction via turning off cascade persist and traversing the graph yourself

So with this we would:

- turn off cascade persist via transaction.setPersistCascade(false)
- fetch the Id values of the 'children'
- update the parent bean
- iterate the children
    - for each detect if it is an insert or update based on the id values previously fetched
    - explicit insert() or update() the child bean
- delete children where parentId = ? and id not in (.. list of child id values ..)


That is pretty much what I'd like Ebean to do in this case.


Now, right at the end of a sprint and don't have much time over the next 3 days ... so if you can hold for a bit we could look at it in more depth then.


Cheers, Rob.


To unsubscribe from this group and stop receiving emails from it, send an email to ebean+unsubscribe@googlegroups.com.

Gustavo Bilert

unread,
Nov 22, 2017, 6:33:26 AM11/22/17
to Ebean ORM
Hi Rob,

Are you planning on doing this anytime soon?

Our homemade implementation is giving us some problems...


Thanks, Gustavo.

Rob Bygrave

unread,
Nov 22, 2017, 6:46:47 AM11/22/17
to ebean@googlegroups
Yes I'm keen to do this.  I might be able to get into it next week.

If you don't see me update this thread by next Wednesday ... then prompt me again.



Cheers, Rob.


To unsubscribe from this group and stop receiving emails from it, send an email to ebean+unsubscribe@googlegroups.com.

Gustavo Bilert

unread,
Nov 22, 2017, 10:40:30 AM11/22/17
to Ebean ORM
That's great news!

Just to make sure we are on the same page here:
What you will do is change the save() behavior so it will "merge" the incoming object graph with what is already in the database, inserting/updating/deleting the children based on their presence in the database, right?


Cheers, Gustavo.

Rob Bygrave

unread,
Nov 23, 2017, 12:55:17 AM11/23/17
to ebean@googlegroups
Strictly speaking we have
- save()  ... insert or update based on bean state
- insert() ... explicit insert
- update() ... explicit update and importantly UPDATE even if the bean state is new (and we call this a "stateless update" / "an update without a prior query")



> What you will do is change the save() behavior

The thought is to either change the existing update(T bean) ... or to add a new method like:

update(T bean , String paths)
merge(T bean , String paths)


The first part would be to outline the use cases we are looking to support (which prototype the API to confirm it makes sense and can become tests).  The reason why I say we might add a merge() method is due to a known case around empty or null collections and if this will support an insert of the top level bean.


e.g. We want to update "order" and it has a OneToMany to "details".

Case 1: Order has some properties and has some details with some properties

Fairly straight forward with update() as it is in that we know the "details" is involved in the update as the order.details list is not null and not empty.


Case 2: Order has some properties and has details of null or is an empty list

Ebean does not explicitly know whether the details should not be involved at all ... or whether the details are effectively all deleted.




So ideally it is either explicit or obvious what part of the object graph should be included in the persist. 

Currently update() when called when the bean is in "new state" includes the properties that have been set.  If a OneToMany "details" was never set then ... we don't get the behavior that an empty "details" actually means delete all the existing details for this bean.


Example model:

@Entity
Order {

    @ManyToOne
    Customer customer

    @OneToMany
    OrderDetail details

    @OneToMany
    OrderShipment shipments

}



merge(order, "details");    // save order + order.details ... if details is null or empty that means delete any existing details

merge(order, "details,customer.shippingAddress");    // save order + order.details + order.customer + order.customer.shippingAddress

merge(order, "details,shipping");    // save order + order.details + order.shipping



So that is my initial thinking.  It would be great to outline / prototype the example cases that we want covered. 




Cheers, Rob.


To unsubscribe from this group and stop receiving emails from it, send an email to ebean+unsubscribe@googlegroups.com.

Gustavo Bilert

unread,
Nov 23, 2017, 3:21:03 PM11/23/17
to Ebean ORM
In general we are following the HTTP methods semantics:

- POST - Insert
- PUT - Insert or Update the whole graph, removing what is missing, here would be the merge() I think. (That is the default HTTP behavior, we do not use PUT for Insert)
- PATCH - json-patch, which has 2 semantics: PATCH (operator based patch that would call EBean's merge() I think) and MERGE PATCH (Just updates the received fields and ignores what is missing)
- DELETE - We delete by id, fetching the object and deleting it so the cascades apply

The problems we have are on PUT, and the PATCH is implemented but I think it is not being actually used yet.
Our records are created with ids when they come from mobile, it works offline so we need the ids to be able to make the associations.


Cheers, Gustavo.

Gustavo Bilert

unread,
Dec 5, 2017, 8:45:31 AM12/5/17
to Ebean ORM
Hi Rob,

You said to prompt you again so here I am.
Any progress on this?


Cheers, Gustavo

Rob Bygrave

unread,
Dec 6, 2017, 2:46:04 AM12/6/17
to ebean@googlegroups
Thanks.


> Any progress on this?

Unfortunately no. I did have a little break which was so good I didn't really fire up the computer at all - so good and bad there (cause I thought I might get a look at this one last week).

I do want to get into this issue / feature.  I have just logged it as https://github.com/ebean-orm/ebean/issues/1221



Ping this post again (or issue #1221) next week and I'll update the status.



Cheers, Rob.


To unsubscribe from this group and stop receiving emails from it, send an email to ebean+unsubscribe@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages