[Huawei Developers] Berk Özyurt:
Hello everyone,

In this article, I would like to tell you about Cloud DB, which online storage that Huawei offers to developers.

What is Cloud DB ?

Although Cloud DB is still in beta version, it is a successful and seamless database structure. In addition to the ease of use, attracts developers with its management and a user-friendly interface. In addition to providing data availability, consistency and security, CloudDB provides seamless data synchronization between the device and the cloud.

If you do not have a server when developing applications, Cloud DB server easily solves our data storage, maintenance and distribution. Also CloudDB is free.

Cloud DB provides 500 GB of data volume for each application, and supports 2000 connections. Looking at their counterparts, it is understood how large and how important these numbers are.

Cloud DB Structure

Object Type: It represents each table in the standard database. In other words, each table containing data and columns is called Object Type.

Cloud DB Zone : Represents the data zone on the cloud side. According to the classic database, Cloud DB Zone is the name of the database or schema name.

Data Entires : It is the area that shows the added data. Here, you can add, update and delete data. When you add data, you will realize that the tables you are used to are in the same way. And while using this technology, it prevents you from suffering from strangeness.

How To Using Cloud DB ?

Now let’s see how to use Cloud DB. Since Cloud DB is still in beta, you have to send a mail to activate this service in your app. To use Cloud DB, you need to create an app after creating your Huawei Developer account. After completing all the necessary steps, you have to request the activation of the service by sending an e-mail to agconnect@huawei.com with the sample header below. The service will be activated within 1–3 business days after your mail arrives. And you can start using it freely.

Cloud DB –Company Name — Developer ID — App ID

After your service activated, log in to AppGallery Connect and select your app under the heading “My Apps”. You can then access the “Cloud DB” panel under the “Build” tab from the menu on the left side of the page, by moving to the “Develop” tab in the upper left. After the page is loaded, activate the service by clicking the “Enable Now” button in the upper right corner.

A Cloud DB Zone must first be created. After then object types should be created. When creating the object type, column names must be entered and the primary key must be identified in the window that opens. Also, at this step, you can edit access control options for the object type. In the image of the below you can find, which users have which permissions should be given. After creating DB Zone and Object Type, Cloud DB provides to export data models and Helper class as JSON or Java for use them in the app without wasting time. After exporting the model classes as Java, you have to add these classes to the relevant directory of the app and start communicate with Cloud DB.

Cloud DB library should be added to the build.gradle file under the project’s app directory and the compatibility mode of the Java source code should be set as 1.8. For this, the following codes should be added to the gradle file and wait downloading the necessary dependencies by click Sync Now.

dependencies {
implementation 'com.huawei.agconnect:agconnect-database:1.2.1.301'
}
compileOptions {
targetCompatibility = 1.8
}

Let’s create a class named CloudDBZoneWrapper for all database operations. By defining all the upsert, query operations in this class, call these methods in the activity/fragment to be used. Thanks to this metods you can coding your app without clutter.

Firstly, Cloud DB objects should created to be used in this class.

private AGConnectCloudDB mCloudDB;
private CloudDBZone mCloudDBZone;
private ListenerHandler mRegister;
private CloudDBZoneConfig mConfig;

After then create an instance from the AGConnectCloudDB object in a constructor method.

public CloudDBZoneWrapper() {
mCloudDB = AGConnectCloudDB.getInstance();
}

Then, initAGConnectCloudDB method must be created for calling on the app landing page. This method must be run before the app is opened, before starting all DB operations. I follow the code by adding a log in every process step to follow the errors more easily. In this way, you can easily find out which line is wrong.

public static void initAGConnectCloudDB(Context context) {
AGConnectCloudDB.initialize(context);
Log.w(Constants.DB_ZONE_WRAPPER, "initAGConnectCloudDB" );
}

Next, creating Object Type, and open/close DBZone operations should be coding. These methods will be used before upsert and query operations for open DBZone and create Object Types.

