Android Jetpack组件系列文章:
Android Jetpack组件(一)LifeCycle
Android Jetpack组件(二)Navigation
Android Jetpack组件(三)ViewModel
Android Jetpack组件(四)LiveData
Android Jetpack组件(五)Room
Android JetPack组件(六)DataBinding
Android Jetpack组件(七)Paging
Android Jetpack组件(八)WorkManager
Android Jetpack组件(九)DataStore
首语
Android使用SQLite作为数据库存储数据,但是SQLite使用繁琐且容易出错,有许多开源的数据如GreenDAO、ORMLite等,这些都是为了方便SQLite的使用而出现的,Google也意识到了这个问题,在Jetpack组件中推出了Room,Room在SQLite上提供了一层封装,可以流畅的访问数据库。
优势
- 拥有SQLite的所有操作功能。
- 使用简单,通过注解的方式实现相关功能,编译时自动生成实现类impl。
- 与LiveData、LifeCycle及Paging天然支持。
依赖
implementation "androidx.room:room-runtime:2.2.6"
annotationProcessor "androidx.room:room-compiler:2.2.6"
相关概念
Room主要包含三个组件:
- 数据库:包含数据库持有者,作为应用已保留的持久关系型数据的底层连接的主要接入点。
使用@Database
注解的类应满足以下条件: - 是扩展
RoomDatabase
的抽象类。 - 在注释中添加与数据库关联的实体列表。
- 包含具有0个参数且返回使用
@Dao
注释的类的抽象方法。 - Entity:表示数据库中的表。
- DAO:包含用于访问数据库的方法。
应用使用 Room 数据库来获取与该数据库关联的数据访问对象 (DAO)。然后,应用使用每个 DAO 从数据库中获取实体,然后再将对这些实体的所有更改保存回数据库中。 最后,应用使用实体来获取和设置与数据库中的表列相对应的值。Room架构图如图所示。
; 使用
创建数据库。
@SkipQueryVerification
@Database(entities = {Student.class}, version = 1, exportSchema = false)
public abstract class StudentDatabase extends RoomDatabase {
private static volatile StudentDatabase database;
private StudentDatabase() {
}
public static StudentDatabase getInstance() {
if (database == null) {
synchronized (StudentDatabase.class) {
if (database == null) {
database = Room.databaseBuilder(AppGlobals.getApplication(), StudentDatabase.class, "room_cache")
.allowMainThreadQueries()
.fallbackToDestructiveMigration()
.fallbackToDestructiveMigrationFrom()
.addMigrations(StudentDatabase.sMigration, StudentDatabase.mMigration)
.build();
}
}
}
return database;
}
static Migration sMigration = new Migration(1, 2) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
database.execSQL("alter table teacher rename to student");
database.execSQL("alter table teacher add column teacher_age INTEGER NOT NULL default 0");
}
};
static Migration mMigration = new Migration(2, 3) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
}
};
}
注意:如果我们设置了 exportSchema
(默认值是true),需要在app.gradle中配置存放位置。
defaultConfig {
...
javaCompileOptions {
annotationProcessorOptions {
arguments=["room.schemaLocation":"$projectDir/schemas".toString()]
}
}
}
关于Room的诸多注解,可参考Room的源码,在 room_common
jar包下,注释非常详细。
创建Entity
@Fts4(languageId ="china")
@Entity(tableName = "student" ,foreignKeys = {@ForeignKey(entity = User.class,parentColumns = "id",childColumns = "key",onDelete = ForeignKey.CASCADE,onUpdate = ForeignKey.RESTRICT)}, ignoredColumns = "score",indices = {@Index("index"),@Index(value = {"name","age"},unique = true)})
public class Student extends Score{
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "id", typeAffinity = ColumnInfo.INTEGER)
public int id;
@ColumnInfo(name = "name", typeAffinity = ColumnInfo.TEXT)
public String name;
@ColumnInfo(name = "age", typeAffinity = ColumnInfo.TEXT)
public String age;
public Student(int id, String name, String age) {
this.id = id;
this.name = name;
this.age = age;
}
@Ignore
public Student(String name, String age) {
this.name = name;
this.age = age;
}
public ForeignTable foreignTable;
@Relation(entity = User.class,parentColumn = "id",entityColumn ="key" ,projection = {"name","age"})
public User mUSer;
}
class ForeignTable{
@PrimaryKey
@NonNull
public String foreign_key;
public byte[] foreign_data;
}
默认情况下,Room将Entity类名作为表名,想单独设置,可通过 @Entity
注解里的 tableName
设置。
每个Entity至少有一个字段作为主键,如果想让数据库为字段自动分配ID,可以使用 autoGenerate
,如果Entity想有符合主键,可以使用 @Entity
注解里的 primaryKeys
,设置复合主键。
Room通过 @Ignore
设置忽略字段,如果Entity继承了父Entity的字段,可以通过 @Entity
注解里的 ignoredColumns
属性设置。
Room支持全文搜索,通过使用 @Fts3
(仅在应用程序具有严格的磁盘空间要求或需要与较旧的SQLite版本兼容时使用)或 @Fts4
添加到Entity来实现。Room版本须高于2.1.0。
需要注意的是:启用Fts的表必须使用Integer类型的主键,且列名为” rowid
“。
如果表支持以多种语言显示内容,可以使用 languageId
指定用于存储每一行语言信息的列。
如果应用不支持使用全文搜索,可以将数据库的某些列编入索引,加快查询速度,通过 @Entity
注解添加 indices
,列出要在索引或符合索引中包含的列名称。
有时候,数据库中的某些字段必须是唯一的,可以通过 @Index
注解的 unique
属性设为 true
,强制实施此唯一属性。如上代码所示可防止 name
和 age
同组值的两行。
在 Room 2.1.0 以上版本中,基于 Java 的不可变值类(使用 @AutoValue 进行注释)用作应用数据库中的Entity。此支持在Entity的两个实例被视为相等(如果这两个实例的列包含相同的值)时尤为有用。
将带有 @AutoValue
注释的类用作实体时,可以使用 @PrimaryKey
、 @ColumnInfo
、 @Embedded
和 @Relation
为该类的抽象方法添加注释。但是,您必须在每次使用这些注解时添加 @CopyAnnotations
注解,以便 Room 可以正确解释这些方法的自动生成实现。
@AutoValue
@Entity
public abstract class User {
@CopyAnnotations
@PrimaryKey
public abstract long getId();
public abstract String getFirstName();
public abstract String getLastName();
public static User create(long id, String firstName, String lastName) {
return new AutoValue_User(id, firstName, lastName);
}
}
创建DAO
最后,我们通过DAO来访问数据。DAO可以是接口,也可以是抽象类,如果是抽象类,则该DAO可以选择有一个以RoomDatabase为唯一参数的构造函数。Room 会在编译时创建每个 DAO 实现。在DAO文件上方添加 @DAO
注解。
@Dao
public interface CacheDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
long save(Cache cache);
@Query("select *from cache where key
=:key")
Cache getCache(String key);
@Delete
int delete(Cache cache);
@Update(onConflict = OnConflictStrategy.REPLACE)
int update(Cache cache);
@RawQuery
Cache getAllCache(SupportSQLiteQuery sqLiteQuery);
}
我们创建好了数据库,定义好了Entity和DAO后,可以操作数据。需要注意,数据操作应在工作线程操作,除非指定在主线程可以查询,否则会发生崩溃。
public abstract CacheDao getCache();
long rowID = StudentDatabase.getInstance().getCache().save(cache);
int lines = StudentDatabase.getInstance().getCache().delete(cache);
销毁与重建
如果需要对数据库中的字段类型进行修改,最好的方式就是销毁与重建。
主要包含以下几个步骤:
- 创建一张和修改的表同数据结构的临时表。
- 将数据从修改的表复制到临时表中。
- 删除要修改的表。
- 将临时表重命名为修改的表名。
static final Migration MIGRATION_3_4 = new Migration(3, 4) {
@Override
public void migrate(SupportSQLiteDatabase database) {
database.execSQL("CREATE TABLE temp_Student (" +
"id INTEGER PRIMARY KEY NOT NULL," +
"name TEXT," +
"age TEXT)");
database.execSQL("INSERT INTO temp_Student (id, name, age)" +
"SELECT id, name, age FROM Student");
database.execSQL("DROP TABLE Student");
database.execSQL("ALTER TABLE temp_Student RENAME TO Student");
}
};
预填充数据库
有时候,需要在应用启动的时候就加载一组特定的数据,这就称为预填充数据库。
从应用资源预填充
如需从位于应用assets/目录中的任意位置的预封装数据库文件预填充Room数据库,请先从 RoomDatabase.Builder
对象调用 createFromAsset()
,然后再调用 build()
。
@Database(entities = {Cache.class}, version = 1)
public abstract class PreDatabase extends RoomDatabase {
private static volatile PreDatabase database;
private PreDatabase() {
}
public static PreDatabase getInstance() {
if (database == null) {
synchronized (PreDatabase.class) {
if (database == null) {
Room.databaseBuilder(context, PreDatabase.class, "Sample.db")
.createFromAsset("database/myapp.db")
.build();
}
}
}
return database;
}
}
从文件系统预填充
如果觉得在assets目录下占用应用体积,可以在应用启动时从服务端下载数据库文件到本地,从设备文件系统任意位置(应用的 assets/ 目录除外)的预封装数据库文件预填充Room数据库,请先从 RoomDatabase.Builder
对象调用 createFromFile()
,然后再调用 build()
。
@Database(entities = {Cache.class}, version = 1)
public abstract class PreDatabase extends RoomDatabase {
private static volatile PreDatabase database;
private PreDatabase() {
}
public static PreDatabase getInstance() {
if (database == null) {
synchronized (PreDatabase.class) {
if (database == null) {
Room.databaseBuilder(appContext, PreDatabase.class, "Sample.db")
.createFromFile(new File("mypath"))
.build();
}
}
}
return database;
}
}
Room与LiveData和ViewModel的结合
当Room数据库中的数据发生变化时 ,能够通过LiveData组件通知View层,实现数据的自动更新。
首先使用LiveData将返回的数据包装起来。
@Query("select *from cache")
LiveData<Cache> getCache();
创建ViewModel,实例化数据库。
public class CacheViewModel extends AndroidViewModel {
private MyDatabase myDatabase;
private LiveData<Cache> cacheLiveData;
public CacheViewModel(@NonNull Application application) {
super(application);
myDatabase=MyDatabase.getInstance(application);
cacheLiveData=myDatabase.getCacheDao().getCache();
}
public LiveData<Cache> getCacheLiveData(){
return cacheLiveData;
}
}
在Activity中实例化 CacheViewModel
,监听LiveData的变化。当我们对数据库进行相关操作时, onChanged()
会自动调用。
LiveData<Cache> cacheLiveData = new ViewModelProvider(this).get(CacheViewModel.class).getCacheLiveData();
cacheLiveData.observe(this, new Observer<Cache>() {
@Override
public void onChanged(Cache cache) {
Log.e("yhj", "onChanged: "+cache.key);
}
});
我之前使用的网络框架是RxJava+Retrofit+SQLite组合使用,学习完Jetpack后,我使用LiveData+Retrofit+Room封装了网络请求缓存框架,将Jetpack组合使用能更好的理解相关组件。
Github地址:https://github.com/hujuny/EasyHttp
Original: https://blog.csdn.net/yang_study_first/article/details/115232804
Author: 八归少年
Title: Android Jetpack组件(五)Room
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/817383/
转载文章受原作者版权保护。转载请注明原作者出处!