Markdown版本笔记 | 个人GitHub首页 | 个人博客 | 个人微信 | 个人邮箱 |
---|---|---|---|---|
MyAndroidBlogs | baiqiantao | baiqiantao | bqt20094 | baiqiantao@sina.com |
Room是什么?java
Room是一个持久性数据库。react
The Room persistence library provides an abstraction layer over SQLite to allow for more robust database access while harnessing the full power of SQLite.android
Room持久性数据库提供了SQLite的抽象层,以便在充分利用SQLite的同时容许流畅的数据库访问。git
Room 的优势
在Android领域,目前已有很多优秀的开源的数据库给你们使用,如SQLite、XUtils、greenDao、Realm,那为何咱们还要去学习使用这个库呢?由于Room有下面几个优势:github
编译时
就会验证 - 在编译时检查每一个@Query和@Entity等,而且它不只检查语法问题,还会检查是否有该表,这就意味着没有任何运行时错误的风险可能会致使应用程序崩溃LiveData
、rxjava
集成,特别是与LiveData
等架构组件配合使用后,能产生让人惊叹的效果Room 的组成
Room中有三个主要组件组成:sql
数据库
。注解定义了一系列entities,而且类中提供一系列Dao的抽象方法,也是下层主要链接的访问点。注解的类应该是一个继承 RoomDatabase 的抽象类。在运行时,你能经过调用Room.databaseBuilder()
或者 Room.inMemoryDatabaseBuilder()
得到一个实例建立表
,Database类中的entities数组经过引用这些entity类建立数据库表。每一个entity中的字段都会被持久化到数据库中,除非用@Ignore
注解@Database
的类必须包含一个没有参数的且返回注解为@Dao
的类的抽象方法。在编译时,Room建立一个这个类的实现。当一个类被注解为@Entity
而且引用到带有@Database
注解的entities
属性,Room为这个数据库引用的entity建立一个数据表。 数据库
Entity类可以有一个空的构造函数(若是dao类可以访问每一个持久化的字段)或者一个参数带有匹配entity中的字段的类型和名称的构造函数json
Entity的字段必须为public或提供setter或者getter方法。数组
Ignore:忽略
默认状况下,Room为每一个定义在entity中的字段建立一个列。若是一个entity的一些字段不想持久化,可使用@Ignore
注解它们
@Ignore Bitmap picture;
PrimaryKey:主键
每一个entity必须定义至少一个字段做为主键,即便这里只有一个字段,仍然须要使用@PrimaryKey
注解这个字段。而且,若是想Room动态给entity分配自增主键,能够设置@PrimaryKey的autoGenerate
属性为true。若是entity有个组合的主键,你可使用@Entity
注解的primaryKeys属性:
@PrimaryKey(autoGenerate = true) public int id // 自增主键
组合主键:
@Entity(primaryKeys = {"firstName", "lastName"}) class User {}
tableName:数据库的表名
默认状况下,Room使用类名做为数据库的表名。若是但愿表有一个不一样的名称,设置@Entity注解的tableName属性,以下所示:
@Entity(tableName = "users") class User {}
注意: SQLite中的表名是大小写敏感的。
列名称:ColumnInfo
Room使用字段名称做为列名称。若是你但愿一个列有不一样的名称,为字段增长@ColumnInfo
注解
@ColumnInfo(name = "first_name") public String firstName;
indices:索引
数据库索引能够加速数据库查询,@Entity的indices
属性能够用于添加索引。在索引或者组合索引中列出你但愿包含的列的名称:
@Entity(indices = {@Index("name"), @Index({"first_name", "last_name"}),}) public class User {}
有时,表中的某个字段或字段组合须要确保惟一性,能够设置@Entity的@Index注解的unique属性为true。
@Entity(indices = {@Index(value = {"first_name", "last_name"}, unique = true)}) public class User {}
foreignKeys:外键约束
例如:若是有一个entity叫book,你能够经过使用@ForeignKey注解定义它和user的关系:
@Entity(foreignKeys = @ForeignKey(entity = User.class, parentColumns = "id", childColumns = "user_id")) class Book {}
外键是十分强大的,由于它们容许你指明当引用的entity被更新后作什么。
例如,若是相应的user实例被删除了,你能够经过包含@ForeignKey注解的onDelete=CASCADE
属性让SQLite为这个user删除全部的书籍。SQLite处理@Insert(OnConflict=REPLACE)做为一个REMOVE和REPLACE操做而不是单独的UPDATE操做。这个替换冲突值的方法可以影响你的外键约束。
Embedded:嵌入式字段
有时,但愿entity中包含一个具备多个字段的对象
做为字段。在这种状况下,可使用@Embedded
注解去表明一个但愿分解成一个表中的次级字段的对象。接着你就能够像其余单独的字段那样查询嵌入字段:
@Embedded public Address address;
class Address { @ColumnInfo(name = "post_code") public int postCode; //嵌入式字段还能够包含其余嵌入式字段 }
若是一个实体具备相同类型的多个内嵌字段,则能够经过设置前缀属性(prefix)使每一个列保持唯一。而后将所提供的值添加到嵌入对象中每一个列名的开头
@Embedded(prefix = "foo_") Coordinates coordinates;
对象引用
SQLite是个关系型数据库,可以指明两个对象的关系。大多数ORM库支持entity对象引用其余的。Room明确的禁止这样。更多细节请参考Understand why Room doesn’t allow object references
Room的三大组件之一Dao,以一种干净的方式去访问数据库。
Room 不容许在主线程中访问数据库。除非在建造器中调用allowMainThreadQueries()
,这可能会形成长时间的锁住UI。
注意,异步查询API(返回LiveData或者RxJava流的查询)从这个规则中豁免,由于它们异步的在后台线程中进行查询。
在Dao中建立一个方法而且使用@Insert
注解它,Room会为其生成一个实现,此实现会在单独事务中插入全部参数到数据库中:
@Insert long insertUser(User user); //参数至少有一个,有一个时能够返回long(表明新插入item的rowId)或不返回 @Insert long[] insertAll(User... users); //参数也能够是集合或者数组,此时能够返回long[]或者List<Long>或不返回 @Insert(onConflict = OnConflictStrategy.REPLACE) void insertAll(List<User> users);//能够执行事务操做
long id = db.userDao().insertUser(new User("哎")); List<Long> ids = db.userDao().insertAll(new User("哎哎" + new Random().nextInt(100))); db.userDao().insertAll(Arrays.asList(new User("哎啊"), new User("啊哎")));
@Insert方法必须至少有一个参数,若是参数只有一个,它能够返回一个新插入item的rowId的long值(或不返回)
若是参数是一个集合或数组,它能够返回long[]
或者List<Long>
(或不返回)
@Insert,@Update均可以执行事务操做,定义在OnConflictStrategy
注解类中:
@Retention(SOURCE) public @interface OnConflictStrategy { int REPLACE = 1; //替换旧数据 int ROLLBACK = 2; //回滚事务 int ABORT = 3; //就退出事务 int FAIL = 4; //使事务失败 int IGNORE = 5; //忽略冲突 }
@Delete是一个从数据库中删除一系列给定参数的entities的惯例方法。它使用主键
找到要删除的entities:
@Delete int delete(User user);//根据主键删除entities,能够没有返回值或者返回int,返回int值时表示删除的数量 @Delete int deleteAll(List<User> users); //参数至少有一个,也能够是集合或者数组,参数必须是使用@Entity注解标注的类,且不能为null
User user = db.userDao().findUserByName("啊哎"); //若是没找到的话返回null if (user != null) Log.i("bqt", "【delete数量】" + db.userDao().delete(user)); //不能传入空,不然崩溃 Log.i("bqt", "【deleteAll数量】" + db.userDao().deleteAll(db.userDao().getAll()));
尽管一般不是必须的,你可以拥有这个方法返回int值指示数据库中删除的数量。
没有提供根据主键等方式删除(包括更新)数据的方式,参数必须是使用@Entity注解标注的类,不能是字符串!
@Update
是更新一系列entities集合、给定参数的惯例方法。它使用query来匹配每一个entity的主键:
@Update int updateAll(User... users);//规则和delete同样
尽管一般不是必须的,你可以拥有这个方法返回int值指示数据库中更新的数量。
@Query 是DAO类中使用的主要注解,每一个@Query方法在编译时
(而不是运行时)被校验,若是查询包含语法错误,或者若是用户表不存在
,Room会报出合适的错误消息:
@Query("SELECT * FROM table_user") List<User> getAll();//返回值为集合或数组的话,返回找到的全部数据,没找到时返回长度为0的集合或数组
查询时传入参数
若是你须要传入参数到查询语句中去过滤操做,你可使用方法参数
:
@Query("SELECT * FROM user WHERE age > :minAge") User[] loadAllUsersOlderThan(int minAge); @Query("SELECT * FROM user WHERE uid IN (:userIds)") List<User> loadAllByIds(int[] userIds);
当这个查询在编译期被处理,Room会匹配:minAge
绑定的方法参数。Room经过使用参数名称
执行匹配,若是没有匹配到,在你的app编译期将会报错。
查询时能够传入多个参数或者屡次引用同一个参数。
@Query("SELECT * FROM user WHERE first_name LIKE :first AND last_name LIKE :last LIMIT 1") User findByName(String first, String last);
返回列中的子集
多数时候,咱们仅须要获取一个entity中的部分字段。例如,你的UI可能只展现user第一个和最后一个名称,而不是全部关于用户的细节。经过获取展现在UI的有效数据列可使查询完成的更快
。
只要查询中列结果集可以被映射到返回的对象中,Room容许你返回任何java对象
。例如,经过建立以下POJO拿取用户的姓和名:
@Query("SELECT first_name, last_name FROM user") List<NameTuple> loadFullName();
public class NameTuple { @ColumnInfo(name="first_name") public String firstName; @ColumnInfo(name="last_name") public String lastName; }
Room理解查询返回first_name和last_name的列值被映射到NameTuple类中。所以,Room可以生成合适的代码。若是查询返回太多columns,或者一个列不存在,Room将会报警。
返回LiveData对象
使用返回值类型为LiveData实现数据库更新时ui数据自动更新:
db.userDao().loadUsersByName2("%哎%") //能够在主线程调用 .observe(this, users -> tvTitle.setText("【最新查询结果】" + new Gson().toJson(users)));
返回RxJava对象
Room也能返回RxJava2的Publisher和Flowable对象
,需添加android.arch.persistence.room:rxjava2
依赖:
@Query("SELECT * from user where uid = :uid") Flowable<User> findUserById(int uid);
直接游标访问
若是你的应用逻辑直接访问返回的行,你能够从你的查询当中返回一个Cursor对象:
@Query("SELECT * FROM user WHERE uid > :uid LIMIT 5") Cursor biggerId(int uid);//不推荐
注意:很是不建议使用Cursor API 由于它不能保证行是否存在或者行包含什么值。
使用这个功能仅仅是由于你已经有一个返回cursor的代码,而且你不能轻易的重构。
多表查询
在SQLite数据库中,咱们能够指定对象之间的关系,所以咱们能够将一个或多个对象与一个或多个其余对象绑定。这就是所谓的一对多和多对多的关系。
一些查询可能访问多个表去查询结果,Room容许你写任何查询,因此你也能链接表。
以下代码段展现如何执行一个根据借书人姓名
模糊查询借的书
的相关信息。
@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") List<Book> findBooksBorrowedByNameSync(String userName);
从这些查询当中也能返回POJOs,例如,能够写一个POJO去装载userName和petName,以下:
@Query("SELECT user.name AS userName, pet.name AS petName FROM user, pet WHERE user.id = pet.user_id") LiveData<List<UserPet>> loadUserAndPetNames();
class UserPet { public String userName; public String petName; }
Room为原始类型
和可选的装箱类型
提供嵌入支持。然而,有时你可能使用一个单独存入数据库的自定义数据类型
。为了添加这种类型的支持,你能够提供一个TypeConverter
把自定义类转化为一个Room可以持久化的已知类型。
例如:若是咱们想持久化Date的实例,咱们能够写以下TypeConverter在数据库中去存储相等的Unix时间戳:
public Date birthday;
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(); } }
接着,你增长@TypeConverters
注解到RoomDatabase
子类:
@Database(entities = {User.java}, version = 1) @TypeConverters({Converter.class}) public abstract class AppDatabase extends RoomDatabase { public abstract UserDao userDao(); }
使用这些转换器后,你就能够像使用的原始类型同样使用自定义类型了:
@Query("SELECT * FROM user WHERE birthday BETWEEN :from AND :to") List<User> findUsersBornBetweenDates(Date from, Date to);
注意:除了将 @TypeConverters 放在RoomDatabase里面影响全局外,还能够将 @TypeConverter 限制在不一样的范围内,包含单独的entity、Dao和Dao中的 methods,具体查看官方文档。
一般,咱们使用SQL语句模糊查询时是这样的:
select * from user where name like '%包_天%'; //%表明任意个字符,_表明一个字符
可是在Dao中不能简单地这么写,任你各类拼接啊、转义啊,都不能把那个%
给它弄进去。
这里不得不说一个Room的一个好处,就是你在写sql语句的时候它会检查,格式不对,里面会报红线,根本就编译不经过。
皇天不负有心人,最终在Stack Overflow上面搜了一下,找到了最终答案,应该是这样的
@Query("SELECT * from user where last_name LIKE '%' || :name || '%'") LiveData<List<User>> loadUsersByName(String name);
原来是用双竖杠去拼接,而不是加号,欲哭无泪啊。而后我就去搜了一下sql语句拼接,找到这样一段话:
在SQL中的SELECT语句中,可以使用一个特殊的操做符来拼接两个列。根据你所使用的DBMS,此操做符可用加号(+)或两个竖杠(||)表示。
在MySQL和MariaDB中,必须使用特殊的函数。
Access和SQL Server使用 + 号。
SQLite、DB二、Oracle、PostgreSQL、Open Office Base使用 ||。
其实除了我上面用双竖杠拼接的方式外,还有一种方法,就是在传参的时候,把%_%
给拼接好,这种方式我也是测试了一下,也是能够的,由于最终都是转换成sql的标准查询语句。
db.userDao().loadUsersByName("%包_天%")
并且,我感受这种方式写起来更优雅,应该也是Google变相推荐的方式。
总结
db.userDao().loadUsersByName("%包_天%")
方法参数
先后使用||
拼接,如:@Query("SELECT * from user where name LIKE '_%' || :name || '%'")
在编译时,将数据库的模式信息导出到JSON文件中,这样可有利于咱们更好的调试和排错(DataBase的exportSchema = true)
module中的build.gradle:
android { defaultConfig { javaCompileOptions { annotationProcessorOptions { arguments = ["room.schemaLocation": "$projectDir/schemas".toString()]//room的数据库概要、记录 } } } }
配置完毕以后,咱们在构建项目以后会在对应路径生成schemas文件夹。
文件夹内的1.json
等文件分别是数据库版本一、二、3等版本对应的概要、记录。
若是我们删除了entity中的一个字段,运行程序后,就会出现下面这个问题。
java.lang.IllegalStateException: Room cannot verify the data integrity. Looks like you've changed schema but forgot to update the version number. You can simply fix this by increasing the version number.
大体的意思是:你修改了数据库,可是没有升级数据库的版本
这时候我们根据错误提示增长版本号,但没有提供migration,APP同样会crash。
java.lang.IllegalStateException: A migration from 1 to 2 was required but not found. Please provide the necessary Migration path via
RoomDatabase.Builder.addMigration(Migration ...)
or allow for destructive migrations via one of theRoomDatabase.Builder.fallbackToDestructiveMigration*
methods.
大体的意思是:让咱们添加一个addMigration或者调用fallbackToDestructiveMigration完成迁移
接下来,我们增长版本号并使用fallbackToDestructiveMigration()
,虽然可使用了,可是咱们会发现,数据库的内容都被咱们清空了,显然这种方式是不友好的。若是不想清空数据库,就须要提供一个实现了的migration。
Room容许使用
Migration
类保留用户数据。每一个Migration类在运行时指明一个开始版本和一个结束版本,Room执行每一个Migration类的migrate()
方法,使用正确的顺序去迁移数据库到一个最近版本。
若是不提供必需的Migrations类,Room会重建数据库,这意味着你将丢失数据库中的全部数据。
好比我们要在Department中添加phoneNum
public class Department { @PrimaryKey(autoGenerate = true) int id; String dept; @ColumnInfo(name = "emp_id") int empId; @ColumnInfo(name = "phone_num") String phoneNum; }
一、把版本号自增
@Database(entities = {Department.class, Company.class}, version = 2, exportSchema = false) @TypeConverters(DateConverter.class) public abstract class DepartmentDatabase extends RoomDatabase {}
二、添加一个version:1->2的migration
static final Migration MIGRATION_1_2 = new Migration(1, 2) { @Override public void migrate(SupportSQLiteDatabase database) { database.execSQL("ALTER TABLE department ADD COLUMN phone_num TEXT"); } };
三、把migration 添加到 databaseBuilder
Room.databaseBuilder(context, DepartmentDatabase.class, DB_NAME) .addMigrations(MIGRATION_1_2) .build();
再次运行APP就会发现,数据库表更新了,而且旧数据也保留了。
在平时的开发时,数据库的升级并不老是循序渐进的从 version: 1->2,2->3,3->4。老是会出现 version:1->3,或 2->4 的状况。这时候咱们又该怎么办呢?
方法很简单。当用户升级 APP 时,咱们替用户升级数据库版本。
具体作法:
version:1->2
database.execSQL("CREATE TABLE `Fruit` (`id` INTEGER, `name` TEXT, PRIMARY KEY(`id`))"); //建立表
version:2->3
database.execSQL("ALTER TABLE Book ADD COLUMN pub_year INTEGER"); //添加字段
version:3->4
//字段类型修改 db.execSQL("CREATE TABLE db_new (_id TEXT, _name TEXT, _phone INTEGER, PRIMARY KEY(_id))"); //建立表 db.execSQL("INSERT INTO db_new (_id, _name, _phone) SELECT _id, _name, _phone FROM db_old"); //复制表 db.execSQL("DROP TABLE db_old"); //删除表 db.execSQL("ALTER TABLE db_new RENAME TO students"); //修改表名称
而后把 migration 添加到 Room database builder:
Room.databaseBuilder(getApplicationContext(), AppDatabase.class, "database-name") .addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4) .build();
这样,要是用户刚下载的 APP,目前咱们定义了migrations:version 1 到 2, version 2 到 3, version 3 到 4, 因此 Room 会一个接一个的触发全部 migration。
其实 Room 能够处理大于 1 的版本增量:咱们能够一次性定义一个从 1 到 4
的 migration,以提高迁移的速度。
.addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4, MIGRATION_1_4)
implementation "android.arch.persistence.room:runtime:1.1.1" //Room annotationProcessor "android.arch.persistence.room:compiler:1.1.1" testImplementation "android.arch.persistence.room:testing:1.1.1" //Test helpers for Room implementation "android.arch.persistence.room:rxjava2:1.1.1" //为room添加rxjava支持库 implementation "android.arch.lifecycle:reactivestreams:1.1.1" //和LiveData一块儿使用
@Database(entities = {User.class}, version = 1) @TypeConverters({MyConverters.class}) public abstract class AppDatabase extends RoomDatabase { public abstract UserDao userDao(); //没有参数的抽象方法,返回值所表明的类必须用@Dao注解 }
Build后会自动生成的AppDatabase的实现类:
@Entity(tableName = "table_user") //自定义表名称 public class User { @PrimaryKey(autoGenerate = true) public int uid;//自增主键 @ColumnInfo(name = "user_name") public String name; //自定义列名称 public Date birthday; //类型转换 @Ignore public String tips = "忽略的字段"; //忽略 //省略了get、set、构造方法等代码 }
@Dao public interface UserDao { @Insert long insertUser(User user); //参数至少有一个,有一个时能够返回long(表明新插入item的rowId)或不返回 @Insert List<Long> insertAll(User... users); //参数也能够是集合或者数组,此时能够返回long[]或者List<Long>或不返回 @Insert(onConflict = OnConflictStrategy.REPLACE) void insertAll(List<User> users);//能够执行事务操做 @Delete int delete(User user);//根据主键删除entities,能够没有返回值或者返回int,返回int值时表示删除的数量 @Delete int deleteAll(List<User> users); //参数至少有一个,也能够是集合或者数组,参数必须是使用@Entity注解标注的类,且不能为null @Query("SELECT * FROM table_user") List<User> getAll();//返回值为集合或数组的话,返回找到的全部数据,没找到时返回长度为0的集合或数组 @Query("SELECT * FROM table_user WHERE user_name LIKE :name LIMIT 1") User findUserByName(String name); //返回值为User的话,只返回找到的第一条数据,没找到时返回null @Query("SELECT * FROM table_user WHERE uid IN (:userIds)") User[] loadAllByIds(int[] userIds); //没找到时返回长度为0的集合或数组 @Query("SELECT * FROM table_user WHERE uid > :uid LIMIT 5") Cursor biggerId(int uid);//不推荐 @Query("SELECT * from table_user where uid = :uid") Flowable<User> findUserById(int uid); //和rxjava结合使用 @Query("SELECT * from table_user where user_name LIKE :name ") LiveData<List<User>> loadUsersByName(String name); //和LiveData结合使用 @Query("SELECT * from table_user where user_name LIKE '_%' || :name || '%'") LiveData<List<User>> loadUsers(String name);//模糊搜索 }
Build后自动生成的UserDao的实现类:
public class MainActivity extends FragmentActivity { private TextView tvTitle, tvWeixin, tvFriend, tvContact, tvSetting; private String[] titles = {"微信", "通信录", "发现", "我"}; private MyViewModel mModel; private AppDatabase db; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); mModel = ViewModelProviders.of(this).get(MyViewModel.class); db = Room.databaseBuilder(getApplicationContext(), AppDatabase.class, "dbname").build(); db.userDao().loadUsersByName("%哎_%") //返回LiveData<List<User>>,实际数据库操做工做在子线程 .observe(this, users -> { Log.i("bqt", "【是否在主线程中】" + (Looper.getMainLooper() == Looper.myLooper())); //true String newUsers = GsonUtils.toJson(users); Log.i("bqt", "【数据库中查询到的数据发生变化】" + newUsers); mModel.getMutableLiveData().setValue(newUsers); //这里是主线程,能够直接使用setValue }); } private void initView() { tvWeixin = findViewById(R.id.tv_tab_bottom_weixin); tvFriend = findViewById(R.id.tv_tab_bottom_friend); tvContact = findViewById(R.id.tv_tab_bottom_contact); tvSetting = findViewById(R.id.tv_tab_bottom_setting); tvWeixin.setTag(0); tvFriend.setTag(1); tvContact.setTag(2); tvSetting.setTag(3); tvWeixin.setOnClickListener(v -> onClickTab((Integer) v.getTag())); tvFriend.setOnClickListener(v -> onClickTab((Integer) v.getTag())); tvContact.setOnClickListener(v -> onClickTab((Integer) v.getTag())); tvSetting.setOnClickListener(v -> onClickTab((Integer) v.getTag())); tvTitle = findViewById(R.id.tv_title); tvTitle.setOnClickListener(v -> { new Thread(() -> { List<User> allUsers = db.userDao().getAll();//必须在子线程访问数据库 mModel.getMutableLiveData().postValue("所有数据\n" + GsonUtils.toJson(allUsers)); //在子线程必须用postValue }).start(); tvTitle.setBackgroundColor(0xFF000000 + new Random().nextInt(0xFFFFFF)); }); getSupportFragmentManager().beginTransaction().add(R.id.id_container, MyFragment.newInstance("MyFragment")).commit(); } private void onClickTab(int position) { tvWeixin.setSelected(position == 0); tvFriend.setSelected(position == 1); tvContact.setSelected(position == 2); tvSetting.setSelected(position == 3); tvTitle.setText(titles[position]); switch (position) { case 0: new Thread(() -> { long id = db.userDao().insertUser(new User("哎")); List<Long> ids = db.userDao().insertAll(new User("哎哎" + new Random().nextInt(100))); db.userDao().insertAll(Arrays.asList(new User("哎啊"), new User("啊哎"))); Log.i("bqt", "【id】" + id + "【ids】" + ids); }).start(); //不能在主线程访问数据库,不然报IllegalStateException break; case 1: new Thread(() -> { User user = db.userDao().findUserByName("啊哎"); //若是没找到的话返回null if (user != null) Log.i("bqt", "【delete数量】" + db.userDao().delete(user)); //不能传入空,不然崩溃 User[] users = db.userDao().loadAllByIds(new int[]{1, 2, 3, 4, 5, 6}); //没找到时返回长度为0的集合或数组 Log.i("bqt", "【deleteAll数量】" + db.userDao().deleteAll(Arrays.asList(users))); }).start(); break; case 2: new Thread(() -> { User user = db.userDao().findUserByName("哎%"); //若是没找到的话返回null user.name = "更新name"; Log.i("bqt", "【updateAll数量】" + db.userDao().updateAll(user)); }).start(); break; case 3: db.userDao().findUserById(1) //返回Flowable<User> .map(GsonUtils::toJson) //数组类型转换 .subscribeOn(Schedulers.io()) //必须切换到子线程中访问数据库 //.observeOn(AndroidSchedulers.mainThread()) //能够在子线程中使用postValue,也能够切换到主线程中使用setValue .subscribe(s -> mModel.getMutableLiveData().postValue(s)); default: break; } } }
2019-3-29