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:必需先讀取資料,修改後再更新。