Room Database完全使用手冊

Hanking發表於2019-01-18

前言

Android資料持久層直接使用SQLite很麻煩,Google官方推出了Room, Google對Room的定義: The Room persistence library provides an abstraction layer over SQLite to allow fluent database access while harnessing the full power of SQLite. Room是SQLite之上的一個抽象層,通過Room層來擺脫,繁瑣的資料庫操作,包括建立、更新資料庫、表以及進行增刪查改等。

Room資料庫結構

如下圖,Room資料庫包含三個物件:

1、Entity : 對應資料庫中的表,可以使用Entity註解將一個類變成資料庫中的一張表結構。

2、DAO : 全稱Database Access

Object,定義了對資料庫中資料的讀寫等操作,DAO中可以使用SQL語句來運算元據庫。

3、RoomDatabase : 資料庫持有類,用於建立資料庫或者連線到資料庫。內部包含DAO和Entity。

在這裡插入圖片描述
上面總體說了Room資料庫的三個物件,下面分別介紹三個物件的作用和使用方法。

Entity

Room Database中的Entity表示一張資料表結構,一個Entity例項就是表中的一行,如定義一個Person類的Entity。

@Entity
public class Person{
    @PrimaryKey
    public int id;
    public String firstName;
    public String lastName;
}
複製程式碼

當有兩個Person物件在表中時,表中的資料如下:

在這裡插入圖片描述
其中Person和Entity及資料庫的關係如下所示:
在這裡插入圖片描述

重點:

1、一個Entity物件代表資料表中的一行,一個Entity類代表一張資料表。

2、Entity中的成員變數都是資料表中的列。

3、一個Java類定義成Entity只要加上Entity註解就可以了。

Entity的使用

1、定義一個Entity

@Entity(tableName = "person_table")
public class Person{
    @PrimaryKey
    public int id;

    public String firstName;
    public String lastName;
}
複製程式碼

如果想給Entity命名可以在@Entity註解的後面加上(tableName = "person_table"),如果不加會使用預設命名,上面的id,firstName, lastName都定義成了public型別,是為了資料庫能夠有操作這些資料的許可權,也可以選擇將這些都設定成private型別,然後通過setter和getter來操作。

2、定義主鍵

每一個Entity至少定義一個主鍵(primary key),哪怕Entity中只有一個變數也要將這個變數定義為主鍵,在Room資料庫中使用註解 @PrimaryKey 來定義主鍵,@PrimaryKey 的使用方式有兩種一種是在類變數前面加,如果主鍵比較複雜可以加在@Entity註解的後面。

@Entity(primaryKeys = {"firstName", "lastName"})
public class User {
    public String firstName;
    public String lastName;
}
複製程式碼

3、改變屬性列名

和改變表名稱tableName一樣,可以改變表中的列名稱,使用 @ColumnInfo來改變列的名稱。如果不改的話預設使用變數名的小寫形式。

@Entity(tableName = "person_table")
public class Person {
    @PrimaryKey
    public int id;

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

    @ColumnInfo(name = "last_name")
    public String lastName;
}
複製程式碼

4、使某些變數不生成資料表中的欄位

由上面可知當一個類前加了Entity註解後類中的所有成員變數都會生成表中的屬性列,如果我們不希望某個變數生成表中的屬性列,可以使用註解 @Ignore。

@Entity
public class Person {
    @PrimaryKey
    public int id;
    public String firstName;
    public String lastName;

    @Ignore
    Bitmap picture;
}
複製程式碼

如果有類繼承上面的Person類,並且不想picture生成資料表中的列,那麼可以ignoredColumns

@Entity(ignoredColumns = "picture")
public class RemotePerson extends Person {
    @PrimaryKey
    public int id;

    public boolean hasVpn;
}
複製程式碼

5、兩個資料表關聯

SQLite是關聯式資料庫,可以明確兩個物件間的關係,大多數的關聯式資料庫都可以指定表之間的關聯,但是Room資料庫不能夠之間指定兩個表之間的關係,但是可以通過@ForeignKey註解來進行表的關聯。

@Entity(foreignKeys = @ForeignKey(entity = Person.class,
                                  parentColumns = "id",
                                  childColumns = "user_id"))
public class Book {
    @PrimaryKey
    public int bookId;

    public String title;

    @ColumnInfo(name = "user_id")
    public int userId;
}
複製程式碼

6、巢狀Entity

如果定義的Entity類裡面有個物件,並且希望定義的Entity中的表列欄位包含Entity類物件中的變數,可以在Entity類物件中加@Embedded標註。

public class Address {
    public String street;
    public String state;
    public String city;

    @ColumnInfo(name = "post_code")
    public int postCode;
}

@Entity
public class Person{
    @PrimaryKey
    public int id;

    public String firstName;

    @Embedded
    public Address address;
}
複製程式碼

這樣Person表中的列欄位有id, firstName, street, state, city,和 post_code.

DAO

在DAO(data access object)中,可以使用SQL語句進行對資料庫的操作並且將這些語句與Java中方法關聯呼叫,編譯器會檢查SQL語句並且通過註解生成對應的查詢語句,例如@Insert。

注意:

1、DAO必現是抽象類或者介面

2、所有的查詢語句必須在單獨的執行緒裡面執行。

在這裡插入圖片描述

DAO的使用

可以使用sql中的語句來實現DAO的操作。

1、插入資料

當在方法前使用@Insert註解時,Room資料庫會將方法中的所有引數都插入到資料表中。

@Dao
public interface MyDao {
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    public void insertUsers(User... users);

    @Insert
    public void insertBothUsers(User user1, User user2);

    @Insert
    public void insertUsersAndFriends(User user, List<User> friends);
}
複製程式碼

