一、基礎(chǔ)使用
1.1 Room三個(gè)主要操作類
- Entity 實(shí)體類 代表一個(gè)表中的字段
- Dao數(shù)據(jù)庫操作接口提供增刪改查
- 管理創(chuàng)建數(shù)據(jù)庫
1.2 引入依賴
https://developer.android.com/jetpack/androidx/releases/room
選擇性引入
dependencies {
def room_version = "2.2.5"
implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version" // For Kotlin use kapt instead of annotationProcessor
// optional - Kotlin Extensions and Coroutines support for Room
// implementation "androidx.room:room-ktx:$room_version"
// optional - RxJava support for Room
// implementation "androidx.room:room-rxjava2:$room_version"
// optional - Guava support for Room, including Optional and ListenableFuture
// implementation "androidx.room:room-guava:$room_version"
// Test helpers
testImplementation "androidx.room:room-testing:$room_version"
}
1.3 創(chuàng)建數(shù)據(jù)庫實(shí)體 Entity
@Entity(tableName = "User") //數(shù)據(jù)庫中表名
public class User {
//主鍵 自增
@PrimaryKey(autoGenerate = true)
private int id;
@ColumnInfo(name = "user_name", typeAffinity = ColumnInfo.TEXT) //實(shí)際數(shù)據(jù)庫中的字段user_name 數(shù)據(jù)類型
private String name;
@ColumnInfo(name = "user_gender")
private String gender;
private int age;
@Ignore //忽略字段
private int flag;
public User(int id, String name, String gender, int age) {
this.id = id;
this.name = name;
this.gender = gender;
this.age = age;
}
public User(String name, String gender, int age) {
this.name = name;
this.gender = gender;
this.age = age;
}
@Ignore
public User(String gender, int age) {
this.gender = gender;
this.age = age;
}
// 省略 get set ......
}
構(gòu)造方法中的形參代表查詢時(shí)需要查詢的字段儒老,如果有多個(gè)構(gòu)造方法 可以使用注解@Ignore忽略, 同樣
1.4 創(chuàng)建數(shù)據(jù)庫操作接口 Dao
@Dao //Database access object 數(shù)據(jù)庫訪問接口 所有增刪改查等操作都在此聲明
public interface UserDao {
// long Not sure how to handle insert method's return type.long
@Insert
void insertUser(User... users);
// int 影響的行數(shù)
@Update
int updateUser(User... users);
@Delete
void deleteUser(User... users);
@Query("DELETE FROM USER")
void deleteUser();
@Query("SELECT * FROM USER ORDER BY ID DESC")
List<User> getAllUser();
}
1.4.1 唯一約束+REPLACE實(shí)現(xiàn)有則更新無則插入
- 實(shí)體類中通過indices 將name字段設(shè)置成唯一約束
@Entity(tableName = "chatrow", indices = {@Index(value = {"name"}, unique = true)} ) //數(shù)據(jù)庫實(shí)體類
public class User {...}
- Dao中插入語句設(shè)置onConflict插入模式
public interface UserDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
void insertUser(User... users);
}
- 插入時(shí)如果表中有名字相同則更新本條數(shù)據(jù) 否則插入
- 更多語法參考官網(wǎng) https://developer.android.com/training/data-storage/room/accessing-data?hl=zh-cn
1.5 創(chuàng)建數(shù)據(jù)庫管理 Database
// entities ={多個(gè)表逗號隔開}
@Database(entities = {User.class}, version = 1, exportSchema = false)
public abstract class MyDatabase extends RoomDatabase {
public abstract UserDao getUserDao();
// 若entities有多個(gè)實(shí)例,則應(yīng)該寫多個(gè)Dao
}
1.6 使用
操作數(shù)據(jù)庫必須在子線程中挣棕,為了簡單此處直接使用allowMainThreadQueries()強(qiáng)制在主線程運(yùn)行,正常開發(fā)不允許
MyDatabase myDatabase = Room.databaseBuilder(this, MyDatabase.class, "user")
.allowMainThreadQueries()//強(qiáng)制在主線程運(yùn)行 不建議
.build();
UserDao userDao = myDatabase.getUserDao();
//增加
userDao.insertUser(new User("nikola", "男", 22));
//修改ID為3的
int row = userDao.updateUser(new User(3, "kolia", "女", 22));
//查詢所有
List<User> allUser = userDao.getAllUser();
二亲桥、使用單例注意事項(xiàng)
Google官方稱創(chuàng)建數(shù)據(jù)庫實(shí)例較耗時(shí)洛心,所以使用單例模式,但要注意: 無參構(gòu)造方法不能使用private修飾 否則Room自動生成實(shí)現(xiàn)類時(shí)會報(bào)錯(cuò)
// entities ={多個(gè)集合逗號隔開}
@Database(entities = {User.class}, version = 1, exportSchema = false)
public abstract class MyDatabase extends RoomDatabase {
private static final String DATABASE_NAME="my_db.db";
private static MyDatabase myDatabase;
public MyDatabase() {
}
synchronized public static MyDatabase getInstance(Context context) {
if (null == myDatabase) {
myDatabase = Room.databaseBuilder(context.getApplicationContext(), MyDatabase.class, DATABASE_NAME)
.allowMainThreadQueries() //強(qiáng)制在主線程運(yùn)行 不建議
.build();
}
return myDatabase;
}
public abstract UserDao getUserDao();
// 若entities有多個(gè)實(shí)例题篷,則應(yīng)該寫多個(gè)Dao
}
指定數(shù)據(jù)庫db文件保存路徑
synchronized public static MyDatabase getInstance(Context context) {
if (null == myDatabase) {
File priExternalDir = FileUtil.getPriExternalDir(context, Environment.DIRECTORY_DOWNLOADS);
final String dbPath = new File( priExternalDir.getAbsolutePath()+File.separator+DATABASE_NAME).getPath();
myDatabase = Room.databaseBuilder(context.getApplicationContext(), MyDatabase.class, dbPath)
.allowMainThreadQueries() //強(qiáng)制在主線程運(yùn)行 不建議
.build();
}
return myDatabase;
}
三词身、 LiveData 監(jiān)聽數(shù)據(jù)庫變化
3.1 返回值使用LiveData包裝
數(shù)據(jù)變化只在查詢時(shí)提現(xiàn),所以修改UserDao中查詢所有的返回值類型, 且LiveData內(nèi)部已經(jīng)實(shí)現(xiàn)再子線程中執(zhí)行悼凑,可以直接調(diào)用
@Query("SELECT * FROM USER ORDER BY ID DESC")
// List<User> getAllUser();
LiveData<List<User>> getAllUserLive();
3.2 設(shè)置監(jiān)聽
LiveData<List<User>> listLiveData = userDao.getAllUserLive();
listLiveData.observe(this, new Observer<List<User>>() {
@Override
public void onChanged(List<User> users) {
......
}
});
當(dāng)對數(shù)據(jù)庫進(jìn)行增刪改時(shí)會回調(diào)onChanged
四偿枕、數(shù)據(jù)庫版本遷移
- 變更Entity中的字段 要增加 Database中的version,
- 遷移數(shù)據(jù)庫有保留原有數(shù)據(jù)與 破壞式清空原有數(shù)據(jù)
4.1 破壞式
allowMainThreadQueries 不保留數(shù)據(jù)直接刪除原有數(shù)據(jù)庫
MyDatabase myDatabase = Room.databaseBuilder(this, MyDatabase.class, "user")
.allowMainThreadQueries()//強(qiáng)制在主線程運(yùn)行 不建議
.fallbackToDestructiveMigration() //破壞式: 當(dāng)數(shù)據(jù)庫版本變化時(shí)不做數(shù)據(jù)遷移
.build();
4.2 保留原有數(shù)據(jù)遷移
當(dāng)Entity字段改變時(shí)調(diào)用.addMigrations(Migration... migrations)户辫,指定從某個(gè)版本遷移到某個(gè)版本需要做的某項(xiàng)操作渐夸,具體操作定義在Migration中
MyDatabase myDatabase = Room.databaseBuilder(this, MyDatabase.class, "user")
.allowMainThreadQueries()//強(qiáng)制在主線程運(yùn)行 不建議
.addMigrations(migration) //保留原有數(shù)據(jù)
.build();
- 表中增加type字段時(shí)的addMigrations 實(shí)參 migration如下,需要自行寫增加字段語句
static final Migration migration = new Migration(2,3) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
database.execSQL("ALTER TABLE user ADD COLUMN type INTEGER NOT NULL DEFAULT 1");
}
};
- 刪除某個(gè)字段渔欢,sqlLet沒有刪除字段語句墓塌,只能創(chuàng)建新的數(shù)據(jù)庫定義需要的字段,將原有數(shù)據(jù)庫數(shù)據(jù)復(fù)制過去奥额,刪除舊數(shù)據(jù)庫后再將新數(shù)據(jù)庫重命名
new Migration(3,4) 表示從版本3升級到版本4
static final Migration migration = new Migration(3,4) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
database.execSQL("CREATE TABLE user_temp (id INTEGER PRIMARY KEY NOT NULL , user_name TEXT," +
"user_gender TEXT)");
database.execSQL("INSERT INTO user_temp (id, user_name, user_gender) " +
"SELECT id, user_name, user_gender FROM user");
database.execSQL("DROP TABLE user");
database.execSQL("ALTER TABLE user_temp RENAME to user");
}
};