Android Jetpack架构组件—Room

1,395 阅读5分钟

一、Room介绍

1、概述

Room是Jetpack组件库一员,是Google官方提供的数据库ORM框架,在SQLite上提供了一个抽象层,以便在利用SQLite的全部功能的同时能更加流畅的访问数据库。

谷歌官方的介绍:

The Room persistence library provides an abstraction layer over SQLite to allow for more robust database access while harnessing the full power of SQLite.

Room持久性库在SQLite上提供了一个抽象层,允许在充分利用SQLite功能的同时进行更健壮的数据库访问。

简单来说:Room是一个基于SQLite的强大数据库框架。

2、Room中三个主要组件

Database:数据库持有者,该组件用来创建一个database holder。注解定义实体的列表,类的内容定义从数据库中获取数据的对象(DAO)。它也是底层连接的主要入口。这个被注解的类是一个继承RoomDatabase的抽象类。在运行时,可以通过调用Room.databaseBuilder() 或者 Room.inMemoryDatabaseBuilder()来得到它的实例。

Entity:实体类,该组件的一个示例表示数据库的一行数据,对于每个Entity类来说,都会有对应的table被创建。想要这些Entity被创建,就需要卸载上面的Database的注解参属entities列表中,默认Entity中的所有字段都会来创建表,除非该字段上加上@Ignore注解。

Dao:访问数据库的一系列方法,该组件用来表述具有Data Access Object(DAO)功能的类或者接口,DAO类时Room的重要组件,负责定义查询(添加或者删除等)数据库的方法。使用@Database注解的类中必须定义一个不带参数的抽象方法,这个方法返回使用@Dao注解的类,返回类型为@Dao注解过的类的抽象方法Room会在编译时生成这个类的实现。

乍看不懂这三个概念觉得很懵逼,一头雾水的没关系,也不需要强记,我只是在这记录一下,往下看实战部分就行,回过头来,概念自然就明白了。

二、实战

1、添加Room库的依赖

