Huawei Cloud DB Nedir? Nasıl Kullanılır?

Berk Özyurt
Huawei Developers - Türkiye
8 min readMay 7, 2020

Herkese merhaba,

Bu yazımda sizlere Huawei’nin geliştiricilere sunduğu online depolama alanı Cloud DB’den bahsetmek istiyorum.

Cloud DB Nedir?

Cloud DB henüz beta sürümünde olmasına rağmen gayet başarılı ve sorunsuz bir şekilde çalışan bir veritabanı yapısı. Kullanım kolaylığının yanı sıra yönetim kolaylığı ve kullanıcı dostu bir arayüz ile de geliştiricileri kendisine çekiyor. Veri kullanılabilirliği, tutarlılık ve güvenlik sağlamanın yanı sıra CloudDB, cihaz ve bulut arasında kesintisiz veri senkronizasyonu sağlıyor.

Eğer Uygulama geliştirirken sunucunuz yoksa, Cloud DB sunucusu veri depolama, bakım ve dağıtımımızı kolayca çözer. Ayrıca CloudDB ücretsizdir.

Cloud DB her bir uygulama için 500 GB veri hacmi sağlarken tam 2000 bağlantı sayısını desteklemektedir. Benzerlerine bakılınca bu rakamların ne kadar büyük ve ne kadar önemli olduğu rahatlıkla görülebilir.

Cloud DB Yapısı

Object Type : Klasik veri tabanı mantığında bulunan her bir tabloyu temsil eder. Yani içinde verilerin tutulduğu, sütunların olduğu her bir tabloya Object Type denilir.

Cloud DB Zone : Bulut tarafındaki veri bölgesini temsil eder. Yine klasik veri tabanı mantığı ile düşünülürse veri tabanı adına, şema adına karşılık gelir.

Data Entires : Data entires kısmı ise oluşturulan Object Type’lara eklenen veriler listelenir, güncellenir ve silinir. Veri ekledikçe göreceksiniz ki alışkın olduğunuz tablolar aynı şekilde burada yer alıyor ve bu teknolojiyi kullanırken yabancılık çekmenizin önüne geçiyor.

Cloud DB Nasıl Kullanılır ?

Şimdi gelelim Cloud DB’nin nasıl kullanılacağına. Cloud DB henüz beta sürümünde olduğu için bu servisin uygulamanızda aktif edilmesi için bir mail göndermeniz gerekiyor. Cloud DB kullanmak için Huawei Developer accountunuzu oluşturduktan sonra bir uygulama yaratmanız gerekmekte. Gerekli tüm adımları tamamladıktan sonra agconnect@huawei.com adresine aşağıdaki örnek başlık ile mail göndererek servisin aktif edilmesini talep etmeniz gerek. Mailiniz ulaştıktan sonra 1–3 iş günü içinde servis aktif edilecektir ve özgürce kullanmaya başlayabilirsiniz.

Cloud DB — Şirket Adı — Developer ID — App ID

Servisiniz aktif edildikten sonra, AppGallery Connect’e giriş yapıp “My Apps” başlığından uygulamanızı seçin. Daha sonra sol üst tarafta bulunan “Develop” sekmesine ilerleyerek açılan sayfanın sol tarafındaki menüden “Build” sekmesinin altından “Cloud DB” paneline erişebilirsiniz. Sayfa yüklendikten sonra sağ üst köşede bulunan “Enable Now” butonuna tıklayarak servisi kullanıma hazır hale getirebilirsiniz.

Sırası ile ilk önce bir Cloud DB Zone oluşturulmalı. Daha sonra ihtiyaç duyulan object typelar oluşturulmalı. Object type oluştururken açılan pencerede sütun isimleri girilip, primery key belirlenmelidir. Ayrıca bu aşamada object type için bir erişim kontrolü yapılabilir. Aşağıdaki görselde hangi kullanıcılara hangi yetkileri vermemiz gerektiğini görebilirsiniz. Koskoca bir veri tabanı oluşturmak işte bu kadar kolay. DB Zone ve Object Type oluşturduktan sonra bunları uygulamada vakit kaybetmeden kullanmak için Cloud DB veri modellerini ve Helper sınıfını JSON veya Java olarak export etmemizi sağlıyor. Model sınıflarını Java olarak export ettikten sonra uygulamanın ilgili dizinine bu classları ekleyip uygulamanın Cloud DB ile iletişim kurmasına başlayabiliriz artık.

Projenin app dizinin altındaki build.gradle dosyası içine Cloud DB kütüphanesi eklenip Java kaynak kodun uyumluluk modu 1.8 olarak belirlenmelidir. Bunun için aşağıdaki kodlar gradle dosyasına eklenmeli ve Sync Now diyerek gerekli bağımlılıkların indirilmesi beklenmeli.

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

