Blog

How to Load Related Data Objects – The ‘Auto-Load’ Approach

by on September 1, 2019

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) {
           Log.i(TAG, "Restaurant locations have not been loaded");
       } else if (locations.size() == 0) {
           Log.i(TAG, "There are no related locations");
       } else {
           for (Location location : locations) {
               Log.i(TAG, "Location: Street address - " + location.getStreetAddress() + ", City - " + location.getCity());
           }
       }
    }

    API example:

    private static void fetchingFirstPageAsync() {
       final long startTime = System.currentTimeMillis();
    
       final AsyncCallback<List<Restaurant>> callback = new AsyncCallback<List<Restaurant>>() {
           @Override
           public void handleResponse(List<Restaurant> restaurants) {
               Log.i(TAG, "Loaded " + restaurants.size() + " restaurant objects");
    
               for (Restaurant restaurant : restaurants) {
                   Log.i(TAG, "Restaurant name = " + restaurant.getName());
                   printLocations(restaurant.getLocations());
               }
    
               Log.i(TAG, "Total time (ms) - " + (System.currentTimeMillis() - startTime));
           }
    
           @Override
           public void handleFault(BackendlessFault fault) {
               Log.e(TAG, fault.getMessage());
           }
       };
    
       Backendless.Data.of(Restaurant.class).getObjectCount(new AsyncCallback<Integer>() {
           @Override
           public void handleResponse(Integer count) {
               Log.i(TAG, "Total restaurants in the Backendless storage - " + count);
               Backendless.Data.of(Restaurant.class).find(callback);
           }
    
           @Override
           public void handleFault(BackendlessFault fault) {
               Log.e(TAG, fault.getMessage());
           }
       });
    }


    Method to print out related locations:

    private fun printLocations(locations: List<Location>?) {
       when {
           locations == null -> Log.i(TAG, "Restaurant locations have not been loaded")
           locations.size == 0 -> Log.i(TAG, "There are no related locations")
           else -> for (location in locations) {
               Log.i(TAG, "Location: Street address - ${location.streetAddress}, City - ${location.city}")
           }
       }
    }

    API example:

    private fun fetchingFirstPageAsync() {
       val startTime = System.currentTimeMillis()
    
       val callback = object : AsyncCallback<List<Restaurant>> {
           override fun handleResponse(restaurants: List<Restaurant>) {
               Log.i(TAG, "Loaded ${restaurants.size} restaurant objects")
    
               for (restaurant in restaurants) {
                   Log.i(TAG, "Restaurant name = ${restaurant.name}")
                   printLocations(restaurant.locations)
               }
    
               Log.i(TAG, "Total time (ms) - ${System.currentTimeMillis() - startTime}")
           }
    
           override fun handleFault(fault: BackendlessFault) {
               Log.e(TAG, fault.message)
           }
       }
    
       Backendless.Data.of(Restaurant::class.java).getObjectCount(object : AsyncCallback {
           override fun handleResponse(count: Int?) {
               Log.i(TAG, "Total restaurants in the Backendless storage - $count")
               Backendless.Data.of(Restaurant::class.java).find(callback)
           }
    
           override fun handleFault(fault: BackendlessFault) {
               Log.e(TAG, fault.message)
           }
       })
    }


    Location and Restaurant classes:

    @interface Restaurant : NSObject
    
    @property (strong, nonatomic) NSString *name;
    @property (strong, nonatomic) NSArray *locations;
    
    @end
    
    @interface Location : NSObject
    
    @property (strong, nonatomic) NSString *city;
    @property (strong, nonatomic) NSString *street;
    
    @end

    Method to print out related locations:

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

    API example:

    NSLog(@"============ Fetching first page ============");
        
    NSDate *startTime = [NSDate date];
    DataStoreFactory *dataStore = [Backendless.shared.data of:[Restaurant class]];
        
    [dataStore findWithResponseHandler:^(NSArray *restaurants) {
        NSLog(@"Loaded %lu restaurant objects", (unsigned long)restaurants.count);
            
        [dataStore getObjectCountWithResponseHandler:^(NSInteger totalRestaurants) {
            NSLog(@"Total restaurants in the Backendless storage - %li",(long)totalRestaurants);
            for (Restaurant *restaurant in restaurants) {
                NSLog(@"\nRestaurant name = %@", restaurant.name);
                [self printLocations:restaurant.locations];
            }
            NSLog(@"Total time (ms) - %g", 1000*[[NSDate date] timeIntervalSinceDate:startTime]);
        } errorHandler:^(Fault *fault) {
            NSLog(@"Error: %@", fault.message);
        }];
            
    } errorHandler:^(Fault *fault) {
        NSLog(@"Error: %@", fault.message);
    }];


    Location and Restaurant classes:

    @objcMembers class Restaurant: NSObject {
        var name: String?
        var locations: [Location]?
    }
    
    @objcMembers class Location: NSObject {
        var city: String?
        var street: String?
    }

    Method to print out related locations:

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

    API example:

    print("============ Fetching first page ============")
            
    let startTime = Date()
    let dataStore = Backendless.shared.data.of(Restaurant.self)
            
    dataStore.find(responseHandler: { restaurants in            
        print("Loaded \(restaurants.count) restaurant objects")
    
        dataStore.getObjectCount(responseHandler: { totalRestaurants in
            print("Total restaurants in the Backendless storage - \(totalRestaurants)")
                    
            if let restaurants = restaurants as? [Restaurant] {
                for restaurant in restaurants {
                    print("\nRestaurant name = \(restaurant.name ?? "")")
                    self.printLocations(restaurant.locations)
                }
                print("Total time (ms) - \(Int(Date().timeIntervalSince(startTime) * 1000))")
            }
                    
        }, errorHandler: { fault in
            print("Error: \(fault.message ?? "")")
        })
    }, errorHandler: { fault in
        print("Error: \(fault.message ?? "")")
    })

    const Backendless = require('backendless')
    /*
     Or use `import Backendless from 'backendless'` for client side.
     If you don't use npm or yarn to install modules, you can add the following line
     <script src="//api.backendless.com/sdk/js/latest/backendless.min.js"></script>
     to your index.html file and use the global Backendless variable.
    */
    
    Backendless.initApp('YOUR_APP_ID', 'YOUR_JS_API_KEY')
    
    const loadRestaurants = () => {
      return Backendless.Data.of('Restaurant').find()
    }
    
    const onSuccess = restaurants => {
      restaurants.forEach(restaurant => {
        printLocations(restaurant.locations)
      })
    }
    
    const printLocations = locations => {
      if (!locations) {
        return console.log('Restaurant locations have not been loaded')
      }
    
      if (!locations.length) {
        return console.log('There are no related locations')
      }
    
      locations.forEach(location => {
        console.log(`Location: Street address - ${ location.streetAddress }, City - ${ location.city }`)
      })
    }
    
    const onError = error => {
      console.error('Server reported an error: ', error.message)
      console.error('error code: ', error.code)
      console.error('http status: ', error.status)
    }
    
    Promise.resolve()
      .then(loadRestaurants)
      .then(onSuccess)
      .catch(onError)
    


    Method to print out related locations:

    static void _printLocations(List<Map> locations) {
      if (locations == null) {
        print("Restaurant locations have not been loaded");
      } else if (locations.isEmpty) {
        print("There are no related locations");
      } else {
        for (Map location in locations) {
          print("Location: Street address - ${location['streetAddress']}, City - ${location['city']}");
        }
      }
    }

    API example:

    static void _fetchingFirstPageAsync() {
       int startTime = DateTime.now().millisecondsSinceEpoch;
    
       Backendless.Data.of("Restaurant").getObjectCount().then((count) {
         print("Total restaurants in the Backendless storage - $count");
        
         Backendless.Data.of("Restaurant").find().then((restaurants) {
           print("Loaded ${restaurants.length} restaurant objects");
    
           restaurants.forEach((restaurant) {
             print("Restaurant name = ${restaurant['name']}");
             _printLocations(restaurant['locations'].cast
    ());
           });
    
           print("Total time (ms) - ${DateTime.now().millisecondsSinceEpoch - startTime}");
         });
       });
     }


    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

    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!

      Leave a Reply