0
点赞
收藏
分享

微信扫一扫

Android架构组件JetPack之Room(三),阿里P8架构师

千妈小语 2022-01-30 阅读 191
  • @Ignore 用于告诉Room需要忽略的字段或方法
  • 建立索引:在@Entity注解的indices属性中添加索引字段。例如:indices = {@Index(value = {"first_name", "last_name"}, unique = true), ...}, unique = true可以确保表中不会出现{"first_name", "last_name"} 相同的数据。

1.2 Entitiy间的关系

不同于目前存在的大多数ORM库,Room不支持Entitiy对象间的直接引用。(具体原因可以参考: Understand why Room doesn’t allow object references)
但Room允许通过**外键(Foreign Key)**来表示Entity之间的关系。

@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;
}

如上面代码所示,Book对象与User对象是属于的关系。Book中的user_id,对应User中的id。 那么当一个User对象被删除时, 对应的Book会发生什么呢?

@ForeignKey注解中有两个属性onDeleteonUpdate, 这两个属性对应ForeignKey中的onDelete()onUpdate(), 通过这两个属性的值来设置当User对象被删除/更新时,Book对象作出的响应。这两个属性的可选值如下:

  • CASCADE:User删除时对应Book一同删除; 更新时,关联的字段一同更新
  • NO_ACTION:User删除时不做任何响应
  • RESTRICT:禁止User的删除/更新。当User删除或更新时,Sqlite会立马报错。
  • SET_NULL:当User删除时, Book中的userId会设为NULL
  • SET_DEFAULT:与SET_NULL类似,当User删除时,Book中的userId会设为默认值

1.3 对

象嵌套

在某些情况下, 对于一张表中的数据我们会用多个POJO类来表示,在这种情况下可以用@Embedded注解嵌套的对象,比如:

class Address {
public String street;
public String state;
public String city;

@ColumnInfo(name = “post_code”)
public int postCode;
}

@Entity
class User {
@PrimaryKey
public int id;

public String firstName;

@Embedded
public Address address;
}

以上代码所产生的User表中,Column 为id, firstName, street, state, city, post_code

2. 创建数据访问对象(DAO)

@Dao
public interface UserDao {
@Query(“SELECT * FROM user”)
List getAll();

@Query(“SELECT * FROM user WHERE uid IN (:userIds)”)
List 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(List users);

@Insert(onConflict = OnConflictStrategy.REPLACE)
public void insertUsers(User… users);

@Delete
void delete(User user);

@Update
public void updateUsers(List users);
}

DAO 可以是一个接口,也可以是一个抽象类, Room会在编译时创建DAO的实现。

Tips:

  • @Insert方法也可以定义返回值, 当传入参数仅有一个时返回long, 传入多个时返回long[]List<Long>, Room在实现insert方法的实现时会在一个事务进行所有参数的插入。
  • @Insert的参数存在冲突时, 可以设置onConflict属性的值来定义冲突的解决策略, 比如代码中定义的是@Insert(onConflict = OnConflictStrategy.REPLACE), 即发生冲突时替换原有数据
  • @Update@Delete 可以定义int类型返回值,指更新/删除的函数

DAO中的增删改方法的定义都比较简单,这里不展开讨论,下面更多的聊一下查询方法。

2.1 简单的查询

Talk is cheap, 直接show code:

@Query(“SELECT * FROM user”)
List getAll();

2.2 查询参数传递

@Query(“SELECT * FROM user WHERE uid IN (:userIds)”)
List 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);

看代码应该比较好理解, 方法中传递参数arg, 在sql语句中用:arg即可。编译时Room会匹配对应的参数。

2.3 查询表中部分字段的信息

在实际某个业务场景中, 我们可能仅关心一个表部分字段的值,这时我仅需要查询关心的列即可。

定义子集的POJO类:

public class NameTuple {
@ColumnInfo(name=“first_name”)
public String firstName;

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

在DAO中添加查询方法:

@Query(“SELECT first_name, last_name FROM user”)
public List loadFullName();

2.3 查询结果的返回类型

Room中查询操作除了返回POJO对象及其List以外, 还支持:

  • LiveData<T>:
    LiveData是架构组件库中提供的另一个组件,可以很好满足数据变化驱动UI刷新的需求。Room会实现更新LiveData的代码。

@Query(“SELECT first_name, last_name FROM user WHERE region IN (:regions)”)
public LiveData<List> loadUsersFromRegionsSync(List regions);

  • Flowablbe<T> Maybe<T> Single<T>:
    Room 支持返回RxJava2 的Flowablbe, MaybeSingle对象,对于使用RxJava的项目可以很好的衔接, 但需要在gradle添加该依赖:android.arch.persistence.room:rxjava2

@Query(“SELECT * from user where id = :id LIMIT 1”)
public Flowable loadUserById(int id);

  • Cursor:
    返回Cursor是为了支持现有项目中使用Cursor的场景,官方不建议直接返回Cursor.

2.4 联表查询

Room支持联表查询,接口定义上与其他查询差别不大, 主要还是sql语句的差别。

@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 findBooksBorrowedByNameSync(String userName);
    }

3. 创建数据库

Room中DataBase类似SQLite API中SQLiteOpenHelper,是提供DB操作的切入点,但是除了持有DB外, 它还负责持有相关数据表(Entity)的数据访问对象(DAO), 所以Room中定义Database需要满足三个条件:

  • 继承RoomDataBase,并且是一个抽象类
  • 用@Database 注解,并定义相关的entity对象, 当然还有必不可少的数据库版本信息
  • 定义返回DAO对象的抽象方法

@Database(entities = {User.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
public abstract UserDao userDao();
}

创建好以上Room的三大组件后, 在代码中就可以通过以下代码创建Database实例。

AppDatabase db = Room.databaseBuilder(getApplicationContext(),
AppDatabase.class, “database-name”).build();

三、数据库迁移

3.1 Room数据库升级

在传统的SQLite API中,我们如果要升级数据库, 通常在SQLiteOpenHelper.onUpgrade方法执行数据库升级的sql语句,这些sql语句的通常根据数据库版本以文件的方式或者用数组来管理。有人说这种方式升级数据库就像在拆炸弹,相比之下在Room中升级数据库简单的就像是按一个开关而已。

Room提供了Migration类来实现数据库的升级:

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

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) {
ation(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) {

举报

相关推荐

0 条评论