Jetpack -room (一)基本使用

一、简介

Room是安卓中SQLite上的一个抽象层应用框架,可以更轻松、更好地保存数据,下图是对比RoomGreendaoOrmlite三种组件对比图,由下图可以粗略得出,ROOM相比较其他两者速度会有一定提升。

ROOM架构如下图所示(图片来自google):

img

Room包含 3 个主要组件:

  • DataBase(数据库):包含数据库持有者,并作为应用已保留的持久关系型数据的底层连接的主要接入点。
  • Entity:表示数据库中的表。
  • DAO:包含用于访问数据库的方法。

应用使用 Room数据库来获取与该数据库关联的数据访问对象 (DAO)。然后,应用使用每个 DAO从数据库中获取实体,然后再将对这些实体的所有更改保存回数据库中。 最后,应用使用实体来获取和设置与数据库中的表列相对应的值。

二、常用注解

2.1、@Entity

  • @Entity:注解标识创建表,使用@Entity(tableName = "users")自定义表名
  • @ColumnInfo:注解标识数据库列,使用@ColumnInfo(name = "last_name")自定义列名
  • @Ignore:注解表示忽略此对象,数据库中不会创建此列;若使用继承,忽略父类的一个对象,使用`@Entity(ignoredColumns = "picture")``
  • ``@NonNull`:非空
  • @PrimaryKey:注解表示为主键,使用@Entity(primaryKeys = {"firstName", "lastName"})表示复合主键,使用@PrimaryKey(autoGenerate = true)设置主键自且类型为`INTEGER``
  • ``indices: 对特定的field建立索引加速数据库访问,索引:@Entity(indices = @Index("name")),复合索引:@Entity(indices = @Index(value = {"last_name", "address"}))`
  • @ForeignKey:用于设置外键@Entity(foreignKeys = @ForeignKey(entity = User.class, parentColumns = "id",childColumns = "user_id"))

2.2、@Dao

@Dao:注解是一个数据访问对象

2.2.1、@Insert:增

//onConflict = OnConflictStrategy.REPLACE 存在会替换
@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.2.2、@Update:改

//返回类型为int的情况,表示更新对象的个数
@Update
public void updateUsers(User... users);

2.2.3、@Delete:删

//返回类型为int的情况,表示删除对象的个数
@Delete
public void deleteUsers(User... users);

2.2.4、@Query:查

@Query("SELECT * FROM user")
public User[] loadAllUsers();

@Query("SELECT * FROM user WHERE age > :minAge")
public User[] loadAllUsersOlderThan(int minAge);

@Query("SELECT first_name, last_name FROM user WHERE region IN (:regions)")
public List<NameTuple> loadUsersFromRegions(List<String> regions);

//多表查询
@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);

其他

我们使用时会发现系统所提供的的增删改查并不能满足我们需要的操作,如我需要根据ID删除数据应该怎么做,这个时候就需要我们对SQL语法有一定掌握,然后使用@Query编写我们需要的SQL语句。

@Query("delete from user where id=:id")
void deleteUserById(int id);

三、实战

这部分针对数据的增删改查,以及数据库升级、迁移使用做介绍

3.1、基本使用

3.1.1、添加依赖

 def room_version = "2.3.0"

implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version"

// 支持rxjava2
implementation "androidx.room:room-rxjava2:$room_version"

// 支持rxjava3
implementation "androidx.room:room-rxjava3:$room_version"

3.1.2、创建表

@Entity
public class User {
    @PrimaryKey(autoGenerate = true)
    public int uid;

    @ColumnInfo(name = "first_name")
    public String firstName;

    @ColumnInfo(name = "last_name")
    public String lastName;
}

3.1.3、创建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.1.4、创建RoomDatabase

@Database(entities = {User.class}, version = 1,exportSchema = false)
public abstract class AppDatabase extends RoomDatabase {
    //volatile防止重排序
    private static volatile AppDatabase mAppDatabase;

    public abstract UserDao userDao();

    public static AppDatabase getSingleton(Context context) {
        if (mAppDatabase == null) {
            synchronized (AppDatabase.class) {
                if (mAppDatabase == null) {
                    //主线程使用.allowMainThreadQueries().build();创建
                    mAppDatabase = Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class, "user.db")
                            .allowMainThreadQueries()
                            .build();

                }
            }
        }
        return mAppDatabase;
    }
}

3.1.5、测试使用

AppDatabase appDatabase = AppDatabase.getSingleton(this);
User user = new User(0,"张","三");
User user1 = new User(1,"李","四");
appDatabase.userDao().insertAll(user);
appDatabase.userDao().insertAll(user1);
List<User> userList = appDatabase.userDao().getAll();

3.2 类型转换器

定义类型转换器

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();
    }
}

使用类型转换器

  • 放在Database,那么该数据库中的所有DaosEntities都可以使用它。
  • 放在DaoDao中的所有方法都可以使用它。
  • 放在Entity,实体的所有字段都可以使用它。
  • 放在Entity字段上,则只有该字段才能使用它。
  • Dao方法上,该方法的所有参数都可以使用它。
//放在Dao方法参数上,那么只有该字段才能使用它。
@TypeConverters({Converters.class})

3.3 与RxJava并肩作战

添加依赖

implementation 'androidx.room:room-rxjava2:2.1.0-beta01'

@Query方法:房间支持类型的返回值 PublisherFlowableObservable

@Insert@Update@Delete方法:2.1.0室和更高版本支持返回类型的值 CompletableSingle<T>Maybe<T>

@Dao
public interface MyDao {
    @Query("SELECT * from user where id = :id LIMIT 1")
    public Flowable<User> loadUserById(int id);

    // Emits the number of users added to the database.
    @Insert
    public Maybe<Integer> insertLargeNumberOfUsers(List<User> users);

    // Makes sure that the operation finishes successfully.
    @Insert
    public Completable insertLargeNumberOfUsers(User... users);

    /* Emits the number of users removed from the database. Always emits at
       least one user. */
    @Delete
    public Single<Integer> deleteUsers(List<User> users);
}

3.4 数据库升级

要使迁移逻辑按预期运行,使用完整查询,而不是引用表示查询的常量,否则会造成数据丢失

static final Migration MIGRATION_1_2 = new Migration(1, 2) {
    @Override
    public void migrate(SupportSQLiteDatabase database) {
        database.execSQL("CREATE TABLE `Fruit` (`id` INTEGER, "
                + "`name` TEXT, PRIMARY KEY(`id`))");
    }
};

static final Migration MIGRATION_2_3 = new Migration(2, 3) {
    @Override
    public void migrate(SupportSQLiteDatabase database) {
        database.execSQL("ALTER TABLE Book "
                + " ADD COLUMN pub_year INTEGER");
    }
};

Room.databaseBuilder(getApplicationContext(), MyDb.class, "database-name")
        .addMigrations(MIGRATION_1_2, MIGRATION_2_3).build();

推荐阅读更多精彩内容