Relations management with Backendless 4

Hi,

I wanna understand better the relations management introduced with Backendless 4.

Imagine this scenario:

class Parent: BackendlessEntity {
    
    var name: String?
    var child: Child?
}

and the child:

class Child: BackendlessEntity {
    
    var name: String?
    var age: Int?
}

Then I created the instances and setup the relations.

let child = Child()
        child.name = "Jonh junior"
        child.age = 32
        
        let parent = Parent()
        parent.name = "Jonh"
        parent.child = child

With Backendless 3 the following code create automatically the “Parent” and the “Child” table, save the child and the parent objects and create a 1:1 relation between them:

Backendless.sharedInstance()?.data.of(Parent.ofClass()).save(parent, response: { (response) in
            print(response)
        }, error: { (fault) in
            print(fault)
        })

With a test I notice that this doesn’t works with the new version of Backendless.
Reading the documentation I notice that we have to make many additional step for doing this:

    save the child object save the parent object create a relation by using dataStore?.setRelation API (create it automatically the column in the Parent table?)

Why now we have to make 3 different service calls?

Thanks,
Matteo

Hi Matteo,

In this particular case, yes, you’d need to make three calls, however, if you look at the entire set of relationship management (create/update/delete), the number of calls ends up being the same or actually slightly less in 4.0 than in 3.x. For example:

Creating a relationship with new parent and children:

3.x: - 1 call

    Save parent object and child object in one call
4.0: - 3 calls
    Save parent object

    Save child object

    Set relation object

Creating a relationship with existing parent and children:

3.x: - 3 calls

    Load parent object

    Load child object
    Save updated parent object

4.0: - 1 call
    Set relation between parent and child (via where clause)
Creating a relationship with new parent and existing children: -------------------------------------------- 3.x: - 2 calls
    Load child object

    Save parent object with the child

4.0: - 2 calls
    Save parent object Set relation between the parent and the child (via where clause)
Adding more new children to an existing parent -------------------------------------------- 3.x: - 2 calls
    Load parent object and add child to it

    Save parent object with the child

4.0: - 2 calls
    Save child object Set relation between the parent and the child (via where clause)
Adding more existing children to an existing parent -------------------------------------------- 3.x: - 3 calls
    Load parent object Load child object, update parent with the child

    Save parent object with the child

4.0: - 1 call
    Set relation between the parent and the child (via where clause)
Delete a child from a parent -------------------------------------------- 3.x: - 2 calls
    Load parent object WITH children (super expensive operation). Find child in the collection of related children and remove it from the collection.

    Save parent object without the child

4.0: - 1 call
    Delete relation between the parent and the child (via where clause)
As you can see, in the grand scheme of things, it will be about the same or even less with 4.0. You also asked why you need to do it that way. The primary reason is performance. We realize it is nice and convenient to save one whole structure and have all the objects saved in the right tables, but it really leads to a lot of problems. Our goal for 4.0 was to streamline the performance and make the server highly reliable and super fast. This is one of the changes which helps us with achieving that goal.

Regards,
Mark

1 Like

Hi Mark,

I agree that in most of the cases, the new implemtation is better that before (from a Backend perspective).

But in my app I have a synch process in which I created many (i.e. 100) parent objects each with a relation with child object, a GeoPoint.

I don’t wanna overload the client app with so much API calls (300 for 100 parent objects). So I decided to create a Custom Business Logic with the responsibility to save the parent, save the GeoPont and finally save the relation object.

I want that the app sends to my backend a payload with a format like this:

[
  {
    "parent": {
      "name": "Matteo",
      "location": null
    },
    "location": {
      "latitude": 44.729172,
      "objectId": null,
      "metadata": {},
      "longitude": 8.939524,
      "categories": [
        "Default"
      ]
    }
  },
  {
    "parent": {
      "name": "MArk",
      "location": null
    },
    "location": {
      "latitude": 33.099793,
      "objectId": null,
      "metadata": {},
      "longitude": -96.825049,
      "categories": [
        "Default"
      ]
    }
  }
]

So, it was the turn to create the Custom Business Logic.

