Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ENH: Add a merge(bean) and merge(bean, paths) #1221

Closed
rbygrave opened this issue Dec 6, 2017 · 1 comment
Closed

ENH: Add a merge(bean) and merge(bean, paths) #1221

rbygrave opened this issue Dec 6, 2017 · 1 comment
Assignees
Milestone

Comments

@rbygrave
Copy link
Member

rbygrave commented Dec 6, 2017

So there are 2 issues around stateless updates as they are currently:

  • Does not work well when @Id values are not server generated (application supplied Id values) as it doesn't differentiate well between inserts and updates in that case
  • Does not work well for the cases when a OneToMany is null or empty ... and where that means delete all the children

The intention is to add merge() that will work in these cases.

  /**
   * Merge the bean using the given merge options.
   *
   * @param bean    The bean to merge
   * @param options The options to control the merge
   */
  void merge(Object bean, MergeOptions options);

Examples

     // save the customer bean + shippingAddress + billingAddress
  
    MergeOptions options = new MergeOptionsBuilder()
      .addPath("shippingAddress")
      .addPath("billingAddress")
      .setClientGeneratedIds()  
      .build();

    ebeanServer.merge(customer, options);
     // save the customer and contacts

    // note that if contacts is null or empty this means delete the
    // existing contacts for that customer 
  
    MergeOptions options = new MergeOptionsBuilder()
      .addPath("contacts")
      .build();

    ebeanServer.merge(customer, options);
     // save the customer and contacts

    MergeOptions options = new MergeOptionsBuilder()
      .addPath("billingAddress")
      .addPath("shippingAddress")
      .addPath("contacts")
      .addPath("contacts.messages") // nested OneToMany here
      .setClientGeneratedIds()
      .setDeletePermanent()
      .build();

    ebeanServer.merge(customer, options);

setClientGeneratedIds()

This option tells the merge that the Id values can be client generated. Say there is a mobile app creating the data and then sync'ing it to the server.

In this case the merge process can't assume a bean should be update when it has an Id value and instead must check and it does this for ManyToOne and OneToOne paths. In the examples above if the merge process sees a shippingAddress or billingAddress Id that doesn't match the existing online Id value ... it needs to check that against the DB before deciding if the address is an insert or update.

setDeletePermanent()

By default when merge identifies existing beans to delete (that are not in the merging object graph) it will delete them 'normally' and so beans with @SoftDelete will be soft deleted. Set setDeletePermanent() for those deletes to instead be permanent deletes.

Overview

What merge does is first execute a query that returns all the Id values of the paths that we are looking to merge. This means it knows what exists in the DB (Ids) and uses this to determine for each bean in the paths ... if they need to be inserted or updated ... and if the DB has Ids that are NOT in the merge graph then to delete those ones.

The merge process only fetches the Ids as that is the only information required to process the merge and that query (or queries) is expected to be relatively low cost.

@rbygrave rbygrave self-assigned this Dec 6, 2017
@rbygrave
Copy link
Member Author

SQL Logging

Below is an example query that fetches Ids that the merge uses to determine what are inserts, updates and deletes (for the paths that we are merging).

select t0.id, t3.id, t1.id, t2.id 
from customer t0 
left join maddress t3 on t3.id = t0.shipping_address_id  
left join maddress t1 on t1.id = t0.billing_address_id  
left join mcontact t2 on t2.customer_id = t0.id  where t0.id = ?

For ManyToOne and OneToOne we can also sometimes see additional queries to check the existence of a unknown Id value.

-- checking the existence of a shippingAddress ... where the Id 
-- is unknown and we are using setClientGeneratedIds()

select t0.id from address t0 where t0.id = ?

@rbygrave rbygrave added this to the 11.15.1 milestone Mar 13, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant