Jetpack Room數(shù)據(jù)庫

一、基礎(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);
}

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");
    }
};
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末苫幢,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子垫挨,更是在濱河造成了極大的恐慌韩肝,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件九榔,死亡現(xiàn)場離奇詭異哀峻,居然都是意外死亡涡相,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門剩蟀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來催蝗,“玉大人,你說我怎么就攤上這事育特”牛” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵缰冤,是天一觀的道長犬缨。 經(jīng)常有香客問我,道長锋谐,這世上最難降的妖魔是什么遍尺? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任截酷,我火速辦了婚禮涮拗,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘迂苛。我一直安慰自己三热,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布三幻。 她就那樣靜靜地躺著就漾,像睡著了一般。 火紅的嫁衣襯著肌膚如雪念搬。 梳的紋絲不亂的頭發(fā)上抑堡,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天,我揣著相機(jī)與錄音朗徊,去河邊找鬼首妖。 笑死,一個(gè)胖子當(dāng)著我的面吹牛爷恳,可吹牛的內(nèi)容都是我干的有缆。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼温亲,長吁一口氣:“原來是場噩夢啊……” “哼棚壁!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起栈虚,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤袖外,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后魂务,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體曼验,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡逆害,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蚣驼。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片魄幕。...
    茶點(diǎn)故事閱讀 38,039評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖颖杏,靈堂內(nèi)的尸體忽然破棺而出纯陨,到底是詐尸還是另有隱情,我是刑警寧澤留储,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布翼抠,位于F島的核電站,受9級特大地震影響获讳,放射性物質(zhì)發(fā)生泄漏阴颖。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一丐膝、第九天 我趴在偏房一處隱蔽的房頂上張望量愧。 院中可真熱鬧,春花似錦帅矗、人聲如沸偎肃。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽累颂。三九已至,卻和暖如春凛俱,著一層夾襖步出監(jiān)牢的瞬間紊馏,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工蒲犬, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留朱监,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓暖哨,卻偏偏與公主長得像赌朋,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子篇裁,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評論 2 345