Room的学习总结

什么是Room?

  • Room是google官方开发的对象关系映射(ORM)库框架,采用注解的形式。它能缓存相关数据,使用户在断网的情况下浏览相应内容,重新连接网络时,用户发起的内容更改都会同步到服务器。能将本地数据保存到数据库中,它提供了一个SQLite的抽象层(封装),能够让我们更加稳健地访问数据库,能够提升数据库的性能。

Room的三个主要组件

  1. Entity: 表示Room数据库内的表(Table)。
  2. Database: 数据库的持有者,是与 App 持久关联数据的底层连接的主要访问点。是一个继承 RoomDatabase 的抽象类。在类前注解中包含与数据库相关联的实体列表。类中包含一个具有 0 个参数的抽象方法,并返回用 @Dao 注解的类。 用户可以通过调用 Room.databaseBuilder() 或 Room.inMemoryDatabaseBuilder() 获取 Database 实例。
  3. Dao(Data access object): Dao是用户来使用访问数据库的主要工具,它可以是一个接口,可以是一个抽象类。如果是一个抽象类,可以有一个构造函数,其只接收一个 RoomDatabase 参数。** 当系统编译时,Room会自动为Dao创造具体实现。

如何使用Room?

Entity的使用:

  • 先上一段代码,在对其进行解释
// An highlighted block
@Entity(tableName = "users")
public class User{
    @PrimaryKey    
    public int id;

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

    @ColumnInfo(name = "last_name")
    public  String lastName;
    
    @Ignore
    Bitmap picture;
}
  • 在以上代码中,注解@Entity表示这是一个数据库的表,其中的tableName为自定义表的名称。
  • 注解@PrimaryKey表示主键,也就是表中的主键,每个表都要至少有一个主键,当这个主键是一个字符串时,还要加上@NonNull注解,不然会出现编译错误。
  • 注解@ColumnInfo表示这是数据库表中的一个列。其中的name表示此对象在表中对应的类名,如果不添加此注解,Room默认会以此变量名作为其在表中的列名。
  • 注解@Ignore表示忽略此对象,数据库中不会创建此列。
// An highlighted block
@Entity(foreignKeys = @ForeignKey(entity = User.class,
                                    parentColumns = "id",
                                   childColumns = "user_id"))
public class Book {
	@PrimaryKey
	public int bookId;
  
	public String title;
  
	@ColumnInfo(name = "user_id")
	public int userId;
}
  • 注解@ForeignKey: 在SQLite数据库中,我们可以指定对象之间的关系,可以让对象引用彼此,但是Room中却禁止这样做。虽然在Room中我们不能使用直接的对象关系,但是Room提供了外键, 外键在@Entity中的foreignKey定义父表子表的关系,当父表中某条记录子表有依赖的时候,父表的这条记录是不能删除的。
// An highlighted block
public class Address {
    public String street;
    public String state;
    public String city;

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

@Entity(tableName = "users")
public class User{
    @PrimaryKey    
    public int id;

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

    @ColumnInfo(name = "last_name")
    public  String lastName;
    
    @Ignore
    Bitmap picture;
    
	@Embedded
    public Address address;
}

  • 注解@Enbedded: 是一个嵌套的注解。有时候,我们需要将多个对象组合成一个对象,对象与对象之间是有嵌套关系的。以上的代码中,User类中通过嵌套Address对象,现在User表中也包含了Address中的列street,state等等…
// An highlighted block
@Entity(tableName = "users",indices = {@Index("firstName"),
        @Index(value = {"last_name", "address"})})
public class User{
    @PrimaryKey    
    public int id;

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

    @ColumnInfo(name = "last_name")
    public  String lastName;
    
    @Ignore
    Bitmap picture;
    
	@Embedded
    public Address address;
}
  • 注解@indices: 为定义数据库的查找索引,用于快速查找。索引也是分两种的,唯一索引和非唯一索引。如果是唯一索引重复会报错的。可以通过@Index的unique来设置是否唯一索引。

如何使用Database

  • Database要继承RoomDatabase抽象类,要包含一个无参方法,返回Dao对象。并且我们尽量要把这个Database设置为单例模式,让全局只有一个数组库实例,以便于不会出现问题。
// An highlighted block
@Database(entities = {User.class}, version = 1, exportSchema = false)
public abstract class MyDatabase extends RoomDatabase {