public void createObjectType() {
try {
mCloudDB.createObjectType(ObjectTypeInfoHelper.getObjectTypeInfo());
Log.w(Constants.DB_ZONE_WRAPPER, "createObjectTypeSuccess " );
} catch (AGConnectCloudDBException e) {
Log.w(Constants.DB_ZONE_WRAPPER, "createObjectTypeError: " + e.getMessage());
}
}
public void openCloudDBZone() {
mConfig = new CloudDBZoneConfig("DB ZONE NAME HERE",
CloudDBZoneConfig.CloudDBZoneSyncProperty.CLOUDDBZONE_CLOUD_CACHE,
CloudDBZoneConfig.CloudDBZoneAccessProperty.CLOUDDBZONE_PUBLIC);
mConfig.setPersistenceEnabled(true);
Log.w(Constants.DB_ZONE_WRAPPER, "openCloudDBZoneSuccess " );
try {
mCloudDBZone = mCloudDB.openCloudDBZone(mConfig, true);
} catch (AGConnectCloudDBException e) {
Log.w(Constants.DB_ZONE_WRAPPER, "openCloudDBZoneError: " + e.getMessage());
}
}
public void closeCloudDBZone() {
try {
mCloudDB.closeCloudDBZone(mCloudDBZone);
Log.w(Constants.DB_ZONE_WRAPPER, "closeCloudDBZoneSuccess " );
} catch (AGConnectCloudDBException e) {
Log.w(Constants.DB_ZONE_WRAPPER, "closeCloudDBZoneError: " + e.getMessage());
}
}

Now, the necessary methods for upsert and query operations should be written. But first, a few callbacks have to be added to get the results of these actions to the activities and fragments where the actions are operated. In this way, all DB operations will be gathered in a single class, without the activity being too tired and without crowd of code.

public interface UiCallBack {
void onAddOrQuery(List<TableUser> userList);
void isLastID(int lastID);.
void isDataUpsert(Boolean state);
}
public void addCallBacks(UiCallBack uiCallBack) {
mUiCallBack = uiCallBack;
}

Now the necessary method for upsert operation should be written. Upsert contains both insert and update operations. If upsert with a certain ID, the data in the related line will update. If it is upsert by new ID, a new line will added. So, both insert and update are carried out with the same method.

First, it should be checked whether DBZone is created or not. If DBZone has an error, will not upsert data. Then, upsert with CloudDBZoneTask object. Since I will add data to the User table, I gave the user object as a parameter to this method. If you need to add data to other tables, you should create a new method and give the object of the related table as a parameter. When upsert operation complated , if upsert is successful, it must return true, if occured an error, return false. For this, at the beginning of the method, a variable named Boolean state was defined and its first value was set as false. Then, if upsert is successful, state is set to true, and if error occurs, method will return false.

public void insertUser(TableUser user) {
boolean state = false;
if (mCloudDBZone == null) {
Log.w(Constants.DB_ZONE_WRAPPER, "INSERT USER : CloudDBZone is null, try re-open it");
return;
}
CloudDBZoneTask<Integer> upsertTask = mCloudDBZone.executeUpsert(user);
if (mUiCallBack == null) {
return;
}
upsertTask.addOnSuccessListener(new OnSuccessListener<Integer>() {
@Override
public void onSuccess(Integer cloudDBZoneResult) {
state = true;
Log.w(Constants.DB_ZONE_WRAPPER, "INSERT USER : upsert " + cloudDBZoneResult + " records");
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
state = false;
mUiCallBack.updateUiOnError("INSERT USER : Insert user info failed");
}
});
if (mUiCallBack != null) {
mUiCallBack.isDataUpsert(state);
}
}

Now, let’s make a query in the this class. For this, I will get the list of users which I added to the database with the same model class. Two methods are required when making query request. The first is the getAllUsers method for DB operations, and the other is called userListResult method, for add data to array. Firstly, CloudDBZone control should be done in getAllUsers method. Then, query request will make by creating a task. If the request is successful, the userListResult method is calling with the user object. If the request is successful, the userListResult method will called with the user object. An arrayList is created in the userListResult method, and all results are thrown into this list. Then, by adding a callback, the results can be called up in the activity or fragment.

public void getAllUsers() {
if (mCloudDBZone == null) {
Log.w(Constants.DB_ZONE_WRAPPER, "GET USER DETAIL : CloudDBZone is null, try re-open it");
return;
}
CloudDBZoneTask<CloudDBZoneSnapshot<TableUser>> queryTask = mCloudDBZone.executeQuery(
CloudDBZoneQuery.where(TableUser.class),
CloudDBZoneQuery.CloudDBZoneQueryPolicy.POLICY_QUERY_FROM_CLOUD_ONLY);
queryTask.addOnSuccessListener(new OnSuccessListener<CloudDBZoneSnapshot<TableUser>>() {
@Override
public void onSuccess(CloudDBZoneSnapshot<TableUser> snapshot) {
userListResult (snapshot);
Log.w(Constants.DB_ZONE_WRAPPER, "GET USER DETAIL : GoResults: ");
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
if (mUiCallBack != null) {
mUiCallBack.updateUiOnError("GET USER DETAIL : Query user list from cloud failed");
}
}
});
}

