Room 持續性資料庫為 SQLite 提供抽象層,可讓資料庫更順暢的存取,同時充分發揮 SQLite 的效用,由於 SQLite API 存在一些缺點,因此 Google 強烈建議使用 Room 來存取 SQLite 資料庫中的資料。

SQLite API 存在以下缺點,而 Room 改善了這些問題:

  • 無法在編譯時檢查語法錯誤,必需等到執行時才顯示錯誤訊息。
  • 必需使用大量樣板程式碼,才能將資料庫資料轉為資料物件。

專案設定

目前 Android 尚無內建 Room,必需在應用程式的 build.gradle 新增相關套件。

dependencies {
    def room_version = "2.4.3"
    implementation "androidx.room:room-runtime:$room_version"
    annotationProcessor "androidx.room:room-compiler:$room_version"
    testImplementation "androidx.room:room-testing:$room_version"
}

資料庫操作

Room 的操作主要包含資料實體、資料存取物件 (DAO) 及資料庫類別三個元件。

資料實體
表示資料庫中的資料表結構及資料實體。
資料存取物件 (DAO)
提供各種操作資料庫中資料的方法,通常包含新增、更新、刪除及讀取。
資料庫類別
保存資料庫,並作為資料庫連線的主要存取點。

資料實體

@Entity(tableName = "user")
public class User {
    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "uid")
    public int uid;

    @ColumnInfo(name = "name")
    public String name;

    @ColumnInfo(name = "account")
    public String account;
}

資料存取物件 (DAO)

@Dao
public interface UserDao {
    @Query("SELECT * FROM user")
    List<User> getAll();

    @Query("SELECT * FROM user WHERE uid IN (:userIds)")
    List<User> loadAllByIds(int[] userIds);

    @Query("SELECT * FROM user WHERE uid = :uid LIMIT 1")
    User getByUid(int uid);

    @Insert(onConflict = REPLACE)
    void insertAll(User... users);

    @Update(onConflict = REPLACE)
    void updateAll(User... users);

    @Delete
    void delete(User user);
}

資料庫類別

@Database(entities = {User.class}, version = 1, exportSchema = false)
public abstract class AppDatabase extends RoomDatabase {
    private static final int NUMBER_OF_THREADS = 4;
    public static final ExecutorService executor = Executors.newFixedThreadPool(NUMBER_OF_THREADS);

    private static volatile AppDatabase INSTANCE;
    public static AppDatabase getDatabase(final Context context) {
        if (INSTANCE == null) {
            synchronized (AppDatabase.class) {
                if (INSTANCE == null) {
                    INSTANCE = Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class, "databaseName").build();
                }
            }
        }
        return INSTANCE;
    }

    public abstract UserDao getUserDao();
}
  • 行 3 ~ 4:使用 Room 時,無法在主執行緒操作資料庫,這邊先建立一個 ExecutorService,之後操作資料庫時可以直接拿來用。
  • 行 6 ~ 16:使用單例模式建立資料庫實體。

新增資料

// 建立資料
AppDatabase database = AppDatabase.getDatabase(this);
UserDao userDao = database.getUserDao();
User user = new User();
user.name = "測試姓名";
user.account = "ID0000001";

// 多執行緒寫入資料
AppDatabase.executor.execute(() -> {
    userDao.insertAll(user);
});

讀取資料

AppDatabase database = AppDatabase.getDatabase(this);
UserDao userDao = database.getUserDao();
AppDatabase.executor.execute(() -> {
    List<User> list = userDao.getAll();
});

刪除資料

AppDatabase database = AppDatabase.getDatabase(this);
UserDao userDao = database.getUserDao();
AppDatabase.executor.execute(() -> {
    User user = userDao.getByUid(1);
    userDao.delete(user);
});
  • 行 4 ~ 5:必需先讀取資料再刪除。

更新資料

AppDatabase database = AppDatabase.getDatabase(this);
UserDao userDao = database.getUserDao();
AppDatabase.executor.execute(() -> {
    User user = userDao.getByUid(1);
    user.name = "測試姓名1";
    userDao.updateAll(user);
});
  • 行 4 ~ 6:必需先讀取資料,修改後再更新。