一、Room
1、Room 概述
- Room 是 Jetpack 中的一个重要成员,它是一个持久化库,它为管理数据库提供了简单强大的方法
2、Room 引入
- 在模块级
build.gradle
中引入相关依赖
implementation "androidx.room:room-runtime:2.2.5"
annotationProcessor "androidx.room:room-compiler:2.2.5"
二、Room 简单案例
1、Application
- MyApplication.java
package com.my.room.application;import android.app.Application;
import android.content.Context;public class MyApplication extends Application {public static final String TAG = MyApplication.class.getSimpleName();private static Context context;@Overridepublic void onCreate() {super.onCreate();context = this;}public static Context getContext() {return context;}
}
2、Entity
- Staff.java
package com.my.room.entity;import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.PrimaryKey;@Entity(tableName = "Staff")
public class Staff {@PrimaryKey(autoGenerate = true)public Long id;@ColumnInfo(name = "name")public String name;@ColumnInfo(name = "age")public Integer age;@ColumnInfo(name = "cardNum")public String cardNum;@Overridepublic String toString() {return "Staff{" +"id=" + id +", name='" + name + '\'' +", age=" + age +", cardNum='" + cardNum + '\'' +'}';}
}
3、DAO
- StaffDAO.java
package com.my.room.dao;import androidx.room.Dao;
import androidx.room.Delete;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.Update;import com.my.room.entity.Staff;import java.util.List;@Dao
public interface StaffDAO {@Insertvoid insert(Staff staff);@Deletevoid delete(Staff staff);@Updatevoid update(Staff staff);@Query("SELECT * FROM Staff")List<Staff> queryAll();
}
4、Database
- MyDatabase.java
package com.my.room.database;import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;import com.my.room.application.MyApplication;
import com.my.room.dao.StaffDAO;
import com.my.room.entity.Staff;@Database(entities = {Staff.class}, version = 1, exportSchema = false)
public abstract class MyDatabase extends RoomDatabase {public static final String TAG = MyDatabase.class.getSimpleName();public static final String DATABASE_NAME = "test.db";private static MyDatabase myDatabase;public static MyDatabase getInstance() {if (myDatabase == null) {myDatabase = Room.databaseBuilder(MyApplication.getContext(), MyDatabase.class, DATABASE_NAME).build();}return myDatabase;}public abstract StaffDAO getStaffDAO();
}
5、Repository Observer
- InsertObserver.java
package com.my.room.repository.observer;public interface InsertObserver {void onSuccess();void onError(String msg);
}
- DeleteObserver.java
package com.my.room.repository.observer;public interface DeleteObserver {void onSuccess();void onError(String msg);
}
- UpdateObserver.java
package com.my.room.repository.observer;public interface UpdateObserver {void onSuccess();void onError(String msg);
}
- QueryAllObserver.java
package com.my.room.repository.observer;import java.util.List;public interface QueryAllObserver<T> {void onResult(List<T> ts);
}
6、Repository
- RepositoryItem.java
package com.my.room.repository;public class RepositoryItem<T> {public static final int CODE_SUCCESS = 1;public static final int CODE_ERROR = 2;private int code;private String msg;private T data;public RepositoryItem(int code, String msg, T data) {this.code = code;this.msg = msg;this.data = data;}public int getCode() {return code;}public void setCode(int code) {this.code = code;}public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;}public T getData() {return data;}public void setData(T data) {this.data = data;}@Overridepublic String toString() {return "RepositoryItem{" +"code=" + code +", msg='" + msg + '\'' +", data=" + data +'}';}
}
- StaffRepository.java
package com.my.room.repository;import android.os.AsyncTask;import com.my.room.dao.StaffDAO;
import com.my.room.database.MyDatabase;
import com.my.room.entity.Staff;
import com.my.room.repository.observer.DeleteObserver;
import com.my.room.repository.observer.InsertObserver;
import com.my.room.repository.observer.QueryAllObserver;
import com.my.room.repository.observer.UpdateObserver;import java.util.List;public class StaffRepository {private MyDatabase myDatabase;private StaffDAO staffDAO;public StaffRepository() {myDatabase = MyDatabase.getInstance();staffDAO = myDatabase.getStaffDAO();}// ====================================================================================================public void insert(Staff staff, InsertObserver insertObserver) {new InsertAsyncTask(staff, insertObserver).execute();}public void delete(Staff staff, DeleteObserver deleteObserver) {new DeleteAsyncTask(staff, deleteObserver).execute();}public void update(Staff staff, UpdateObserver updateObserver) {new UpdateAsyncTask(staff, updateObserver).execute();}public void queryAll(QueryAllObserver<Staff> queryAllObserver) {new QueryAllAsyncTask(queryAllObserver).execute();}// ====================================================================================================private class InsertAsyncTask extends AsyncTask<Void, Void, RepositoryItem<Object>> {private Staff staff;private InsertObserver insertObserver;public InsertAsyncTask(Staff staff, InsertObserver insertObserver) {this.staff = staff;this.insertObserver = insertObserver;}@Overrideprotected RepositoryItem doInBackground(Void... voids) {try {staffDAO.insert(staff);return new RepositoryItem(RepositoryItem.CODE_SUCCESS, null, null);} catch (Exception e) {e.printStackTrace();return new RepositoryItem(RepositoryItem.CODE_ERROR, e.getMessage(), null);}}@Overrideprotected void onPostExecute(RepositoryItem repositoryItem) {super.onPostExecute(repositoryItem);if (repositoryItem.getCode() == RepositoryItem.CODE_SUCCESS) {insertObserver.onSuccess();} else {insertObserver.onError(repositoryItem.getMsg());}}}private class DeleteAsyncTask extends AsyncTask<Void, Void, RepositoryItem<Object>> {private Staff staff;private DeleteObserver deleteObserver;public DeleteAsyncTask(Staff staff, DeleteObserver deleteObserver) {this.staff = staff;this.deleteObserver = deleteObserver;}@Overrideprotected RepositoryItem<Object> doInBackground(Void... voids) {try {staffDAO.delete(staff);return new RepositoryItem(RepositoryItem.CODE_SUCCESS, null, null);} catch (Exception e) {e.printStackTrace();return new RepositoryItem(RepositoryItem.CODE_ERROR, e.getMessage(), null);}}@Overrideprotected void onPostExecute(RepositoryItem<Object> repositoryItem) {super.onPostExecute(repositoryItem);if (repositoryItem.getCode() == RepositoryItem.CODE_SUCCESS) {deleteObserver.onSuccess();} else {deleteObserver.onError(repositoryItem.getMsg());}}}private class UpdateAsyncTask extends AsyncTask<Void, Void, RepositoryItem<Object>> {private Staff staff;private UpdateObserver updateObserver;public UpdateAsyncTask(Staff staff, UpdateObserver updateObserver) {this.staff = staff;this.updateObserver = updateObserver;}@Overrideprotected RepositoryItem<Object> doInBackground(Void... voids) {try {staffDAO.update(staff);return new RepositoryItem(RepositoryItem.CODE_SUCCESS, null, null);} catch (Exception e) {e.printStackTrace();return new RepositoryItem(RepositoryItem.CODE_ERROR, e.getMessage(), null);}}@Overrideprotected void onPostExecute(RepositoryItem<Object> repositoryItem) {super.onPostExecute(repositoryItem);if (repositoryItem.getCode() == RepositoryItem.CODE_SUCCESS) {updateObserver.onSuccess();} else {updateObserver.onError(repositoryItem.getMsg());}}}private class QueryAllAsyncTask extends AsyncTask<Void, Void, RepositoryItem<List<Staff>>> {private QueryAllObserver<Staff> queryAllObserver;public QueryAllAsyncTask(QueryAllObserver<Staff> queryAllObserver) {this.queryAllObserver = queryAllObserver;}@Overrideprotected RepositoryItem<List<Staff>> doInBackground(Void... voids) {try {List<Staff> staffs = staffDAO.queryAll();return new RepositoryItem(RepositoryItem.CODE_SUCCESS, null, staffs);} catch (Exception e) {e.printStackTrace();return new RepositoryItem(RepositoryItem.CODE_ERROR, e.getMessage(), null);}}@Overrideprotected void onPostExecute(RepositoryItem<List<Staff>> repositoryItem) {super.onPostExecute(repositoryItem);queryAllObserver.onResult(repositoryItem.getData());}}
}
7、Activity Layout
- activity_staff.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".StaffActivity"><Buttonandroid:id="@+id/btn_insert"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="增"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /><Buttonandroid:id="@+id/btn_delete"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="删"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toBottomOf="@+id/btn_insert" /><Buttonandroid:id="@+id/btn_update"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="改"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toBottomOf="@+id/btn_delete" /><Buttonandroid:id="@+id/btn_query_all"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="查"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toBottomOf="@+id/btn_update" />
</androidx.constraintlayout.widget.ConstraintLayout>
8、Activity Code
- StaffActivity.java
package com.my.room;import androidx.appcompat.app.AppCompatActivity;import android.os.Bundle;
import android.util.Log;
import android.widget.Button;import com.my.room.entity.Staff;
import com.my.room.repository.StaffRepository;
import com.my.room.repository.observer.DeleteObserver;
import com.my.room.repository.observer.InsertObserver;
import com.my.room.repository.observer.UpdateObserver;public class StaffActivity extends AppCompatActivity {public static final String TAG = StaffActivity.class.getSimpleName();private StaffRepository staffRepository;private Button btnInsert;private Button btnDelete;private Button btnUpdate;private Button btnQueryAll;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_staff);staffRepository = new StaffRepository();btnInsert = findViewById(R.id.btn_insert);btnDelete = findViewById(R.id.btn_delete);btnUpdate = findViewById(R.id.btn_update);btnQueryAll = findViewById(R.id.btn_query_all);btnInsert.setOnClickListener(v -> {Staff staff = new Staff();staff.name = "jack";staff.age = 20;staff.cardNum = "staff-001";staffRepository.insert(staff, new InsertObserver() {@Overridepublic void onSuccess() {Log.i(TAG, "------------------------------ insert success");}@Overridepublic void onError(String msg) {Log.i(TAG, "------------------------------ insert error - " + msg);}});});btnDelete.setOnClickListener(v -> {Staff staff = new Staff();staff.id = 1L;staffRepository.delete(staff, new DeleteObserver() {@Overridepublic void onSuccess() {Log.i(TAG, "------------------------------ delete success");}@Overridepublic void onError(String msg) {Log.i(TAG, "------------------------------ delete error - " + msg);}});});btnUpdate.setOnClickListener(v -> {Staff staff = new Staff();staff.id = 1L;staff.name = "jack";staff.age = 30;staff.cardNum = "staff-002";staffRepository.update(staff, new UpdateObserver() {@Overridepublic void onSuccess() {Log.i(TAG, "------------------------------ update success");}@Overridepublic void onError(String msg) {Log.i(TAG, "------------------------------ update error - " + msg);}});});btnQueryAll.setOnClickListener(v -> {staffRepository.queryAll(staffs -> {if (staffs == null) {Log.i(TAG, "------------------------------ queryAll - staffs is null");return;}if (staffs.size() == 0) {Log.i(TAG, "------------------------------ queryAll - staffs is empty");return;}for (Staff staff : staffs)Log.i(TAG, "------------------------------ queryAll - " + staff);});});}
}
Test
- 增 -> 查 -> 改 -> 查 -> 删 -> 查,输出结果
I/StaffActivity: ------------------------------ insert success
I/StaffActivity: ------------------------------ queryAll - Staff{id=1, name='jack', age=20, cardNum='staff-001'}
I/StaffActivity: ------------------------------ update success
I/StaffActivity: ------------------------------ queryAll - Staff{id=1, name='jack', age=30, cardNum='staff-002'}
I/StaffActivity: ------------------------------ delete success
I/StaffActivity: ------------------------------ queryAll - staffs is empty
三、Room 简单案例解析
1、Entity 解析
@Entity(tableName = "Staff")
public class Staff {@PrimaryKey(autoGenerate = true)public Long id;@ColumnInfo(name = "name")public String name;@ColumnInfo(name = "age")public Integer age;@ColumnInfo(name = "cardNum")public String cardNum;@Overridepublic String toString() {return "Staff{" +"id=" + id +", name='" + name + '\'' +", age=" + age +", cardNum='" + cardNum + '\'' +'}';}
}
@Entity(tableName = "Staff")
:指定 Staff 类为 Room 数据库中 Staff 表对应的实体类
-
@PrimaryKey(autoGenerate = true)
:指定 id 字段为主键,并且它的值是自动生成的 -
@ColumnInfo(name = "name")
:指定 name 字段映射到表中的 name 列,@ColumnInfo(name = "age")
和@ColumnInfo(name = "cardNum")
同理
- Staff 表会在 APP 首次安装时由 Room 根据 Staff 类创建,SQL 语句如下
CREATE TABLE IF NOT EXISTS Staff (id INTEGER PRIMARY KEY AUTOINCREMENT,name TEXT,age INTEGER,cardNum TEXT
);
2、DAO 解析
@Dao
public interface StaffDAO {@Insertvoid insert(Staff staff);@Deletevoid delete(Staff staff);@Updatevoid update(Staff staff);@Query("SELECT * FROM Staff")List<Staff> queryAll();
}
@Dao
:指定 StaffDAO 接口与 Room 数据库进行交互,以操作 Staff 类,该接口使用 Room 相关注解来定义操作方法
-
@Insert
:插入数据,这里传递一个 Staff 对象作为参数,Room 将这个对象插入到数据库中 -
@Delete
:删除数据,这里传递一个 Staff 对象作为参数,Room 基于该对象的 id 字段来删除相应的行 -
@Update
:修改数据,这里传递一个 Staff 对象作为参数,Room 基于该对象的 id 字段来修改相应的行 -
@Query("SELECT * FROM Staff")
:查询数据,查询表中的所有行
3、Database 解析
@Database(entities = {Staff.class}, version = 1, exportSchema = false)
public abstract class MyDatabase extends RoomDatabase {public static final String TAG = MyDatabase.class.getSimpleName();public static final String DATABASE_NAME = "test.db";private static MyDatabase myDatabase;public static MyDatabase getInstance() {if (myDatabase == null) {myDatabase = Room.databaseBuilder(MyApplication.getContext(), MyDatabase.class, DATABASE_NAME).build();}return myDatabase;}public abstract StaffDAO getStaffDAO();
}
(1)注解解析
@Database
:指定 MyDatabase 抽象类为 Room 数据库
(2)思路解析
-
单例模式确保整个应用程序中只有一个 MyDatabase 实例对象
-
定义获取 StaffDao 实例对象的抽象方法,Room 将自动生成这个方法的实现
4、Repository 解析
package com.my.room.repository;import android.os.AsyncTask;import com.my.room.dao.StaffDAO;
import com.my.room.database.MyDatabase;
import com.my.room.entity.Staff;
import com.my.room.repository.observer.DeleteObserver;
import com.my.room.repository.observer.InsertObserver;
import com.my.room.repository.observer.QueryAllObserver;
import com.my.room.repository.observer.UpdateObserver;import java.util.List;public class StaffRepository {private MyDatabase myDatabase;private StaffDAO staffDAO;public StaffRepository() {myDatabase = MyDatabase.getInstance();staffDAO = myDatabase.getStaffDAO();}// ====================================================================================================public void insert(Staff staff, InsertObserver insertObserver) {new InsertAsyncTask(staff, insertObserver).execute();}public void delete(Staff staff, DeleteObserver deleteObserver) {new DeleteAsyncTask(staff, deleteObserver).execute();}public void update(Staff staff, UpdateObserver updateObserver) {new UpdateAsyncTask(staff, updateObserver).execute();}public void queryAll(QueryAllObserver<Staff> queryAllObserver) {new QueryAllAsyncTask(queryAllObserver).execute();}// ====================================================================================================private class InsertAsyncTask extends AsyncTask<Void, Void, RepositoryItem<Object>> {private Staff staff;private InsertObserver insertObserver;public InsertAsyncTask(Staff staff, InsertObserver insertObserver) {this.staff = staff;this.insertObserver = insertObserver;}@Overrideprotected RepositoryItem doInBackground(Void... voids) {try {staffDAO.insert(staff);return new RepositoryItem(RepositoryItem.CODE_SUCCESS, null, null);} catch (Exception e) {e.printStackTrace();return new RepositoryItem(RepositoryItem.CODE_ERROR, e.getMessage(), null);}}@Overrideprotected void onPostExecute(RepositoryItem repositoryItem) {super.onPostExecute(repositoryItem);if (repositoryItem.getCode() == RepositoryItem.CODE_SUCCESS) {insertObserver.onSuccess();} else {insertObserver.onError(repositoryItem.getMsg());}}}private class DeleteAsyncTask extends AsyncTask<Void, Void, RepositoryItem<Object>> {private Staff staff;private DeleteObserver deleteObserver;public DeleteAsyncTask(Staff staff, DeleteObserver deleteObserver) {this.staff = staff;this.deleteObserver = deleteObserver;}@Overrideprotected RepositoryItem<Object> doInBackground(Void... voids) {try {staffDAO.delete(staff);return new RepositoryItem(RepositoryItem.CODE_SUCCESS, null, null);} catch (Exception e) {e.printStackTrace();return new RepositoryItem(RepositoryItem.CODE_ERROR, e.getMessage(), null);}}@Overrideprotected void onPostExecute(RepositoryItem<Object> repositoryItem) {super.onPostExecute(repositoryItem);if (repositoryItem.getCode() == RepositoryItem.CODE_SUCCESS) {deleteObserver.onSuccess();} else {deleteObserver.onError(repositoryItem.getMsg());}}}private class UpdateAsyncTask extends AsyncTask<Void, Void, RepositoryItem<Object>> {private Staff staff;private UpdateObserver updateObserver;public UpdateAsyncTask(Staff staff, UpdateObserver updateObserver) {this.staff = staff;this.updateObserver = updateObserver;}@Overrideprotected RepositoryItem<Object> doInBackground(Void... voids) {try {staffDAO.update(staff);return new RepositoryItem(RepositoryItem.CODE_SUCCESS, null, null);} catch (Exception e) {e.printStackTrace();return new RepositoryItem(RepositoryItem.CODE_ERROR, e.getMessage(), null);}}@Overrideprotected void onPostExecute(RepositoryItem<Object> repositoryItem) {super.onPostExecute(repositoryItem);if (repositoryItem.getCode() == RepositoryItem.CODE_SUCCESS) {updateObserver.onSuccess();} else {updateObserver.onError(repositoryItem.getMsg());}}}private class QueryAllAsyncTask extends AsyncTask<Void, Void, RepositoryItem<List<Staff>>> {private QueryAllObserver<Staff> queryAllObserver;public QueryAllAsyncTask(QueryAllObserver<Staff> queryAllObserver) {this.queryAllObserver = queryAllObserver;}@Overrideprotected RepositoryItem<List<Staff>> doInBackground(Void... voids) {try {List<Staff> staffs = staffDAO.queryAll();return new RepositoryItem(RepositoryItem.CODE_SUCCESS, null, staffs);} catch (Exception e) {e.printStackTrace();return new RepositoryItem(RepositoryItem.CODE_ERROR, e.getMessage(), null);}}@Overrideprotected void onPostExecute(RepositoryItem<List<Staff>> repositoryItem) {super.onPostExecute(repositoryItem);queryAllObserver.onResult(repositoryItem.getData());}}
}
- StaffRepository 类是对 StaffDAO 的封装,封装的主要目的是异步任务处理
(1)异步任务处理
- 对于每种 StaffDAO 实例对象的操作都定义了一个继承 AsyncTask 的内部类,它用于安排操作在后台线程中执行,然后使用观察者模式来通知调用者
-
InsertAsyncTask:负责在后台线程中执行插入操作,完成后(无论成功还是失败)通过 InsertObserver 通知调用者
-
DeleteAsyncTask:负责在后台线程中执行删除操作,完成后(无论成功还是失败)通过 DeleteObserver 通知调用者
-
UpdateAsyncTask:负责在后台线程中执行插入操作,完成后(无论成功还是失败)通过 UpdateObserver 通知调用者
-
QueryAllAsyncTask:负责在后台线程中执行查询操作,完成后(无论成功还是失败)通过 QueryAllObserver 通知调用者
(2)补充
-
线程调度:数据库操作不能在 UI 线程执行,操作在后台线程中(doInBackground 方法)执行完成后,然后回到 UI 线程中(onPostExecute 方法)执行后续操作
-
异常处理:在异步任务种,捕获可能抛出的异常,例如,执行插入操作时主键冲突会抛出异常