Şimdi tüm veri tabanı işlemlerinin yapılacağı CloudDBZoneWrapper isimli sınıfı oluşturalım. Tüm upsert, query işlemlerini bu sınıf içinde tanımlayıp, kullanılacak yerlerde bu metotları çağırarak kolaylıkla ve kod kalabalığı olmadan çalışılabilir.

Bu sınıfta ilk önce kullanılacak Cloud DB nesnelerini oluşturulur.

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

Ardından bir kurucu metot ile AGConnectCloudDB nesnesinden bir örnek oluşturulur.

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

Daha sonra uygulamanın açılış sayfasında çağırmak için initAGConnectCloudDB isimli metot oluşturulmalı. Bu metot uygulama ilk açıldığında, tüm DB işlemlerine başlamadan önce çalıştırılmış olmalıdır. Hata ayıklamayı kolaylaştırmak için ben her işlem adımında bir log ekleyerek kodu takip ediyorum. Bu sayede bir hatanın ya da eksikliğin nerede başladığını kolaylıkla bulabilirsiniz. Bu doğrultuda yine log ekliyorum.

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

Daha sonra objectType yaratma, ve DBZone açıp kapatma işlemleri yapılmalı. Bu metotlar yapılacak upsert ve query işlmelerinde Zone açmak ve object type yaratmak için kullanılacak.

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 ADI BURAYA YAZILACAK", 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()); }}

Şimdi upsert ve query işlemleri için gerekli metotlar yazılmalı. Fakat öncesinde bu işlemlerin sonuçlarını, işlemlerin yapıldığı activitylere ve fragmentlara almak için bir kaç tane callback eklenmesi gerek. Bu sayede activity fazla yorulmadan, kod kalabalığı olmadan tüm DB işlemleri tek bir classta toplanmış olacak.

public interface UiCallBack { // Activity'de override edilecek.  void onAddOrQuery(List<TableUser> userList); //Veri çekerken.  void isLastID(int lastID);// ID bilgisini almak için.  void isDataUpsert(Boolean state); // Veri eklerken}public void addCallBacks(UiCallBack uiCallBack) {  mUiCallBack = uiCallBack;}

Şimdi upsert işlemi için gerekli metot yazılmalı. Upsert, hem insert işlemini hem de update işlemini içinde barındır. Eğer primary key olarak belirlenen bir ID ile upsert işlemi yapılırsa ilgili satırdaki veriler güncellenmiş olur. Yeni bir ID atayarak upsert edilirse yeni bir satır eklenir. Yani, her iki işlem de aynı metot ile yapılmaktadır.

Burada ilk önce DBZone’un yaratılıp yaratılmadığı kontrol edilmeli. Eğer DBZone’da hata varsa upsert işlemi gerçekleşmeyecektir. Daha sonra CloudDBZoneTask ile upsert işlemi gerçekleştirilir. Ben user tablosuna veri ekleyeceğim için bu metoda parametre olarak user nesnesini verdim. Farklı tablolara ekleme işlemi yapmanız gerekirse yeni bir metot oluşturup parametre olarak ilgili tablonun nesnesini vermelisiniz. İşlemler gerçekleştiğinde eğer upsert işlemi başarılıysa true değer döndürmeli, eğer bir hata oluştuysa false değer döndürmeli. Bunun için metodun başında Boolean state adında bir değişken tanımlayıp başlangıç değeri false olarak atandı. Daha sonra eğer upsert başarılıysa state true yapılıyor, hata oluşursa metod false değeri döndürüyor.

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); }}

Şimdi aynı sınıf içinde bir de query yani listeleme işlemi yapalım. Bunun için yine aynı model sınıfından veri tabanına eklediğim kullanıcıların listesini çekeceğim. Query işlemi yapılırken iki metoda ihtiyaç duyulur. Birincisi DB işlemlerinin yapıldığı getAllUsers metodu, diğeri ise verilerin bir array’e eklendiği userListResult metodu. getAllUsers metodu içinde ilk olarak CloudDBZone kontrolü yapılmalı. Daha sonra bir task oluşturarak query işlemi yapılmalı. Eğer istek başarılı ise user nesnesi ile userListResult metodunu çağrılır. userListResult metodu içinde bir arrayList oluşturularak dönen tüm sonuçlar bu arraye atılır. Daha sonra yine callback ekleyerek sonuçların activity içinde çağrılabilmesi sağlanır.

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: ");

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