private void userListResult (CloudDBZoneSnapshot<TableUser> snapshot) {
CloudDBZoneObjectList<TableUser> userInfoCursor = snapshot.getSnapshotObjects();
List<TableUser> userInfoList = new ArrayList<>();
try {
while (userInfoCursor.hasNext()) {
TableUser userInfo = userInfoCursor.next();
userInfoList.add(userInfo);
Log.w(Constants.DB_ZONE_WRAPPER, "USER DETAIL RESULT : processQueryResult: " + userInfo.getUser_city());

}
} catch (AGConnectCloudDBException e) {
Log.w(Constants.DB_ZONE_WRAPPER, "USER DETAIL RESULT : processQueryResult: " + e.getMessage());
}
snapshot.release();
if (mUiCallBack != null) {
mUiCallBack.onAddOrQuery(userInfoList);
}
}

Thus, all database operations within the CloudDBZoneWrapper class have been completed. Now let’s examine how to data upsert or query in activity or fragment.

The UiCallBack method in the CloudDBZoneWrapper class should be implement as the CloudDBZoneWrapper.UiCallBack in the class you which want to do database operations. In this way, all added call back methods will override in this class. Then the CloduDBZoneWrapper object and a new Handler need to be created in the activity. The CloudDBZoneWrapper object must be called within the constructor method. Sample codes are as follows.

private MyHandler mHandler = new MyHandler();
private CloudDBZoneWrapper mCloudDBZoneWrapper;
private static final class MyHandler extends Handler {
@Override
public void handleMessage(@NonNull Message msg) {
// dummy
}
}
public ProfileFragment() {
mCloudDBZoneWrapper = new CloudDBZoneWrapper();
}

Then the object type must be created in onCreate and Cloud DB Zone must be opened. If query is not related to an event, if the data should be loaded while the page is opening, call the getAllUsers method after creating the object type and opening the DB Zone in onCreate.

mHandler.post(() -> {
mCloudDBZoneWrapper.addCallBacks(ProfileFragment.this);
mCloudDBZoneWrapper.createObjectType();
mCloudDBZoneWrapper.openCloudDBZone();
mCloudDBZoneWrapper.getAllUsers();
});

The callback method, which was added to the getAllUsers method in the CloudDBZoneWrapper class, was override in the fragment. In this override method, all data can be used. For example, if wants to access the information of a user with ID = 3 in the user list, the turned list by insert a for loop, user information with ID = 3 is obtained.

@Override
public void onAddOrQuery(List<TableUser> userList) {
for(int i = 0; i <= userList.size()-1; i++){
if(userList.get(i).getId().equals(“3”)){
userName = userList.get(i).getUser_name());
userPhone = userList.get(i).getUser_phone();
userMail = userList.get(i).getUser_mail();
userAge = userList.get(i).getUser_age();
userGender = userList.get(i).getUser_gender();
}
}
}

Now let’s make an upsert. As I writed before, Upsert includes update and insert operations. Both operations are uses the same method. If you want to update a row of data, you must post with the ID information of the data in that row. If a new data is to be added, it should be posted with a new ID.

At this point, Cloud DB has a lack. Unfortunately, the auto increment don’t have when creating the object type. In other words, ID value does not increase automatically when data is added. It has to be given manually. I solved this problem by getting the last ID in the table and increasing it.

Now, create a method called updateUser to update and send back the user information I have previously got in this fragment. Next, a new user object should created here and the values ​​should set. If there is an not to be changed data (as in the example, age and gender ), old values ​​must be set in them. Finally, make post request by calling the insertUser method created in the CloudDBZoneWrapper class.

public void updateProfile(){
TableUser user = new TableUser();
user.setUser_id(“3”);
user.setUser_name(“Yeni İsim”));
user.setUser_phone(“Yeni Telefon”);
user.setUser_mail(“Yeni Mail”);
user.setUser_age(userAge);
user.setUser_gender(userGender);
mHandler.post(() -> {
mCloudDBZoneWrapper.insertUser(user);
});
}

In the fragment, help from call back methods should be obtained to find out whether the transaction is successful. Status check can be done in call back method added to insertUser method.

@Override
public void isDataUpsert(Boolean state) {
if(state){
//successful
}else{
//unsuccessful
}
}

Finally, it is worth mentioning that there is an authentication requirement to upsert. Since Cloud DB is still in beta, absolutely has some errors. But as you can see, all of them are solved easily. For authentication, Auth Service offered by Huawei to developers should be used. A service that is very easy to use. You can find the Auth Service link on the below. After the authentication, your upsert will work. If authentication is not done, the result of upsert will return false.

"https://developer.huawei.com/consumer/en/doc/development/AppGallery-connect-Guides/agc-auth-service-introduction"