Blog

How to handle recursive object references in persistence objects

by on March 7, 2015

A recursive reference is when an object is either directly or indirectly references itself. This design pattern happens rather frequently in applications and quite often distributed systems do not handle it gracefully. Not Backendless – we fully support object recurrence as the same below demonstrates. Consider the following class:

    package com.mbaas.sample;
    import java.util.List;
    public class Person
    {
        public String name;
        public int age;
        public Person mom;
        public Person dad;
        public List<Person> children;
    }

    #import "Person.h"
    @implementation Person
    @end

    import Foundation
    class Person : NSObject {
        var name : String?
        var age : Int = 0
        var mom : Person?
        var dad : Person?
        var children : [Person]?
    }

    Not only does the class reference itself through the “mom” and “dad” fields, it also includes the “children” field which may create a recursive dependency. For example, suppose a person named Bobby has mom and dad named Jennifer and Richard accordingly. Both objects representing the mother and the father refer back to Bobby through the “children” field. The code below demonstrates that structure and saving it in Backendless using the Data Storage API:

      Asynchronous API (Android and Plain Java):

      Person me = new Person();
      me.name = "Bobby";
      me.age = 13;
      Person mom = new Person();
      mom.name = "Jennifer";
      mom.age = 40;
      mom.children = Arrays.asList( new Person[] { me } );
      Person dad = new Person();
      dad.name = "Richard";
      dad.age = 41;
      dad.children = Arrays.asList( new Person[] { me } );
      me.mom = mom;
      me.dad = dad;
      Backendless.Data.of( Person.class ).save( me, new AsyncCallback<Person>()
      {
          @Override
          public void handleResponse( Person person )
          {
              System.out.println( "Person object has been saved" );
          }
          @Override
          public void handleFault( BackendlessFault backendlessFault )
          {
              System.out.println( "Server reported an error " + backendlessFault.getMessage() );
          }
      } );

      Synchronous API (Plain Java only):

      Person me = new Person();
      me.name = "Bobby";
      me.age = 13;
      Person mom = new Person();
      mom.name = "Jennifer";
      mom.age = 40;
      mom.children = Arrays.asList( new Person[] { me } );
      Person dad = new Person();
      dad.name = "Richard";
      dad.age = 41;
      dad.children = Arrays.asList( new Person[] { me } );
      me.mom = mom;
      me.dad = dad;
      Backendless.Data.of( Person.class ).save( me );


      Asynchronous sample: 

      -(void)savePersonAsync {
          Person *me = [Person new];
          me.name = @"Bobby";
          me.age = @13;
          Person *mom = [Person new];
          mom.name = @"Jennifer";
          mom.age = @40;
          mom.children = @[me];
          Person *dad = [Person new];
          dad.name = @"Richard";
          dad.age = @41;
          dad.children = @[me];
          me.mom = mom;
          me.dad = dad;
          [backendless.data
           save:me
           response:^(Person *person) {
               NSLog(@"Person has been saved (ASYNC): %@", person);
           }
           error:^(Fault *fault) {
               NSLog(@"Server reported an error (ASYNC): %@", fault);
           }];
      }

      Synchronous sample: 

      -(void)savePersonSync {
          @try {
              Person *me = [Person new];
              me.name = @"Bobby";
              me.age = @13;
              Person *mom = [Person new];
              mom.name = @"Jennifer";
              mom.age = @40;
              mom.children = @[me];
              Person *dad = [Person new];
              dad.name = @"Richard";
              dad.age = @41;
              dad.children = @[me];
              me.mom = mom;
              me.dad = dad;
              me = [backendless.data save:me];
              NSLog(@"Person has been saved (SYNC): %@", me);
          }
          @catch (Fault *fault) {
              NSLog(@"Server reported an error(SYNC): %@", fault);
          }
      }


      Asynchronous sample: 

      func savePersonAsync() {
              var me = Person()
              me.name = "Bobby"
              me.age = 13
              var mom = Person()
              mom.name = "Jennifer"
              mom.age = 40
              mom.children = [me]
              var dad = Person()
              dad.name = "Richard"
              dad.age = 41
              dad.children = [me]
              me.mom = mom
              me.dad = dad
              backendless.data.save(
                  me,
                  response: { (var person : AnyObject!) -> () in
                      println("Person has been saved (ASYNC): \((person as! Person).name) -> \((person as! Person).age)")
                  },
                  error: { (var fault : Fault!) -> () in
                      println("Server reported an error (ASYNC): \(fault)")
                  }
              )
          }
      }

      Synchronous sample: 

      func savePersonSync() {
              Types.try({ () -> Void in
                  var me = Person()
                  me.name = "Bobby"
                  me.age = 13
                  var mom = Person()
                  mom.name = "Jennifer"
                  mom.age = 40
                  mom.children = [me]
                  var dad = Person()
                  dad.name = "Richard"
                  dad.age = 41
                  dad.children = [me]
                  me.mom = mom
                  me.dad = dad
                  me = self.backendless.data.save(me) as! Person
                  println("Person has been saved (SYNC): \(me.name) -> \(me.age)")
                  },
                  catch: { (exception) -> Void in
                      println("Server reported an error (SYNC): \(exception as! Fault)")
                  }
              )
          }

      Once you run the code, the data structure created in the Backendless database fully reflects the relationships created in the code. See a screenshot of the Person data table:

      The structure in the data table may appear complex, however, it fully resembles the object hierarchy created in the code. Bobby has mom Jennifer and dad Richard. Both Jennifer and Richard have a child with the object ID from Bobby.

      This is some complex stuff which we made look simple and we truly hope you will enjoy using it in your apps!