當@Insert 方法只插入一個引數時,插入會返回long資料代表插入的物件在資料表中的行號,當插入一個陣列或者集合形式的資料時,會返回long陣列或者集合。 2、更新資料

@Update標註的方法會查詢表中與所給資料主鍵一樣的資料進行更新。

@Dao
public interface MyDao {
    @Update
    public void updateUsers(User... users);
}
複製程式碼

3、查詢資料

@Query 標註的查詢方法是使用最多的方法,每個查詢方法在編譯期間就會被驗證是否正確,如果錯誤就會報錯,不會在執行時再提示失敗。在

@Dao
public interface MyDao {
    @Query("SELECT * FROM user")
    public User[] loadAllUsers();
}
複製程式碼

如果存在查詢語法錯誤,或者資料庫中不存在user表,Room資料庫會在編譯時提示錯誤資訊。 也可以使用SQL語句實現複雜的查詢

@Dao
public interface MyDao {
    @Query("SELECT * FROM user WHERE age BETWEEN :minAge AND :maxAge")
    public User[] loadAllUsersBetweenAges(int minAge, int maxAge);

    @Query("SELECT * FROM user WHERE first_name LIKE :search " +
           "OR last_name LIKE :search")
    public List<User> findUserWithName(String search);
}
複製程式碼

4、查詢列表中的資料並返回子列表

有時候並不需要將查詢資料的所有欄位都返回,大部分時候只需要返回查詢資料的一個子域。下面實現返回查詢的一個子域。

public class NameTuple {
    @ColumnInfo(name = "first_name")
    public String firstName;

    @ColumnInfo(name = "last_name")
    public String lastName;
}
複製程式碼

然後查詢返回子域中的欄位

@Dao
public interface MyDao {
    @Query("SELECT first_name, last_name FROM user")
    public List<NameTuple> loadFullName();
}
複製程式碼

RoomDatabase

上面已經介紹了DAO和Entity,Entity定義表,DAO定義對錶中資料進行操作,RoomDatabase包含了DAO,並且提供建立和連線資料庫的方法。

在這裡插入圖片描述

建立Room database

建立Room database包括三個步驟: 1、建立繼承RoomDatabase的抽象類。

2、在繼承的類前使用註解@Database。

3、申明資料庫結構的Entity,並且設定資料庫的版本號。

@Database(entities = {Word.class}, version = 1)//申明Entity和資料庫的版本號
public abstract class WordRoomDatabase 
                      extends RoomDatabase { 
   public abstract WordDao wordDao();//建立DAO的抽象類

   private static WordRoomDatabase INSTANCE;//建立單例
   
   static WordRoomDatabase getDatabase(final Context context) {
 		 if (INSTANCE == null) {
    			synchronized (WordRoomDatabase.class) {
      			if (INSTANCE == null)    {
       				    INSTANCE = Room.databaseBuilder(
						context.getApplicationContext(),
						WordRoomDatabase.class, "word_database")
						.addCallback(sOnOpenCallback)
						.fallbackToDestructiveMigration()
						.build();                
      			}
      		}
      }
  return INSTANCE;}

private static RoomDatabase.Callback sOnOpenCallback =
  new RoomDatabase.Callback(){
	@Override
	public void onOpen (@NonNull SupportSQLiteDatabase db){
		super.onOpen(db);
		initializeData();
	}};

}
複製程式碼

注意: 1、編譯時會檢查SQL語句是否正確

2、不要在主執行緒中進行資料庫操作

3、RoomDatabase最好使用單例模式

最後上一張Android在使用Room資料庫時和Room資料庫互動的圖

在這裡插入圖片描述
application從RoomDatabase中獲取DAO例項,並且通過DAO中定義的方法來運算元據庫中Entity。獲取設定entity的資料。

Room database Migration

當升級Android應用的時候,有時需要更改資料庫中資料的結構,要使用者升級應用的時候保持原有的資料不變,使用資料庫移植Migration是必須的。在Room資料庫中Room persistence library庫提供了升級資料庫時儲存使用者資料的方法。 使用方法 定義Migration物件,每個物件包含資料庫的開始版本號和結束版本號。

    static final Migration MIGRATION_1_2 = new Migration(1, 2) {
        @Override
        public void migrate(SupportSQLiteDatabase database) {
            //這裡面實現更改資料表結構的方法
        }
    };
複製程式碼

如上實現一個Migration物件,重寫migrate方法,在migrate方法中實現升級時要進行的操作。

    static final Migration MIGRATION_2_3 = new Migration(2, 3) {
        @Override
        public void migrate(SupportSQLiteDatabase database) {
            database.execSQL("ALTER TABLE users "
                    + " ADD COLUMN last_update INTEGER");
        }
    };
複製程式碼

如果我們資料庫要從版本1,升級到版本庫,就要定義MIGRATION_1_2 ,MIGRATION_2_3 ,MIGRATION_3_4 ,MIGRATION_1_4 並且改寫單例方法。

    public static UsersDatabase getInstance(Context context) {
        synchronized (sLock) {
            if (INSTANCE == null) {
                INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
                        UsersDatabase.class, "Sample.db")
                        .addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4, MIGRATION_1_4)
                        .build();
            }
            return INSTANCE;
        }
    }
複製程式碼

當然不一定非要定義MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4, MIGRATION_1_4這麼多,也可以一次從MIGRATION_1_4,跳過中間的MIGRATION,但是如果不提供足夠的MIGRATION,從當前版本變到最新版本,Room資料庫會清楚原來的資料重寫建立。

參考文獻

1、developer.android.com/training/da…

2、codelabs.developers.google.com/codelabs/an…

我的csdn

blog.csdn.net/u013309870/…

相關文章