首先在Google的Maven存储库(项目最外层的build.gradle文件中添加如下:

allprojects {
    repositories {
        jcenter()
        google()
    }
}

然后再app/build.gradle文件中添加相关依赖

def room_version = "2.2.0-rc01"
//Room依赖	
implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version"

如果项目是使用Kotlin语言来开发的,在添加room-compiler的时候使用kapt关键字,java语言开发的就使用annotationProcessor关键。否则会导致访问出错。

2、定义Entity

当一个类用 @Entity 注解并且被 @Database 注解中的 entities 属性所引用,Room就会在数据库中为这个被 @Entity 注解的类创建一张表。

默认实体类的类名为表名,参数名为字段名。当然我们也是可以修改的。在@Entity注解中传入了一个参数 tableName用来指定表的名称。

2.1 PrimaryKey

每个Entity必须至少有一个主键(Primary Key),即使只有一个属性,也要使用@PrimaryKey来注释这个属性。如果想让Room为Entity设置自增ID,需要设置@PrimaryKey的autoGenerate属性。

2.2 ColumnInfo

如果想要自定义表中的字段时,需要在属性上面加上 @ColumnInfo 注解,如:@ColumnInfo(name = "ID"),"ID"为自定义字段名。

@Entity(tableName = "PHONE")
public class PhoneBean implements Parcelable {

    @PrimaryKey(autoGenerate = true) // 设置主键
    @ColumnInfo(name = "ID") // 定义对应的数据库的字段名成
    private int id;

    @ColumnInfo(name = "PHONE")
    private String phone;

    @ColumnInfo(name = "NAME")
    private String name;

    @ColumnInfo(name = "DATE")
    private Date date;

    public PhoneBean(String phone, String name, Date date) {
        this.phone = phone;
        this.name = name;
        this.date = date;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Date getDate(){
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(this.id);
        dest.writeString(this.phone);
        dest.writeString(this.name);
        dest.writeLong(this.date != null ? this.date.getTime() : -1);
    }

    protected PhoneBean(Parcel in) {
        this.id = in.readInt();
        this.phone = in.readString();
        this.name = in.readString();
        long tmpDate = in.readLong();
        this.date = tmpDate == -1 ? null : new Date(tmpDate);
    }

    public static final Parcelable.Creator<PhoneBean> CREATOR = new 
    					Parcelable.Creator<PhoneBean>() {
        @Override
        public PhoneBean createFromParcel(Parcel source) {
            return new PhoneBean(source);
        }

        @Override
        public PhoneBean[] newArray(int size) {
            return new PhoneBean[size];
        }
    };
}
2.3 定义转换

TypeConverter 使用@TypeConverter注解定义转换的方法,如下,将Date类型的数据转换成Long类型来存储。

public class ConversionFactory {

    @TypeConverter
    public static Long fromDateToLong(Date date) {
        return date == null ? null : date.getTime();
    }

    @TypeConverter
    public static Date fromLongToDate(Long value) {
        return value == null ? null : new Date(value);
    }
}

如图,示例中将Date属性值转换为Long类型存储到数据库:

读取到数据库的Date属性值,再将Date属性值转换为字符串显示:

3、定义Dao

Dao是一个interface,定义一系列的增删改查操作数据库的方法。Room也为我们的提供了相关的注解,有@Insert、@Delete、@Update 和 @Query。其中 Update 和 Detele 操作是根据定义的主键进行。

@Dao
public interface PhoneDao {

    /**
     * 查询所有
     *
     * @return
     */
    @Query("SELECT * FROM PHONE")
    List<PhoneBean> getPhoneAll();

    /**
     * 根据指定字段查询
     *
     *我们先来看下 @Query 查询注解,它的参数时String类型,我们直接写SQL语句进行执行,
     *而且编译的时候可以进行语法检查。比如我们根据PHONE查询某个用户的信息:
     * @return
     */
    @Query("SELECT * FROM PHONE WHERE phone = :phone")
    List<PhoneBean> loadPhoneByIds(String phone);

    /**
     * 向数据库中添加数据
     *
     *我们看到直接中有个参数onConflict,表示的是当插入的数据已经存在时候的处理逻辑,
     *有三种操作逻辑:REPLACE、ABORT和IGNORE。
     *如果不指定则默认为ABORT终止插入数据。这里我们将其指定为REPLACE替换原有数据。
     *
     * @param phone
     */
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    void insertAll(List<PhoneBean> phone);

    /**
     * 修改数据
     *
     *如果需要修改某一条数据,则使用 @Update注解,和@Delete一样也是根据主键来查找要删除的实体。
     *
     * @param phone
     */
    @Update()
    void update(PhoneBean phone);

    /**
     * 删除数据
     *
     *如果需要删除表的数据则使用 @Delete注解:
     *使用主键来查找要删除的实体。
     *
     * @param phoneBean
     */
    @Delete()
    void delete(PhoneBean phoneBean);
}

4、定义Database——创建数据库

@Database(entities = {PhoneBean.class}, version = 1, exportSchema = false)
@TypeConverters({ConversionFactory.class})
public abstract class PhoneDatabase extends RoomDatabase {

    public static PhoneDatabase getDefault(Context context) {
        return buildDatabase(context);
    }

    private static PhoneDatabase buildDatabase(Context context) {
        return Room.databaseBuilder(context.getApplicationContext(), PhoneDatabase.class, "PHONE.db")
                .allowMainThreadQueries()
                .build();
    }

    public abstract PhoneDao getPhoneDao();
}

5、使用

  • 增加
private void insertPhone(String mName, String mPhone) {
    List<PhoneBean> mPhones = new ArrayList<>();
    mPhones.add(new PhoneBean(mPhone, mName));
    PhoneDatabase.getDefault(getApplicationContext()).getPhoneDao().insertAll(mPhones);
}
  • 查询
private void queryPhone() {
    List<PhoneBean> mPhoneLists = 
    		PhoneDatabase.getDefault(getApplicationContext()).getPhoneDao().getPhoneAll();
    // .....
}
  • 修改
private void updatePhone(String name, String phone) {
    PhoneDatabase.getDefault(getActivity().getApplicationContext()).getPhoneDao().
    					update(new PhoneBean(phone, name));
    // ......
}
  • 删除
private void deletePhone() {
    PhoneDatabase.getDefault(getActivity().getApplicationContext()).getPhoneDao().
    					delete(mPhoneBean);
    // ......
}

三、总结

以上就是Jetpack架构组件Room的基本使用,掌握以上基本操作其实就够了,像什么结合LiveData啥的,等用熟了Jetpack组件就自然明白了。