Room Persistence Library(官方介紹)
官方ORM(Object Relational Mapping)框架專題
Google官方推出的Android架構(gòu)組件系列文章(六)Room持久化庫
Room 的官方API 可以查看這里
介紹
Room是谷歌官方的數(shù)據(jù)庫ORM(對象關(guān)系映射)框架服傍,使用起來非常方便。
Room提供了一個(gè)SQLite之上的抽象層,使得在充分利用SQLite功能的前提下順暢的訪問數(shù)據(jù)庫骇径。
對于需要處理大量結(jié)構(gòu)化數(shù)據(jù)的App來說假残,把這些數(shù)據(jù)做本地持久化會帶來很大的好處缭贡。常見的用例是緩存重要數(shù)據(jù)塊。這樣當(dāng)設(shè)備無法連網(wǎng)的時(shí)候辉懒,用戶仍然可以瀏覽內(nèi)容阳惹。而用戶對內(nèi)容做出的任何改動(dòng)都在網(wǎng)絡(luò)恢復(fù)的時(shí)候同步到服務(wù)端。
引入
1眶俩、項(xiàng)目build.gradle中添加如下代碼倉庫
allprojects {
repositories {
jcenter()
google()
}
}
2莹汤、app Module中引入
// Room依賴
implementation 'android.arch.persistence.room:runtime:1.1.0'
annotationProcessor "android.arch.persistence.room:compiler:1.1.0"
組成
Room中有三個(gè)主要的組件:
1、Database
數(shù)據(jù)庫組件颠印,底層連接的主要入口纲岭,主要作用:
- 創(chuàng)建database holder
- 使用注解定義實(shí)體類
- 實(shí)體類定義了從數(shù)據(jù)庫中獲取數(shù)據(jù)的對象(DAO)
這個(gè)被注解的類是一個(gè)繼承RoomDatabase的抽象類。在運(yùn)行時(shí)线罕,可以通過調(diào)用Room.databaseBuilder() 或者 Room.inMemoryDatabaseBuilder()來得到它的實(shí)例止潮。
2、Entity
實(shí)體類組件钞楼, 一個(gè)類表示數(shù)據(jù)庫的一個(gè)表喇闸。
注意
- 1、你必須在Database類中的entities數(shù)組中引用這些entity類
- 2询件、entity中的每一個(gè)field都將被持久化到數(shù)據(jù)庫仅偎,除非使用了@Ignore注解。
3雳殊、DAO
DAO查詢組件橘沥,DAO(Data Access Object) 數(shù)據(jù)訪問對象是一個(gè)面向?qū)ο蟮臄?shù)據(jù)庫接口。
DAO是Room的主要組件夯秃,負(fù)責(zé)定義查詢(添加或者刪除等)數(shù)據(jù)庫的方法座咆。
4、示意圖
示例代碼
1仓洼、User.java ---- 實(shí)體類組件(Entity)
-
建表:當(dāng)一個(gè)類用@Entity注解并且被@Database注解中的entities屬性所引用時(shí)(
@Database(entities = {User.class}, version = 1)
)介陶,Room就會在數(shù)據(jù)庫中為那個(gè)entity創(chuàng)建一張表。 - 表名:Room默認(rèn)把類名作為數(shù)據(jù)庫的表名色建,自定義表名需要使用@Entity注解的tableName屬性哺呜,@Entity(tableName = "users")。
- 建列:默認(rèn)Room會為實(shí)體類中定義的每一個(gè)字段(field)都創(chuàng)建一個(gè)數(shù)據(jù)表列(column)箕戳。
- 列名:默認(rèn)使用字段名作為列名某残,如果想指定列名国撵,可以使用 @ColumnInfo(name = "your_name")。
- 持久化:要持久化一個(gè)字段(字段數(shù)據(jù)寫入數(shù)據(jù)庫)玻墅,Room必須有獲取它的渠道介牙。你可以把字段寫成public,也可以為它提供一個(gè)setter和getter澳厢。如果你使用setter和getter的方式环础,記住它們要基于Room的Java Bean規(guī)范。
- 忽略:如果一個(gè)實(shí)體類中有你不想持久化的字段剩拢,那么你可以使用@Ignore來注釋它們线得。
- 主鍵:每個(gè)實(shí)體類Entity都必須至少定義一個(gè)field作為主鍵(primary key),主鍵自增需要使用@PrimaryKey的autoGenerate屬性徐伐。
- 組合主鍵:需要使用@Entity注解的primaryKeys屬性贯钩,比如@Entity(primaryKeys = {"firstName", "lastName"}),多個(gè)字段聯(lián)合形成一個(gè)主鍵組合呵晨,保證主鍵的唯一性
@Entity(tableName = "users")
public class User {
@PrimaryKey(autoGenerate = true)
private int uid;
@ColumnInfo(name = "first_name")
private String firstName;
@ColumnInfo(name = "last_name")
private String lastName;
@Ignore
Bitmap picture;
}
2魏保、UserDao.java ---- 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 first_name LIKE :first AND "
+ "last_name LIKE :last LIMIT 1")
User findByName(String first, String last);
@Insert
void insertAll(User... users);
@Delete
void delete(User user);
}
3、AppDatabase.java ---- 數(shù)據(jù)庫組件
@Database(entities = {User.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
public abstract UserDao userDao();
}
4摸屠、獲取database實(shí)例
獲取database實(shí)例的時(shí)候應(yīng)該保持單例模式谓罗,因?yàn)閿?shù)據(jù)庫的實(shí)例對內(nèi)存的開銷是比較大的,而且程序內(nèi)一般也不需要多個(gè)database的實(shí)例季二。
AppDatabase db = Room.databaseBuilder(getApplicationContext(),AppDatabase.class, "database-name").build();
相關(guān)概念
1檩咱、索引(Indices )
為了提高查詢的效率,可能給特定的字段建立索引胯舷。
要為一個(gè)entity添加索引刻蚯,在@Entity注解中添加indices屬性,列出你想放在索引或者組合索引中的字段桑嘶。
代碼示例:
@Entity(indices = {@Index("name"), @Index("last_name", "address")})
class User {
@PrimaryKey
public int id;
public String firstName;
public String address;
@ColumnInfo(name = "last_name")
public String lastName;
@Ignore
Bitmap picture;
}
2炊汹、唯一性(uniqueness)
指定某個(gè)字段或者幾個(gè)字段的值必須是唯一的,比如用戶名或手機(jī)號之類的賬戶唯一標(biāo)識字段逃顶。
可以通過把@Index注解的unique屬性設(shè)置為true來實(shí)現(xiàn)唯一性
@Entity(indices = {@Index(value = {"first_name", "last_name"},
unique = true)})
class User {
@PrimaryKey
public int id;
@ColumnInfo(name = "first_name")
public String firstName;
@ColumnInfo(name = "last_name")
public String lastName;
@Ignore
Bitmap picture;
}
3讨便、外鍵約束(Foreign Key)
一個(gè)表中的外鍵(Foreign Key) 指向另一個(gè)表中的主鍵(Primary Key),在更新和刪除時(shí)起到約束的作用以政。比如霸褒,如果你想在刪除主鍵表中的一條數(shù)據(jù)時(shí)可以同時(shí)刪除外鍵約束表中相對應(yīng)的數(shù)據(jù),你可以在@ForeignKey注解中加上onDelete=CASCADE盈蛮。
下面的代碼就指定了Book表中的user_id字段為User表的外鍵废菱,與User表的id字段一一對應(yīng),使用Entity的foreignKeys屬性指定,寫法如下:
@Entity(foreignKeys = @ForeignKey(entity = User.class,
parentColumns = "id",
childColumns = "user_id"))
class Book {
@PrimaryKey
public int bookId;
public String title;
@ColumnInfo(name = "user_id")
public int userId;
}
-
注意
替換沖突『 @Insert(onConfilict=REPLACE) 』不適用于外鍵約束殊轴,onConfilict不是單獨(dú)的sql命令衰倦,可以理解為一組REMOVE和REPLACE的操作,請參見SQLite文檔的ON_CONFLICT語句梳凛,onConfilict有如下五種沖突解決算法耿币。
4梳杏、對象嵌套
就是一個(gè)實(shí)體類中嵌入另一個(gè)實(shí)體類韧拒,可以多層嵌套。比如你在User中嵌套Address十性,如果你使用@Embedded注解Address的話叛溢,那么User表中就擁有了Address的所有字段了。
為了防止多個(gè)實(shí)體嵌套造成字段重復(fù)劲适,你可以通過設(shè)置prefix屬性來保持每列的唯一性楷掉。Room會將提供的值添加到嵌入對象的每個(gè)列名的開頭。
@Entity
class User {
@PrimaryKey
public int id;
public String firstName;
@Embedded
public Address address;
}
class Address {
public String street;
public String state;
public String city;
@ColumnInfo(name = "post_code")
public int postCode;
}
數(shù)據(jù)查詢(DAO)
DAO抽象出了一種操作數(shù)據(jù)庫的簡便方法霞势。下面介紹一下常見的查詢方式烹植。
1、新增愕贡、插入(Insert)
創(chuàng)建一個(gè)DAO方法并使用@Insert注解草雕,Room就會在工作線程中將所有參數(shù)插入到數(shù)據(jù)庫。
如果@Insert方法僅僅接收一個(gè)參數(shù)固以,那它可以返回一個(gè)long墩虹,表示插入項(xiàng)的rowId。如果參數(shù)是一個(gè)數(shù)組或集合憨琳,它會返回long []或List<Long>诫钓。
@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);
}
2、修改篙螟、更新(Update)
根據(jù)每個(gè)entity的主鍵作為更新的依據(jù)菌湃,此方法可以返回一個(gè)int值,指示數(shù)據(jù)庫中更新的行數(shù)遍略。
@Dao
public interface MyDao {
@Update
public void updateUsers(User... users);
}
3惧所、刪除(Delete)
使用主鍵找到要?jiǎng)h除的entity,此方法可以返回一個(gè)int值墅冷,指示數(shù)據(jù)庫中被刪除的行數(shù)纯路。
@Dao
public interface MyDao {
@Delete
public void deleteUsers(User... users);
}
4、查詢(Query)
@Query(查詢)是DAO
類中使用的主要注解寞忿〕刍#可以讓你執(zhí)行數(shù)據(jù)庫讀/寫操作。每個(gè)@Query
方法都會在編譯時(shí)驗(yàn)證,如果查詢語句有問題叫编,會發(fā)生編譯錯(cuò)誤而不是運(yùn)行時(shí)故障辖佣。
Room還會檢查查詢的返回值,如果返回的對象字段名和查詢結(jié)果的相應(yīng)字段名不匹配搓逾,Room將以下面兩種方式提醒你:
- 如果是某些字段名不匹配會給出警告卷谈。
- 如果沒有匹配的字段名則會給出錯(cuò)誤提示。
4-1霞篡、簡單查詢
@Dao
public interface MyDao {
@Query("SELECT * FROM user")
public User[] loadAllUsers();
}
4-2世蔗、條件查詢 (傳參)
注意查詢方法的入?yún)⒃诓樵冋Z句中的寫法“:minAge”,另外朗兵,在使用in操作符進(jìn)行查詢時(shí)污淋,別忘記加上“()”哦!
@Dao
public interface MyDao {
//查詢User表中年齡大于入?yún)⒌臄?shù)據(jù)集合
@Query("SELECT * FROM user WHERE age > :minAge")
public User[] loadAllUsersOlderThan(int minAge);
//查詢User表中年齡介于入?yún)⒅g的數(shù)據(jù)集合
@Query("SELECT * FROM user WHERE age BETWEEN :minAge AND :maxAge")
public User[] loadAllUsersBetweenAges(int minAge, int maxAge);
//查詢User表中名字中入?yún)㈥P(guān)鍵字的數(shù)據(jù)集合
@Query("SELECT * FROM user WHERE first_name LIKE :search "
+ "OR last_name LIKE :search")
public List<User> findUserWithName(String search);
//查詢User表中id符合入?yún)⒓现械臄?shù)據(jù)集合
@Query("SELECT * FROM user WHERE uid IN (:userIds)")
List<User> loadAllByIds(int[] userIds);
}
4-3余掖、部分查詢
有時(shí)候我們僅僅需要的是數(shù)據(jù)表中的部分?jǐn)?shù)據(jù)寸爆,這個(gè)時(shí)候我們可以指定DAO的查詢方法只返回我們需要的字段,這樣不僅節(jié)約資源而且查詢更快盐欺。
方法是在查詢語句中指定需要獲取的字段赁豆,然后指定對應(yīng)的實(shí)體類來獲取查詢的返回?cái)?shù)據(jù)。
例如冗美,User的實(shí)際字段有如下四個(gè)魔种,但是我們只需要其中的first_name和last_name,那么我們可以重新定義一個(gè)實(shí)體類UserName墩衙,然后在查詢方法中指定只查詢first_name和last_name字段务嫡,并使用UserName實(shí)體來獲取查詢語句的返回?cái)?shù)據(jù)。
注:這些“裁剪”的實(shí)體類也是可以使用@Embedded注解的漆改。
@Entity(tableName = "users")
public class User {
@PrimaryKey(autoGenerate = true)
private int uid;
@ColumnInfo(name = "first_name")
private String firstName;
@ColumnInfo(name = "last_name")
private String lastName;
@Ignore
Bitmap picture;
}
public class UserName{
@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();
}
4-4心铃、原始查詢 RawQuery
我們可以利用 RawQuery
進(jìn)行原始SQL語句查詢,示例代碼:
@Dao
interface RawDao {
@RawQuery
User getUserViaQuery(SupportSQLiteQuery query);
}
SimpleSQLiteQuery query = new SimpleSQLiteQuery("SELECT * FROM User WHERE id = ? LIMIT 1", new Object[]{userId});
User user2 = rawDao.getUserViaQuery(query);
1挫剑、Room將根據(jù)函數(shù)的返回類型("User ")生成代碼去扣,如果未能通過正確的查詢,將導(dǎo)致運(yùn)行時(shí)異常樊破。
2愉棱、RawQuery方法只能用于讀取查詢。對于寫入查詢哲戚,請使用RoomDatabase.getOpenHelper().getWritableDatabase()
5奔滑、可觀察的查詢(Observable queries)
5-1、Query
當(dāng)執(zhí)行查詢的時(shí)候顺少,你通常希望app的UI能自動(dòng)在數(shù)據(jù)更新的時(shí)候更新朋其。為此王浴,在query方法中使用 LiveData 類型的返回值。當(dāng)數(shù)據(jù)庫變化的時(shí)候梅猿,Room會生成所有的必要代碼來更新LiveData氓辣。
@Dao
public interface MyDao {
@Query("SELECT * FROM user WHERE age BETWEEN :minAge AND :maxAge")
public LiveData<List<User>> loadAllUsersBetweenAges(int minAge, int maxAge);
}
使用步驟:
- 創(chuàng)建一個(gè)LiveData實(shí)例來保存某種類型的數(shù)據(jù)。 這通常在您的ViewModel類中完成袱蚓。
public class UserViewModel extends ViewModel {
private LiveData<List<User>> mUsers;
public LiveData<List<User>> getUsers() {
if (mUsers== null) {
mUsers= MyDao.loadAllUsersBetweenAges(10,30);
}
return mUsers;
}
...
}
- 創(chuàng)建一個(gè)Observer對象钞啸,該對象定義onChanged()方法,該方法在LiveData對象的數(shù)據(jù)發(fā)生變化時(shí)回調(diào)喇潘, 通常是在UI控制器中創(chuàng)建Observer對象辟躏,比如Activity或Fragment鬼贱。
public class UserActivity extends AppCompatActivity {
private UserViewModel mModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mModel = ViewModelProviders.of(this).get(UserViewModel.class);
final Observer<List<User>> userObserver = new Observer<List<User>>() {
@Override
public void onChanged(@Nullable List<User> users) {
//update the ui
}
};
mModel.getCurrentName().observe(this, userObserver);
}
}
- 使用observe()方法將Observer對象附加到LiveData對象简十。 observe()方法使用LifecycleOwner對象鄙早。 這將Observer對象訂閱到LiveData對象哨毁,以便通知其更改枫甲。 您通常將Observer對象附加到UI控制器中,比如Activity或Fragment扼褪。
5-2想幻、RawQuery
RawQuery方法可以返回可觀察的類型,但您需要使用注釋中的observedEntities()字段指定在查詢中訪問哪些表话浇。
代碼示例:
@Dao
interface RawDao {
@RawQuery(observedEntities = User.class)
LiveData<List<User>> getUsers(SupportSQLiteQuery query);
}
LiveData<List<User>> liveUsers = rawDao.getUsers(
new SimpleSQLiteQuery("SELECT * FROM User ORDER BY name DESC"));
Rxjava
Room還可以讓你定義的查詢返回RxJava2的Publisher和Flowable對象脏毯。要使用這個(gè)功能,在Gradle dependencies中添加android.arch.persistence.room:rxjava2幔崖。然后你就可以返回RxJava2中定義的對象類型了食店,如下面的代碼所示:
@Dao
public interface MyDao {
@Query("SELECT * from user where id = :id LIMIT 1")
public Flowable<User> loadUserById(int id);
}
Cursor
如果你的app需要直接獲得返回的行,你可以在查詢中返回Cursor對象赏寇。但是非常不鼓勵(lì)使用Cursor 吉嫩,因?yàn)樗鼰o法保證行是否存在,或者行包含什么值嗅定。
public interface MyDao {
@Query("SELECT * FROM user WHERE age > :minAge LIMIT 5")
public Cursor loadRawUsersOlderThan(int minAge);
}
多表查詢
一些查詢可能要求查詢多張表來計(jì)算結(jié)果自娩。Room允許你書寫任何查詢,因此表連接(join)也是可以的渠退。
而且如果響應(yīng)是一個(gè)可觀察的數(shù)據(jù)類型忙迁,比如Flowable或者LiveData,Room將觀察查詢中涉及到的所有表碎乃,檢測出所有的無效表姊扔。
下面的代碼演示了如何執(zhí)行一個(gè)表連接查詢來查出借閱圖書的user與被借出圖書之間的信息。
邏輯:入?yún)serName ---- (user.name LIKE :userName) ----> user ---- (user.id = loab.userid) ----> loab ---- (loan.book_id = book.id) ----> book
語句:book <---- (loan.book_id = book.id) ---- loab <---- (user.id = loab.userid) ---- user <---- (user.name LIKE :userName) ---- userName
@Dao
public interface MyDao {
@Query("SELECT * FROM book "
+ "INNER JOIN loan ON loan.book_id = book.id "
+ "INNER JOIN user ON user.id = loan.user_id "
+ "WHERE user.name LIKE :userName")
public List<Book> findBooksBorrowedByNameSync(String userName);
}
- INNER JOIN:內(nèi)連接梅誓,顯示左表和右表符合連接條件的記錄
- JOIN: 如果表中有至少一個(gè)匹配恰梢,則返回行
- LEFT JOIN: 即使右表中沒有匹配晨川,也從左表返回所有的行
- RIGHT JOIN: 即使左表中沒有匹配,也從右表返回所有的行
- FULL JOIN: 只要其中一個(gè)表中存在匹配删豺,就返回行
類型轉(zhuǎn)換
1共虑、Room對Java的基本數(shù)據(jù)類型以及其包裝類型都提供了支持
2、但是有時(shí)候你可能使用了一個(gè)自定義的數(shù)據(jù)類型呀页,并且你想將此類型的數(shù)據(jù)存儲到數(shù)據(jù)庫表中的字段里妈拌。
為了實(shí)現(xiàn)自定義數(shù)據(jù)類型的轉(zhuǎn)換,你需要一個(gè)類型轉(zhuǎn)換器TypeConverter蓬蝶,它將負(fù)責(zé)處理自定義數(shù)據(jù)類和Room可以保存的已知類型之間的轉(zhuǎn)換尘分。
比如,如果我們想要保存Date實(shí)例丸氛,那么第一步
- 寫一個(gè)TypeConverter類培愁,實(shí)現(xiàn)Date類型和Long類型的數(shù)據(jù)轉(zhuǎn)換
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();
}
}
可以看到上面的轉(zhuǎn)換類,提供了兩個(gè)方法以實(shí)現(xiàn)Date類型和Long類型的數(shù)據(jù)相互轉(zhuǎn)換缓窜。
-
使用TypeConverter類定续,實(shí)現(xiàn)持久化Date類型的數(shù)據(jù)
這里就需要把這個(gè)轉(zhuǎn)換類Converters
添加到我們的數(shù)據(jù)庫組件AppDatabase
中了
@Database(entities = {User.java}, version = 1)
@TypeConverters({Converter.class})
public abstract class AppDatabase extends RoomDatabase {
public abstract UserDao userDao();
}
數(shù)據(jù)庫升級
-
基本使用
實(shí)現(xiàn)就是在對應(yīng)的Database中通過Migration進(jìn)行升級,使用的方式是:
1禾锤、利用Migration 構(gòu)建數(shù)據(jù)庫升級語句
//數(shù)據(jù)庫升級
private static final Migration migration_1_2 = new Migration(1, 2) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
}
};
2私股、在數(shù)據(jù)庫的構(gòu)造方法中通過addMigrations()方法傳入對應(yīng)的Migration
public synchronized static AppDatabase getInstance(byte[] passphrase) {
if (INSTANCE == null) {
synchronized (AppDatabase.class) {
if (INSTANCE == null) {
INSTANCE = Room
.databaseBuilder(mContext.getApplicationContext(), AppDatabase.class, sDbPath)
.openHelperFactory(new HelperFactory(passphrase))
.allowMainThreadQueries()
.addMigrations(migration_1_2)
.build();
}
}
}
return INSTANCE;
}
3、數(shù)據(jù)庫版本(version )+1
@Database(entities = {MarkBook.class}, version = 2, exportSchema = false)
public abstract class AppDatabase extends RoomDatabase {
....
總結(jié):就是把數(shù)據(jù)庫的變化通過SQL語句傳到數(shù)據(jù)庫的構(gòu)造方法中恩掷。
- 常用的幾種數(shù)據(jù)庫升級
新增表
//數(shù)據(jù)庫升級
private static final Migration migration_1_2 = new Migration(1, 2) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
database.execSQL("CREATE TABLE IF NOT EXISTS SYMBOL_SETTING (_id INTEGER primary key NOT NULL, dbName TEXT, symbolJson TEXT)");
}
};
增加字段
//數(shù)據(jù)庫升級
private static final Migration migration_1_2 = new Migration(1, 2) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
database.execSQL("ALTER TABLE SURVEY_RECORD ADD STATUS INTEGER DEFAULT 0");
}
};
數(shù)據(jù)庫多次升級
//數(shù)據(jù)庫升級,SURVEY_RECORD新增SJBZ倡鲸、SZFHZZZB字段
private static final Migration migration_2_3 = new Migration(2, 3) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
database.execSQL("ALTER TABLE SURVEY_RECORD ADD SJBZ TEXT");
database.execSQL("ALTER TABLE SURVEY_RECORD ADD SZFHZZZB TEXT");
}
};
//數(shù)據(jù)庫升級
private static final Migration migration_1_2 = new Migration(1, 2) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
database.execSQL("ALTER TABLE SURVEY_RECORD ADD STATUS INTEGER DEFAULT 0");
}
};
public synchronized static TaskDatabase getInstance(byte[] passphrase) {
if (INSTANCE == null) {
synchronized (TaskDatabase.class) {
if (INSTANCE == null) {
INSTANCE = Room
.databaseBuilder(mContext.getApplicationContext(), TaskDatabase.class, sDbPath)
.openHelperFactory(new HelperFactory(passphrase))
.allowMainThreadQueries()
.addMigrations(migration_1_2,migration_2_3)
.build();
}
}
}
return (INSTANCE);
}
備注:數(shù)據(jù)庫升級的時(shí)候最好文字說明一下這次升級了什么,不要因?yàn)橛蠸QL語句就不寫注解了黄娘,這樣不好峭状。
以普通的方式打開SQLite數(shù)據(jù)庫
有時(shí)候我們需要以普通的方式打開SQLite數(shù)據(jù)庫,以方便我們使用原生的更新逼争、刪除优床、查詢語句
TaskDatabase database = TaskDatabase.getInstance(Encryption.getInstance().getDBEncryptionPassword());
SupportSQLiteDatabase db = database.getOpenHelper().getWritableDatabase();
db.execSQL("");