Blog

How to load related data objects – the ‘auto-load’ approach

by on January 28, 2015

Another post covered how to retrieve data objects from Backendless. The code in that article loads a collection of the Restaurant objects and although it does not show it, the related collection of the Location objects arrives un-initialized. That is the default behavior of Backendless Database when it comes to loading related objects. The code below demonstrates that the collection is indeed empty (null). (The code in these examples is from the article describing how to generate client-side code based on data tables.)


    Method to print out related locations:

    private static void printLocations( List<Location> locations )
    {
        if( locations == null )
        {
            System.out.println( "Restaurant locations have not been loaded" );
        }
        else if( locations.size() == 0 )
        {
            System.out.println( "There are no related locations" );
        }
        else
        {
            Iterator<Location> iterator = locations.iterator();
            while( iterator.hasNext() )
            {
                Location location = iterator.next();
                System.out.println( "Location: Street address - " + location.getStreetAddress() + ", City - " + location.getCity() );
            }
        }
    }

    Asynchronous example (Android and plain Java):

    private static void fetchingFirstPageAsync() throws InterruptedException
    {
        long startTime = System.currentTimeMillis();
        final CountDownLatch latch = new CountDownLatch( 1 );
        AsyncCallback<BackendlessCollection<Restaurant>> callback=new AsyncCallback<BackendlessCollection<Restaurant>>()
        {
            @Override
            public void handleResponse( BackendlessCollection<Restaurant> restaurants )
            {
                System.out.println( "Loaded " + restaurants.getCurrentPage().size() + " restaurant objects" );
                System.out.println( "Total restaurants in the Backendless storage - " + restaurants.getTotalObjects() );
                Iterator<Restaurant> iterator=restaurants.getCurrentPage().iterator();
                while( iterator.hasNext() )
                {
                    Restaurant restaurant=iterator.next();
                    System.out.println( "\nRestaurant name = " + restaurant.getName() );
                    printLocations( restaurant.getLocations() );
                }
                latch.countDown();
            }
            @Override
            public void handleFault( BackendlessFault backendlessFault )
            {
            }
        };
        Backendless.Data.of( Restaurant.class ).find( callback );
        latch.await();
        System.out.println( "Total time (ms) - " + (System.currentTimeMillis() - startTime ));
    }

    Synchronous example (plain Java):

    private static void fetchingFirstPage()
    {
        long startTime = System.currentTimeMillis();
        BackendlessCollection<Restaurant> restaurants = Backendless.Data.of(Restaurant.class).find();
        System.out.println( "Loaded " + restaurants.getCurrentPage().size() + " restaurant objects" );
        System.out.println( "Total restaurants in the Backendless storage - " + restaurants.getTotalObjects() );
        Iterator<Restaurant> iterator = restaurants.getCurrentPage().iterator();
        while (iterator.hasNext())
        {
            Restaurant restaurant = iterator.next();
            System.out.println( "\nRestaurant name = " + restaurant.getName() );
            printLocations( restaurant.getLocations() );
        }
        System.out.println( "Total time (ms) - " + (System.currentTimeMillis() - startTime ));
    }

    The code output:

    ============ Fetching first page using the SYNC API ============
    Loaded 4 restaurant objects
    Total restaurants in the Backendless storage - 4
    Restaurant name = McDonald's
    Restaurant locations have not been loaded
    Restaurant name = Buca Di Bepo
    Restaurant locations have not been loaded
    Restaurant name = Cantina Laredo
    Restaurant locations have not been loaded
    Restaurant name = Endless Sweets
    Restaurant locations have not been loaded
    Total time (ms) - 437
    ============ Fetching first page using the ASYNC API ============
    Loaded 4 restaurant objects
    Total restaurants in the Backendless storage - 4
    Restaurant name = McDonald's
    Restaurant locations have not been loaded
    Restaurant name = Buca Di Bepo
    Restaurant locations have not been loaded
    Restaurant name = Cantina Laredo
    Restaurant locations have not been loaded
    Restaurant name = Endless Sweets
    Restaurant locations have not been loaded
    Total time (ms) - 202

    Method to print out related locations:

    function printLocations(locations) {
        if(locations == null) {
            console.log( "Restaurant locations have not been loaded" );
        }
        else if(locations.length == 0) {
            console.log("There are no related locations");
        }
        else {
            for (var i = 0; i < locations.length; i++) {
                var location = locations[i];
    	    console.log("Location: Street address - " + location.streetAddress + ", City - " + location.city);
    	}
        }
    }

    Asynchronous example:

    var APP_ID = "YOUR-APP-ID-GOES-HERE";
    var SECRET_KEY = "YOUR-SECRET-KEY";
    var VERSION = "v1";
    function handleResponse(restaurants) {
        console.log("Loaded " + restaurants.data.length + " restaurant objects" );
        console.log( "Total restaurants in the Backendless storage - " + restaurants.totalObjects);
        for(var i=0; i < restaurants.data.length; i++) {
            var restaurant = restaurants.data[i];
            console.log("Restaurant name = " + restaurant.name);
            printLocations(restaurant.locations);
        }
        console.log("Total time (ms) - " + ((new Date()).getMilliseconds() - startTime ));
    }
    function handleFault(backendlessFault) {
    		console.log( "Server reported an error - ");
    		console.log(backendlessFault.message);
    		console.log(backendlessFault.statusCode);
    	}
    function fetchingFirstPageAsync() {
        try {
            var startTime = (new Date()).getMilliseconds();
    	var callback = new Backendless.Async(handleResponse, handleFault);
    	Backendless.Persistence.of(Restaurant).find(callback);
        }
        catch(e) {
    	throw e.message;
        }
    }
    Backendless.initApp(APP_ID, SECRET_KEY, VERSION);
    fetchingFirstPageAsync();
    var startTime = (new Date()).getMilliseconds();

    Synchronous example:

    var APP_ID = "YOUR-APP-ID-GOES-HERE";
    var SECRET_KEY = "YOUR-SECRET-KEY";
    var VERSION = "v1";
    function fetchingFirstPage() {
        var startTime = (new Date()).getMilliseconds();
        var restaurants = Backendless.Persistence.of(Restaurant).find();
        console.log("============ Fetching first page using the SYNC API ============");
        console.log("Loaded " + restaurants.data.length + " restaurant objects");
        console.log("Total restaurants in the Backendless storage - " + restaurants.totalObjects);
        for(var i = 0; i < restaurants.data.length; i++){
        var restaurant = restaurants.data[i];
        console.log( "Restaurant name = " + restaurant.name);
            printLocations( restaurant.locations);
        }
        console.log( "Total time (ms) - " + ((new Date()).getMilliseconds() - startTime ));
    }
    Backendless.initApp(APP_ID, SECRET_KEY, VERSION);
    fetchingFirstPage();

    Program output:

    ============ Fetching first page using the SYNC API ============
    Loaded 4 restaurant objects
    Total restaurants in the Backendless storage - 4
    Restaurant name = McDonald's
    Restaurant locations have not been loaded
    Restaurant name = Buca Di Bepo
    Restaurant locations have not been loaded
    Restaurant name = Cantina Laredo
    Restaurant locations have not been loaded
    Restaurant name = Endless Sweets
    Restaurant locations have not been loaded
    Total time (ms) - 55
    ============ Fetching first page using the ASYNC API ============
    Loaded 4 restaurant objects
    Total restaurants in the Backendless storage - 4
    Restaurant name = McDonald's
    Restaurant locations have not been loaded
    Restaurant name = Buca Di Bepo
    Restaurant locations have not been loaded
    Restaurant name = Cantina Laredo
    Restaurant locations have not been loaded
    Restaurant name = Endless Sweets
    Restaurant locations have not been loaded
    Total time (ms) - 36

    -(void)printLocations:(NSArray *)locations {
        if (!locations) {
            NSLog(@"Restaurant locations have not been loaded");
            return;
        }
        if (!locations.count) {
            NSLog(@"There are no related locations");
            return;
        }
        for (Location *location in locations) {
            NSLog(@"Location: Street address - %@, City - %@", location.streetAddress, location.city);
        }
    }

    Asynchronous example:

    -(void)fetchingFirstPageAsync {
        NSLog(@"\n============ Fetching first page using the ASYNC API ============");
        NSDate *startTime = [NSDate date];
        BackendlessDataQuery *query = [BackendlessDataQuery query];
        [[backendless.persistenceService of:[Restaurant class]]
         find:query
         response:^(BackendlessCollection *restaurants) {
             NSArray *currentPage =[restaurants getCurrentPage];
             NSLog(@"Loaded %lu restaurant objects", (unsigned long)[currentPage count]);
             NSLog(@"Total restaurants in the Backendless storage - %@", [restaurants getTotalObjects]);
             for (Restaurant *restuarant in currentPage) {
                 NSLog(@"Restaurant name = %@", restuarant.name);
                 [self printLocations:restuarant.locations];
             }
             NSLog(@"Total time (ms) - %g", 1000*[[NSDate date] timeIntervalSinceDate:startTime]);
         }
         error:^(Fault *fault) {
             NSLog(@"Server reported an error: %@", fault);
         }];
    }
    @end

    Synchronous example:

    -(void)fetchingFirstPage {
        NSLog(@"\n============ Fetching first page using the SYNC API ============");
        @try {
            NSDate *startTime = [NSDate date];
            BackendlessDataQuery *query = [BackendlessDataQuery query];
            BackendlessCollection *restaurants = [[backendless.persistenceService of:[Restaurant class]] find:query];
            NSArray *currentPage =[restaurants getCurrentPage];
            NSLog(@"Loaded %lu restaurant objects", (unsigned long)[currentPage count]);
            NSLog(@"Total restaurants in the Backendless storage - %@", [restaurants getTotalObjects]);
            for (Restaurant *restuarant in currentPage) {
                NSLog(@"Restaurant name = %@", restuarant.name);
                [self printLocations:restuarant.locations];
            }
            NSLog(@"Total time (ms) - %g", 1000*[[NSDate date] timeIntervalSinceDate:startTime]);
        }
        @catch (Fault *fault) {
            NSLog(@"Server reported an error: %@", fault);
        }
    }

        func printLocations(locations: [Location]?) {
            if locations == nil {
                println("Restaurant locations have not been loaded")
                return
            }
            if locations?.count == 0 {
                println("There are no related locations")
                return
            }
            for location in locations! {
                println("Location: Street address - \(location.streetAdress), City - \(location.city)")
            }
        }

    Asynchronous example:

    func fetchingFirstPageAsync() {
            println("\n============ Fetching first page using the ASYNC API ============")
            var startTime = NSDate()
            var query = BackendlessDataQuery()
            backendless.persistenceService.of(Restaurant.ofClass()).find(
                query,
                response: { (var restaurants : BackendlessCollection!) -> () in
                    var currentPage = restaurants.getCurrentPage()
                    println("Loaded \(currentPage.count) restaurant objects")
                    println("Total restaurants in the Backendless starage - \(restaurants.totalObjects)")
                    for restaurant in currentPage as! [Restaurant] {
                        println("Restaurant name = \(restaurant.name)")
                        self.printLocations(restaurant.locations)
                    }
                    println("Total time (ms) - \(1000*NSDate().timeIntervalSinceDate(startTime))")
                },
                error: { (var fault : Fault!) -> () in
                    println("Server reported an error: \(fault)")
                }
            )
        }
    }

    Synchronous example:

    func fetchingFirstPage() {
            println("\n============ Fetching first page using the SYNC API ============")
            Types.try({ () -> Void in
                var startTime = NSDate()
                var query = BackendlessDataQuery()
                var restaurants = self.backendless.persistenceService.of(Restaurant.ofClass()).find(query)
                var currentPage = restaurants.getCurrentPage()
                println("Loaded \(currentPage.count) restaurant objects")
                println("Total restaurants in the Backendless starage - \(restaurants.totalObjects)")
                for restaurant in currentPage as! [Restaurant] {
                    println("Restaurant <\(restaurant.ofClass())> name = \(restaurant.name), cuisine = \(restaurant.cuisine)")
                    self.printLocations(restaurant.locations)
                }
                println("Total time (ms) - \(1000*NSDate().timeIntervalSinceDate(startTime))")
                },
                catch: { (exception) -> Void in
                    println("Server reported an error: \(exception as! Fault)")
                }
            )
        }


    There are several approaches for loading related objects for a parent entity. This post reviews one of them – the ‘auto-load’ option. This option is available in Backendless Console. The screenshot below shows the Restaurant table. Notice the ‘auto load’ checkbox in the “owner” and “locations” columns:

    When the checkbox is selected, Backendless automatically includes the related objects for the column into the response. If you select the auto-load checkbox for the “locations” column and re-run the code above, you will get the following output (JAVA example):

      ============ Fetching first page using the SYNC API ============
      Loaded 4 restaurant objects
      Total restaurants in the Backendless storage - 4
      Restaurant name = McDonald's
      There are no related locations
      Restaurant name = Buca Di Bepo
      There are no related locations
      Restaurant name = Cantina Laredo
      Location: Street address - 123 Main St., City - Frisco
      Restaurant name = Endless Sweets
      There are no related locations
      Total time (ms) - 548
      ============ Fetching first page using the ASYNC API ============
      Loaded 4 restaurant objects
      Total restaurants in the Backendless storage - 4
      Restaurant name = McDonald's
      There are no related locations
      Restaurant name = Buca Di Bepo
      There are no related locations
      Restaurant name = Cantina Laredo
      Location: Street address - 123 Main St., City - Frisco
      Restaurant name = Endless Sweets
      There are no related locations
      Total time (ms) - 305


      As you can see from the output, the related location for the “Cantina Laredo” restaurant has been loaded without making any changes to the code. All it took is the selected checkbox in the Console.

      Enjoy!