Böylelikle CloudDBZoneWrapper sınıfı içinde tüm veri tabanı işlemleri tamamlanmış oldu. Şimdi activity veya fragment içinde bu verilerin nasıl upsert edeceğini ya da query ile nasıl çekebileceğini inceleyelim.

Veritabanı işlemleri yapmak istediğimiz sınıfa CloudDBZoneWrapper sınıfındaki UiCallBack metodu CloudDBZoneWrapper.UiCallBack şeklinde implement edilmeli. Bu sayede eklenen tüm call back metodları override edilmiş oluyor. Daha sonra activity içinde CloudDBZoneWrapper nesnesi ve yeni bir Handler oluşturulması gerekiyor. CloudDBZoneWrapper nesnesi kurucu metot içinde çağrılmalı. Örnek kodlar aşağıdaki gibidir.

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();}

Daha sonra onCreate içinde object type oluşturulmalı ve Cloud DB Zone açılmalı. Eğer query işlemleri bir evente bağlı gerçekleşmeyecekse, sayfa açılır açılmaz verilerin yüklenmesi isteniyorsa getAllUsers metodu da onCreate içinde object type oluşturduktan ve DB Zone açtıktan sonra çağırabilir.

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

Daha önce CloudDBZoneWrapper sınıfı içinde getAllUsers metoduna eklenen callback metodu fragment içinde override edilmişti. Bu override metodu içinde istenilen tüm veriler istenilen şekilde kullanılabilir. Örneğin kullanıcı listesinde ID değeri 3 olan bir kullanıcının bilgilerine erişmek isteniyorsa, dönen user modelli listeyi bir for döngüsüne alıp, ID değeri 3 olan user geldiğinde bilgileri alınabilir.

@Overridepublic 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();    }  }}

Şimdi ise son olarak bir upsert işlemi yapalım. Upsert daha önce de belirttiğim gibi update ve insert işlemlerini kapsıyor. Her iki işlem de aynı metot kullanılarak yapılır. Eğer bir veri satırı update edilmek isteniyorsa, o satırdaki verinin ID bilgisi ile birlikte post edilmeli. Eğer yeni bir veri eklenmek isteniyorsa yeni bir ID ile post edilmeli.

Bu noktada Cloud DB’nin bir eksiği hissediliyor. Object type oluştururken auto increment özelliği maalesef yok. Yani veri eklendikçe ID değeri otomatik olarak artmıyor. Bunun manuel olarak elle verilmesi gerekiyor. Ben sorunu tablodaki en son ID değerini çekip, bir arttırıp, yeni ID ataması yaparak çözdüm. Bunun yerine uygulama içinde random değerler de oluşturulabilir. Düşük bir ihtimal de olsa aynı değer gelme şansı elbette var. Bunu da unutmamak lazım. Onun için en güvenli yöntem, en son ID değerini çekip bir arttırarak yeni ID yaratmak.

Şimdi daha önce çektiğim kullanıcı bilgilerini güncelleyip geri göndermek için updateUser isminde bir metot oluşturuyorum fragment içinde. Daha sonra burada yeni bir user nesnesi yaratılıp, değerler set edilir. Eğer değişmesi istenmeyen bir veri varsa (örnekte olduğu gibi yaş ve cinsiyet) bunlara eski değerler set edilmeli. Aksi halde upsert işlemi sonunda set edilmeyen değerler boş kalacaktır. Son olarak CloudDBZoneWrapper sınıfı içinde oluşturulan insertUser metodu çağılarak post işlemi gerçekleştirilir.

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);  });}

İşlemin başarılı olup olmadığını fragment içinde öğrenmek için call back metotlardan yardım alınmalı. insertUser metoduna eklenen call back metodu içinde durum kontrolü yapılabilir.

@Overridepublic void isDataUpsert(Boolean state) {  if(state){    //işlem başarılı  }else{    //işlem başarısız  }}

Son olarak belirtmekte fayda var ki upsert işlemi yapabilmek için kimlik doğrulama şartı var. Cloud DB henüz beta sürümünde olduğu için ufak tefek eksikleri elbette ki var. Fakat görüldüğü gibi hepsinin çözümü gayet kolay. Kimlik doğrulama işleme için yine Huawei’nin geliştiricilere sunduğu Auth Service kullanılmalı. Kullanımı gayet kolay, fazla işlem gerektirmeyen bir servis. Nasıl kullanılacağına dair linke aşağıdan ulaşabilirsiniz. Kimlik doğrulama işlemi yapıldıktan sonra upsert işlemleriniz çalışacaktır, eğer kimlik doğrulama yapılmamış ise upsert işleminin sonucu false dönecek ve kullanıcının upsert yetkisi olmadığına dair bir hata mesajı alacaksınız.

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

Referanslar

--

--