التعامل مع قواعد البيانات باستخدام روم Room Jetpack
15 June
مرحبا عدنا الى شروحات الترسانة JetPack و هذه المرة مع Room لادارة قاعدة البيانات بافضل طريقة ممكنة و باقل عدد من الاخطاء قد تكون Room السلاح الاكثر قابلية للاستعمال مع بين ترسانة Jetpack اذا سبق و حصلت على العديد من الاخطاء بسبب قاعدة البيانات الحقيرة SQLite فان Room هو الحل بدل التعامل cursor ستتعامل مع الكلاسات و هذا افضل و اسرع .
2-تهيئ البيئة :
قم باضافة روم الى ملف
يمكنك معرفة اخر اصدار من المكتبة من هنا :
3- معاينة الجدول:
سنقوم بانشاء جدول به المعرف و يجب ان يكون فريد unique و الاسم و العمر و هكذا سيكون :
4-حسنا الان الى التطبيق :
حسنا سنقوم بانشاء داتا كلاس(اساسيات كوتلن) و فيه سيكون المعرف و الاسم و العمر :
الان سنقوم باضافة DAO :
ما هو DAO هو كائن object يسمح لك للتحقق من صحة الاستعلام اثناء الكومبلين حسنا بعبارة اوضح هو للوصول الى قاعدة البيانات بامان تام من اخطاء برمجية .
DAO اما ان يكون Interface او abstract انا ساستعمل interface :
ستلاحظ انه رغم ان الاستعلامات عبارة عن String الا انه يتم التحقق من المعلومات التي فيه .
يتم استقبال القيم عبر interface و يتم اخدها من الانترفيس فقط باضافة نقطتين قبل اسم المتغير .
قابلة للتحقق
كل الكودات التالية يمكن جمعها في ملف واحد باسم AppDatabase.kt
حسنا لننتقل الى الجهة الاخرى وهي الواجهة :
Main.kt
تجدر الاشارة الى انني استخدمت مكتبة انكو للوصول الى thread منفصل و لابد من طلب الوصول الى قاعدة البيانات من خلال thread منفصل .
5-تخزين الكائنات :
احزر ماذا يمكن باستعمال مكتبة مثل gson تخزين List او اي كائن او نوع يحتوي على بيانات لنفترض ان لدينا Map :
لنقم باضافة حقل خاص ب Map الى data Class السابق Person .
ما سنقوم به الان هو اضافة مكتبةgson الى المشروع .
الان نضيف المحول من json الى ماب في/تحت ملف/كلاس PersonDoa .
ثم نربطه مع Appdatabase .
و في النهاية يمكنك تخزين القيمة في Person و روم ستتكلف بالباقي .
الى هنا نكون قد انتهينا من درس اليوم .
المصادر :
1-ما هي مميزات الروم
- لا تسمح لك روم بان ترسل استعلاما خاطئا لقاعدة البيانات فالتحقق يتم اثناء برمجة التطبيق
- ستحتاج الى طلب او instance واحد لقاعدة البيانات في كل التطبيق
- تتميز روم بالسرعة مقارنة مع realm
- تتميز بالحجم الصغير مقارنة مع realm
- لا يمكن الاتصال بروم من خلال thread الرئيسي و انما من خلال thread منفصل -اختياري-
2-تهيئ البيئة :
قم باضافة روم الى ملف
build.gradle (Module: app)
apply plugin: 'kotlin-android'
//...
dependencies {
//...
implementation "android.arch.persistence.room:runtime:1.1.1"
kapt "android.arch.persistence.room:compiler:1.1.1"
}
implementation "android.arch.persistence.room:runtime:1.1.1"
annotationProcessor "android.arch.persistence.room:compiler:1.1.1"
3- معاينة الجدول:
سنقوم بانشاء جدول به المعرف و يجب ان يكون فريد unique و الاسم و العمر و هكذا سيكون :
table SQLite
+-------+--------------+------+-----+----------------+ | Field | Type | Null | Key | Extra | +-------+--------------+------+-----+----------------+ | id | int | YES | PRI | auto_increment | | name | String | NO | | | | age | String | NO | | | +-------+--------------+------+-----+----------------+
4-حسنا الان الى التطبيق :
حسنا سنقوم بانشاء داتا كلاس(اساسيات كوتلن) و فيه سيكون المعرف و الاسم و العمر :
- @PrimaryKey : لاجل جعل المعرف خاصا و هو اجباري
- autoGenerate : يعني اذا كنت تريد ملىء الحقل تزايديا ضع true
- @ColumnInfo : وضع اسم الحقل اذا كنت ترغب فاعطائه اسما غير الاسم الموجود في الكلاس
AppDatabase.kt (Data Class → Person)
@Entity
data class Person(@PrimaryKey(autoGenerate = true) var id:Int,
@ColumnInfo(name = "name") var name:String,
@ColumnInfo(name = "age") var age:Double)
@Entity
public class Person {
@PrimaryKey
private int id;
@ColumnInfo(name = "name")
private String name;
@ColumnInfo(name = "age")
private Double age;
//getter & setter
// مهمين جدا ليعمل الكود و لم اضفهما اختصارا
}
ما هو DAO هو كائن object يسمح لك للتحقق من صحة الاستعلام اثناء الكومبلين حسنا بعبارة اوضح هو للوصول الى قاعدة البيانات بامان تام من اخطاء برمجية .
DAO اما ان يكون Interface او abstract انا ساستعمل interface :
AppDatabase.kt (Interface → PersonDao)
@Dao
interface PersonDao {
@Query("SELECT * FROM person")
fun getAll(): List<Person>
@Query("SELECT * FROM person WHERE id IN (:personIds)")
fun loadAllByIds(userIds: IntArray): List<Person>
@Query("SELECT * FROM person WHERE name LIKE :name LIMIT 1")
fun findByName(name: String): Person
@Insert(onConflict = REPLACE)
fun insertAll(vararg users: Person)
// or fun insertAll(users: List<Person>)
@Delete
fun delete(user: Person)
}
@Dao
public interface PersonDao {
@Query("SELECT * FROM person")
List getAll();
@Query("SELECT * FROM person WHERE uid IN (:personIds)")
List loadAllByIds(int[] personIds);
@Query("SELECT * FROM person WHERE name LIKE :name AND "
+ "age LIKE :age LIMIT 1")
Person findByName(String name, String age);
@Insert
void insertAll(Person... persons);
@Delete
void delete(Person person);
}
يتم استقبال القيم عبر interface و يتم اخدها من الانترفيس فقط باضافة نقطتين قبل اسم المتغير .
قابلة للتحقق
- @Insert لاضافة مستخدم الى قاعدة البيانات , يرجى الانتباه الى امكانية وجود تعارض conflit .
- @Query لعمل استعلام على قاعدة البيانات .
- @Update لتحديث معلومات المستخدم على قاعدة البيانات .
- @Delete لحذف مستخدم على قاعدة البيانات .
- @RawQuery لاستقبال استعلامات مجهولة و يستحق تدوينة منفردة .
AppDatabase.kt (Main Class → AppDatabase)
@Database(entities = arrayOf(Person::class), version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun personDao(): PersonDao
}
@Database(entities = {Person.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
public abstract PersonDao personDao();
}
كل الكودات التالية يمكن جمعها في ملف واحد باسم AppDatabase.kt
حسنا لننتقل الى الجهة الاخرى وهي الواجهة :
Main.kt
Main.kt (Activity → Main)
class Main : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.ac_main)
//استخدام مكتبة انكو لتسهيل الوصول الى تريد thread منفصل
doAsync {
//instance
val db = Room.databaseBuilder(this@Main, AppDatabase::class.java, "database-name").build()
//انشاء مستحدم جديد
val person=Person( null, "Ismail Bella", 19.0)
//اضافة المستخدم الى قاعدة البيانات من خلال interface
db.userDao().insertAll(person)
// ارسال استعلام للحصول على معلومات المستخدم المسجلة من خلال الاسم
val rp=db.userDao().findByName("Ismail Bella")
//طباعة عمر المستخدم على Log
uiThread { Log.e("your age",rp.age.toString()) }
}
}
}
public class Main extends AppCompatActivity {
@Override
public void onCreate(@Nullable Bundle savedInstanceState, @Nullable PersistableBundle persistentState) {
super.onCreate(savedInstanceState, persistentState);
setContentView(R.layout.ac_main);
new getPerson().execute();
}
private class getPerson extends AsyncTask {
@Override protected Person doInBackground(Void... voids) {
//instance
AppDatabase db = Room.databaseBuilder(getApplicationContext(), AppDatabase.class, "database-name").build();
//انشاء مستحدم جديد
Person person = new Person(null, "Ismail Bella", 19.0);
//اضافة المستخدم الى قاعدة البيانات من خلال interface
db.userDao().insertAll(person);
// ارسال استعلام للحصول على معلومات المستخدم المسجلة من خلال الاسم
Person rp = db.userDao().findByName("Ismail Bella");
return rp;
}
//طباعة عمر المستخدم على Log
protected void onPostExecute(Person result) {
Log.e("your age", result.getAge().toString());
}
}
}
5-تخزين الكائنات :
احزر ماذا يمكن باستعمال مكتبة مثل gson تخزين List او اي كائن او نوع يحتوي على بيانات لنفترض ان لدينا Map :
val map = mapOf(1 to "x", 2 to "y", -1 to "zz")
Map<Integer,String> map=new HashMap<Integer,String>();
map.put(1,"x");
map.put(2,"y");
map.put(-1,"zz");
@Entity
data class Person(@PrimaryKey(autoGenerate = true) var id:Int,
@ColumnInfo(name = "name") var name:String,
@ColumnInfo(name = "age") var age:Double,
var map:Map<Int,String>)
@Entity
public class Person {
@PrimaryKey
private int id;
@ColumnInfo(name = "name")
private String name;
@ColumnInfo(name = "age")
private Double age;
private Map<Integer,String> map;
//getter & setter
// مهمين جدا ليعمل الكود و لم اضفهما اختصارا
}
implementation 'com.google.code.gson:gson:2.8.5'
class MapConverter {
@TypeConverter
fun fromString(value: String): Map<String,String> {
val listType = object : TypeToken<Map<String, String>>() {
}.type
return Gson().fromJson(value, listType)
}
@TypeConverter
fun fromList(list: Map<String,String>): String {
val gson = Gson()
return gson.toJson(list)
}
}
class MapConverter {
@TypeConverter
Map<String,String> fromString(String value){
Type listType = new TypeToken<Map<Integer, String>>() {}.getType();
return new Gson().fromJson(value, listType);
}
@TypeConverter
String fromList( Map<Integer,String> list) {
Gson gson = new Gson();
return gson.toJson(list);
}
}
@Database(entities = arrayOf(Person::class), version = 1)
@TypeConverters(MapConverter::class)
abstract class AppDatabase : RoomDatabase() {
abstract fun personDao(): PersonDao
}
@Database(entities = {Person.class}, version = 1)
@TypeConverters({MapConverter::class})
public abstract class AppDatabase extends RoomDatabase {
public abstract PersonDao personDao();
}
val map = mapOf(1 to "x", 2 to "y", -1 to "zz")
val person=Person( null, "Ismail Bella", 19.0,map)
Map<Integer,String> map=new HashMap<Integer,String>();
map.put(1,"x");
map.put(2,"y");
map.put(-1,"zz");
Person person = new Person(null, "Ismail Bella", 19.0,map);
db.userDao().insert(person);
المصادر :
اترك لنا تعليقا