Android架構元件Room的使用
Room其實就是一個orm,抽象了SQLite的使用,但是它作為Android的親兒子orm,並且原生支援LiveData和Rxjava巢狀使用,學習一下還是不錯的。
Room有3個主要元件
- Database :資料庫
- Entity : 代表資料庫一個表結構
- Dao : 包含訪問資料庫的方法
簡單使用
新增Google Maven倉庫
allprojects {
repositories {
jcenter()
google()
}
}
複製程式碼
新增依賴
dependencies {
// Room
implementation "android.arch.persistence.room:runtime:1.0.0"
annotationProcessor "android.arch.persistence.room:compiler:1.0.0"
}
複製程式碼
定義資料表實體類
班級表
@Entity(tableName = "tb_class")
public class ClassEntity {
@PrimaryKey
private long id;
}
複製程式碼
學生表
//指示資料表實體類
@Entity(tableName = "tb_student",//定義表名
indices = @Index(value = {"name", "sex"}, unique = true),//定義索引
foreignKeys = {@ForeignKey(entity = ClassEntity.class,
parentColumns = "id",
childColumns = "class_id")})//定義外來鍵
public class StudentEntity {
@PrimaryKey //定義主鍵
private long id;
@ColumnInfo(name = "name")//定義資料表中的欄位名
private String name;
@ColumnInfo(name = "sex")
private int sex;
@Ignore//指示Room需要忽略的欄位或方法
private String ignoreText;
@ColumnInfo(name = "class_id")
private String class_id;
//setter and getter
}
複製程式碼
Entity註解可選引數
public @interface Entity {
//定義表名
String tableName() default "";
//定義索引
Index[] indices() default {};
//設為true則父類的索引會自動被當前類繼承
boolean inheritSuperIndices() default false;
//定義主鍵
String[] primaryKeys() default {};
//定義外來鍵
ForeignKey[] foreignKeys() default {};
}
複製程式碼
Index索引註解可選引數
public @interface Index {
//定義需要新增索引的欄位
String[] value();
//定義索引的名稱
String name() default "";
//true-設定唯一鍵,標識value陣列中的索引欄位必須是唯一的,不可重複
boolean unique() default false;
}
複製程式碼
ForeignKey外來鍵註解可選引數
public @interface ForeignKey {
//引用外來鍵的表的實體
Class entity();
//要引用的外來鍵列
String[] parentColumns();
//要關聯的列
String[] childColumns();
//當父類實體(關聯的外來鍵表)從資料庫中刪除時執行的操作
@Action int onDelete() default NO_ACTION;
//當父類實體(關聯的外來鍵表)更新時執行的操作
@Action int onUpdate() default NO_ACTION;
//在事務完成之前,是否應該推遲外來鍵約束
boolean deferred() default false;
//給onDelete,onUpdate定義的操作
int NO_ACTION = 1;
int RESTRICT = 2;
int SET_NULL = 3;
int SET_DEFAULT = 4;
int CASCADE = 5;
@IntDef({NO_ACTION, RESTRICT, SET_NULL, SET_DEFAULT, CASCADE})
@interface Action {
}
}
複製程式碼
定義Dao類
@Dao
public interface StudentDao {
@Query("SELECT * FROM StudentEntity")
List<StudentEntity> getAll();
@Query("SELECT * FROM StudentEntity WHERE id IN (:ids)")
List<StudentEntity> getAllByIds(long[] ids);
@Insert
void insert(StudentEntity... entities);
@Delete
void delete(StudentEntity entity);
@Update
void update(StudentEntity entity);
}
複製程式碼
@insert, @Update都可以執行事務操作,定義在OnConflictStrategy註解類中
public @interface Insert {
//定義處理衝突的操作
@OnConflictStrategy
int onConflict() default OnConflictStrategy.ABORT;
}
複製程式碼
public @interface OnConflictStrategy {
//策略衝突就替換舊資料
int REPLACE = 1;
//策略衝突就回滾事務
int ROLLBACK = 2;
//策略衝突就退出事務
int ABORT = 3;
//策略衝突就使事務失敗
int FAIL = 4;
//忽略衝突
int IGNORE = 5;
}
複製程式碼
定義資料庫
@Database(entities = {StudentEntity.class}, version = 1)
public abstract class RoomDemoDatabase extends RoomDatabase {
public abstract StudentDao studentDao();
}
複製程式碼
生成資料庫例項
RoomDemoDatabase database = Room.databaseBuilder(getApplicationContext(),
RoomDemoDatabase.class, "database_name")
.build();
複製程式碼
生成資料庫例項的其他操作
Room.databaseBuilder(getApplicationContext(),
RoomDemoDatabase.class, "database_name")
.addCallback(new RoomDatabase.Callback() {
//第一次建立資料庫時呼叫,但是在建立所有表之後呼叫的
@Override
public void onCreate(@NonNull SupportSQLiteDatabase db) {
super.onCreate(db);
}
//當資料庫被開啟時呼叫
@Override
public void onOpen(@NonNull SupportSQLiteDatabase db) {
super.onOpen(db);
}
})
.allowMainThreadQueries()//允許在主執行緒查詢資料
.addMigrations()//遷移資料庫使用,下面會單獨拿出來講
.fallbackToDestructiveMigration()//遷移資料庫如果發生錯誤,將會重新建立資料庫,而不是發生崩潰
.build();
複製程式碼
資料庫遷移(升級)
Room.databaseBuilder(getApplicationContext(), MyDb.class, "database-name")
.addMigrations(MIGRATION_1_2, MIGRATION_2_3).build();
static final Migration MIGRATION_1_2 = new Migration(1, 2) {
@Override
public void migrate(SupportSQLiteDatabase database) {
database.execSQL("CREATE TABLE `Fruit` (`id` INTEGER, "
+ "`name` TEXT, PRIMARY KEY(`id`))");
}
};
static final Migration MIGRATION_2_3 = new Migration(2, 3) {
@Override
public void migrate(SupportSQLiteDatabase database) {
database.execSQL("ALTER TABLE Book "
+ " ADD COLUMN pub_year INTEGER");
}
};
複製程式碼
建立巢狀物件
有時,您希望將一個實體或普通的以前的Java物件(POJO)作為資料庫邏輯中的一個完整的整體來表示,即使該物件包含幾個欄位。在這些情況下,您可以使用@Embedded來表示一個物件,您希望將其分解為表中的子欄位。然後可以像對其他單個列一樣查詢嵌入式欄位
class Address {
public String street;
public String state;
public String city;
@ColumnInfo(name = "post_code")
public int postCode;
}
@Entity
class User {
@PrimaryKey
public int id;
public String firstName;
@Embedded
public Address address;
}
複製程式碼
這樣user表中的欄位就包含了id
, firstName
, street
, state
, city
, 和 post_code
注意
:嵌入式欄位還可以包含其他嵌入式欄位
如果一個實體具有相同型別的多個內嵌欄位,則可以通過設定字首屬性(prefix)使每個列保持惟一。然後將所提供的值新增到嵌入物件中每個列名的開頭
@Embedded(prefix = "foo_")
Coordinates coordinates;
複製程式碼
和LiveData
一起使用
新增依賴
// ReactiveStreams support for LiveData
implementation "android.arch.lifecycle:reactivestreams:1.0.0"
複製程式碼
修改返回型別
@Dao
public interface MyDao {
@Query("SELECT first_name, last_name FROM user WHERE region IN (:regions)")
public LiveData<List<User>> loadUsersFromRegionsSync(List<String> regions);
}
複製程式碼
和RxJava一起使用
新增依賴
// RxJava support for Room
implementation "android.arch.persistence.room:rxjava2:1.0.0"
複製程式碼
修改返回型別
@Dao
public interface MyDao {
@Query("SELECT * from user where id = :id LIMIT 1")
public Flowable<User> loadUserById(int id);
}
複製程式碼
直接遊標訪問
@Dao
public interface MyDao {
@Query("SELECT * FROM user WHERE age > :minAge LIMIT 5")
public Cursor loadRawUsersOlderThan(int minAge);
}
複製程式碼
型別轉換
定義轉換類,@TypeConverter註解定義轉換的方法
public class Converters {
@TypeConverter
public static Date fromTimestamp(Long value) {
return value == null ? null : new Date(value);
}
@TypeConverter
public static Long dateToTimestamp(Date date) {
return date == null ? null : date.getTime();
}
}
複製程式碼
@TypeConverters註解,告知資料庫要依賴哪些轉換類
@Database(entities = {User.class}, version = 1)
@TypeConverters({Converters.class})
public abstract class AppDatabase extends RoomDatabase {
public abstract UserDao userDao();
}
複製程式碼
使用這些轉換器,您可以在其他查詢中使用您的自定義型別,正如您將使用基本型別一樣,如下程式碼所示
@Entity
public class User {
...
private Date birthday;
}
複製程式碼
@Dao
public interface UserDao {
...
@Query("SELECT * FROM user WHERE birthday BETWEEN :from AND :to")
List<User> findUsersBornBetweenDates(Date from, Date to);
}
複製程式碼
輸出模式
在編譯時,將資料庫的模式資訊匯出到JSON檔案中,這樣可有利於我們更好的除錯和排錯
build.gradle
android {
...
defaultConfig {
...
javaCompileOptions {
annotationProcessorOptions {
arguments = ["room.schemaLocation":
"$projectDir/schemas".toString()]
}
}
}
}
複製程式碼
您應該將匯出的JSON檔案(表示資料庫的模式歷史記錄)儲存在您的版本控制系統中,因為它允許為測試目的建立您的資料庫的舊版本