    private static MyDatabase sInstance;

    // 构造函数必须是public,否则报错
    public MyDatabase() {
    }

    // 单实例模式,节省资源
    public static MyDatabase getsInstance(Context context) {
        if (sInstance == null) {
            synchronized (MyDatabase.class) {
                if (sInstance == null) {
                    sInstance = Room.databaseBuilder(context.getApplicationContext(),
                            MyDatabase.class, "sampleDb").build();
                }
            }
        }
        return sInstance;
    }

    // 定义获取Dao的方法
    abstract UserDao userDao();
}

如何使用Dao

  • Dao在Room组件中是一个非常重要的组成部分。在这个Dao类中,定义了许多操作数据库的简便方法。下面是几个方法的例子:

注解@Insert插入

// An highlighted block
@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);//插入集合
}
  • 如何插入一个参数,那么可以返回一个Long值,来代表插入的rowId。

注解@update更新

// An highlighted block
@Dao
public interface MyDao {
    @Update
    public void updateUsers(User... users);
}
  • update方法是一个更新一系列Entity的简便方法,他根据每个Entity的主键作为更新的依据。这个方法可以返回一个int值,作为影响的行数(更新的行数)。

注解@Delete删除

// An highlighted block
@Dao
public interface MyDao {
    @Delete
    public void deleteUsers(User... users);
}
  • delete方法是一个可以删除一系列Entity的简便方法,他根据每个Entity的主键作为删除的依据。这个方法也可以返回一个int值,来作为被删除的行数。

注解@Query查询

  • @Query是DAO类中主要被使用的注解,每一个@Query方法都是在编译时检查,如果查询方法写的时候出现问题,那么将会直接出现编译错误。
  • 简单的查询
// An highlighted block
@Dao
public interface MyDao {
    @Query("SELECT * FROM user")
    public User[] loadAllUsers();
}
  • 这个查询是查询表User中所有的字段,加载所有的user。
  • 向Query中传递参数
// An highlighted block
@Dao
public interface MyDao {
    @Query("SELECT * FROM user WHERE age > :minAge")
    public User[] loadAllUsersOlderThan(int minAge);
}
  • 有时候查询的时候,我们要进行筛选,选出满足自己要求的数据进行查询,这个例子中,传入了minAge参数,查询结果是所有age变量大于这个minAge的user对象。
// An highlighted block
@Dao
public interface MyDao {
    @Query("SELECT * FROM user WHERE age BETWEEN :minAge AND :maxAge")
    public User[] loadAllUsersBetweenAges(int minAge, int maxAge);
    @Query("SELECT * FROM user WHERE first_name LIKE :search "
           + "OR last_name LIKE :search")
    public List<User> findUserWithName(String search);
}
  • 这是传入多个参数的例子,第一个查询结果是查询age变量在minAge和maxAge之间的所有user。第二个查询是first_name和last_name中有一个等于search的所有user。
  • 查询字段的子集
    有时候我们不需要查询返回所有的数据,我们只需要表中的某几条数据,这是我们要创建一个类,这个类中包含我们想得到的对象,然后在Query的返回参数中设置成这个类的对象就可以了。
// An highlighted block
public class NameTuple {
    @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();
}
  • 可观察的Query
  • 这个完成很简单,只需要将返回的数据变为LiveData即可。
// An highlighted block
@Dao
public interface MyDao {
    @Query("SELECT first_name, last_name FROM user WHERE region IN (:regions)")
    public LiveData<List<User>> loadUsersFromRegionsSync(List<String> regions);
}

Room数据库的升级和迁移

  • 当用户更新了app的最新版本,当我们新添加表单,增加新的列的时候,我们也要进行数据库的升级或者说是迁移,如果我们不正确的迁移的话,系统就会重建一个数据库,之前数据库的数据就会销毁丢失掉。实现数据库的迁移有好几个方法,但是用的最多的,最好用的就是Migration类的 migrate() 方法。
  • Room让我们可以自定义Migration类来保存用户的数据,每一个自定义的这个类中都要指定from和to作为数据库的版本。
// An highlighted block
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) {
    @Override
    public void migrate(SupportSQLiteDatabase database) {
        database.execSQL("ALTER TABLE Book "
                + " ADD COLUMN pub_year INTEGER");
    }
};
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值