I decided to have these elements:

    A Parent model class A MyService class which contains two services:
    private service ->addParentWithGeoPoint public service -> addParentsWithGeoPoints ( for any tuple (Parent, GeoPoint) it calls the private method)
The Parent class is the following:
public class Parent {
    private GeoPoint location;
    private String name;
    private String objectId;


   // plus getters and setters
}

The implementation of the private method is the following:

private void addParentWithGeoPoint(Parent parent, GeoPoint location) {


    parent = Backendless.Data.of(Parent.class).save(parent);
    location = Backendless.Geo.savePoint(location);
    ArrayList<GeoPoint> locationList = new ArrayList<GeoPoint>();
    locationList.add(location);
    Backendless.Data.of(Parent.class).setRelation(parent, "location:GeoPoint:1", locationList);
}

Now I’m dealing with what will be the signature of the public method to be compatible with payload (List of composite object (parent and GeoPoont) as written before).
What will be the better way using Backendless JAVA SDK?

Hi Matteo,

What if you change the payload to the following?:

[
 {
 "parent": {
 "name": "Matteo",
 "location": {
 "latitude": 44.729172,
 "objectId": null,
 "metadata": {},
 "longitude": 8.939524,
 "categories": [
 "Default"
 ]
 },
 {
 "parent": {
 "name": "MArk",
 "location": {
 "latitude": 33.099793,
 "objectId": null,
 "metadata": {},
 "longitude": -96.825049,
 "categories": [
 "Default"
 ]
 }
]

This way the signature of the service would be:
addParentsWithGeoPoints( List<Parent> parentObjects );

In your implementation, the method which deals with a specific parent would save the location, parent and establish a relation.

Out of curiosity, what is the use-case when a client app needs to save 300 objects with individual locations?

Regards,
Mark

Hi Mark,

Thanks for your answer.
In my app I need to save up to 100 Parent objects each one with a GeoPoint. This is a one time process (on the first Facebook login of the user) and the number of the objects depends of his Facebook likes types.

I’ve tried your implementation suggestion yesterday but I’ve noticed that the location object passed to the backend (with objectId = null), it’s not always valorized in the backend object model (before save it, just at the start of the JAVA backend method)
It seems that the location information disappear and as consequence the location is null on the server side, so I’m unable to save the GeoPoint. Is this an issue regarding a list of objects with related objects that aren’t already saved into the Backend?

This is why I’ve proposed the payload in the before message.

Matteo

Hi Matteo,

It seems rather odd. To make sure I understand - your Java/Android client sends an instance of the following class to a custom service:

public class Parent {
 private GeoPoint location;
 private String name;
 private String objectId;


 // plus getters and setters
}

When the service receives the object, the objectId property of the geopoint is set to null?

I apologize if I misunderstood, please clarify in that case.

Mark

Hi Mark,

I received the location property of the parent object as null, not the objectId of the GeoPoint.

Matteo

Hi Matteo,

Could you please show how you invoke your custom service?

Mark

Hi Mark,

I solved the issue. It was mine problem.
I was using the following code for my Custom Business Logic:

parent = Backendless.Data.of( Parent.class ).save(parent);
GeoPoint location = parent.getLocation();
location = Backendless.Geo.savePoint(location);


ArrayList&lt;GeoPoint&gt; locationList = new ArrayList&lt;GeoPoint&gt;();
locationList.add(location);
Backendless.Data.of(Parent.class).setRelation(parent, "location:GeoPoint:1", locationList);

As you can notice, I was saving the location object after I’ve saved the parent object. I was wrong because I’m taking the location from the parent returned by the save operation (without the related GeoPoint).

Instead, the following code works:

GeoPoint location = parent.getLocation();
location = Backendless.Geo.savePoint(location);
parent = Backendless.Data.of( Parent.class ).save(parent);


ArrayList&lt;GeoPoint&gt; locationList = new ArrayList&lt;GeoPoint&gt;();
locationList.add(location);
Backendless.Data.of(Parent.class).setRelation(parent, "location:GeoPoint:1", locationList);

Thanks for your assistance Mark,
Matteo

Matteo,

I am glad the problem is solved! I was thinking about a slightly different approach with custom services, but I am happy you resolved it.

Regards,
Mark