数据库ROOM-Google新推

来源:互联网 发布:oracle是什么软件 编辑:程序博客网 时间:2024/05/17 08:32

转自 http://www.jianshu.com/p/bb8bd5bc8304

前言

        今年,谷歌新推了不少东西,在看其新架构Architecture Components(AC架构是一个类似MVVM的架构,7.0之前,Google公布过不少MVP架构的官方Demo,看来现在其也是与时俱进了)的相关文章时,注意到一个新的东西_ROOM数据库,这是一个SQLITE类型数据库,用法和Realm类似的地方,便是通过注解来控制实体和操作数据库, 虽然项目很少用到数据库,缓存文件相对来说更简便,不过作为知识储备来说还是需要的,对于新技术我认为多多尝试并不是什么坏事。

        


GitHub链接

ROOM抽象数据库应用Demo------RoomsDemo


使用流程

创建一个数据库,并创建一张animal表和受animal表外键约束的food表作为例子

1.开发环境

JAVA1.8

Android Studio 3.0 Canary3

Gradle 4.0-milestone-1

2.app的build.gradle中依赖导入以下库文件,Rxjava2不是必需的.

1-gradle

3.创建三个文件,分成Entity,Dao,DataBase三类

下图是创建数据库和animal表所需的文件

2-files

(1)Entity文件:

实体类,一个实体类可以是一张表.

Animal类

@Entity(tableName = 'animal') ,    必须

在Animal类前添加该注解,app运行时变会生成一张table,表名默认为类名,也可以通过属性tableName手动设置表名.

@PrimaryKey(autoGenerate=true),必须

在成员变量前添加该注解,可以设置animal表的主键,属性autoGenerate = true即主键会自动增长,即使实体类只有一个成员属性,也必须声明主键.

@ColumnInfo(name = "sname") 

在成员变量前添加该注解,可以手动设置字段名,默认字段名即为变量名.

@Ignore        

在成员属性前添加该注解,可以设置不想存入animal表中的成员变量.

@Embedded

SQLITE数据库是没有Country类型的,所以该注解可以将嵌套在Animal类中的Country类存入到animal表中,下图中,animal表会同时拥有Country类的字段 (cid 和 cname).

如果Animal类中嵌套了多个Country类,需要通过设置属性prefix为不同的值,保证这些Country类的字段在animal表中的字段名都是唯一的.                           

3-animal

Country类

Country的成员变量名是可以和Animal的成员变量名相同的,这个并不会冲突,另外可以看到Country类并不需要额外创建一个相应的country表,因为animal表只是包含了Country类的字段而不是其本身.

4-country

Food类

@Entity(foreignKeys = @ForeignKey(entity = Animal.class,parentColumns = "uid"

* ,childColumns = "aid",onDelete = CASCADE))

foreignkeys属性可以给food表添加外键约束,下图中即food表中的aid字段受aniaml表中的aid字段约束.

@ForeignKey()中,entity即为Food的外键约束类,parentColumns 为外键约束类的主键,childColums 为外键

onDelete = CASCADE属性,添加该属性后,当animal表中删除一条数据时,food表中的与之aid相同的数据也会被同时删除.

5 -foods

Pojo类

animal表中的字段查询出来后,可以返回任何包含该字段的类,不一定需要指定为Animal类,比如我们可以查询animal表中的aid字段和place字段,然后构建成Pojo类来返回,但需要@ColumnInfo注解的帮助.

6-Pojo

至此,创建表所需的实体类和添加外键约束都已经完成.

(2)Dao文件

interface接口,主要是负责各种查询的方法,这个需要了解SQLite语句的格式。

AnimalDao

@Dao 必须

该注解会将interface接口声明为Dao文件,不过查询的表并不是需要指定对应的,demo里面只是为了区分两个表创建了两个Dao文件.

@Insert 

插入注解,方法参数只能是一个数据类型为Entity类的可变数组(下图即为Animal和Food),根据传入的参数,返回long或long[],表示插入的位置,@Insert注解有个onConflict属性,可以细分插入是更新一行数据还是替换整行数据.

@Delete

删除注解,同上.

@Update

更新注解,同上.

@Query

查询注解,需要在括号内添加SQLite语句,SQLite语句有误的话,Studio在编译阶段就会报错.

查询方法的返回值可以为Array或者List,导入Rxjava2后,还可以返回Flowable<Array/List>

query

下图是示例的操作语句.

多表查询和自定义类型参数(需要用到类型转换TypeConverter)可以参考网上大侠的文章,现在相关文章很少,这里不作示例.

animal表

7-animalDao

food表

8-fooddao

(3)DataBase文件

@Database(entities = {Animal.class,Food.class},version = 1)      必须

该注解可以将继承RoomDatabase类的抽象类作Database标记

entities属性,需要生成dao的表,

version属性, 指定生成的数据库版本.

9-database

4.在app初始化时通过Database文件生成相应的Dao文件 ,Database文件生成比较耗费性能,最好做成单例模式,数据库名字即"database-name",不同的name可以生成多个数据库.

10 - dao

5.然后我们就可通过自己定义的到来执行animal和food的增删查改操作了,不过在ROOM中,这些操作会强制在非主线程执行,否则Studio同样在编译阶段就会报错.

下图为animal表插入操作的示例,图中使用的Rxjava2.0,注意onSubscribe(Subscription s){}方法返回的Subscription需要用集合收集起来,在页面销毁时手动调用其cancel()方法,否则容易引起内存泄露.

11-insert

4.下面我们来看下animal表和food表设置的外键约束关系是否生效

(1)先在animal表插入两条数据,可以看到食物一项是没有数据的.

12-animal

(2)然后,在food表中插入熊猫和白头海雕对应的食物

13-food

,再返回animal表,可以看到食物已经加进去了.

13-animal2

(3)最后,只删除animal表中的熊猫,

14 -animal3

再进去查询food表,可以看到熊猫关联的竹子也已经被删除。

15-food2

5.最后,在不需要再使用数据库时,记得关闭数据库

16-database_close

6.数据库升级,app版本更新时,如果需要保留上一个版本的数据库或者新增数据库字段,需要用到Migration类

下图中注释的部分,示例了app版本升级时迁移数据库和更新数据库的方式

17-migration


注意事项

        如果需要用到Migration类来升级数据库,需要注意

1.清单文件中需要声明versionCode属性,否则Studio在run阶段会报错.

18-manifest

2.app版本更新后,Database文件也需要更新对应的version.

18-database

3.和Realm对比                                                                                                                                           (1)Realm需要类强制继承RealmObject或者实现RealmModel接口,并添加@RealmClass注解, ROOM则只需要一个类上@Entity注解;

(2)Realm的类是可以不设置主键的,ROOM则必需显式的用@PrimaryKey注解声明一个主键;

(3)Realm的增删查改操作可以在主线程执行,ROOM则需要开辟子线程,否则后者在编译阶段时就会报错;

(4)Realm中,类的成员变量,除String外的引用类型,同样需要继承RealmObject或者实现RealmModel接口,集合类型需要使用RealmList代替,而ROOM中则不能直接存储集合类型;

(5)DataBinding中,如果要实现双向绑定(比如熟知的MVVM),类需要继承自BaseObservable,此时使用Realm数据库会多一些限制;

(6)速度上ROOM和其他ORM数据库差异不会太多,而Realm的速度则是毋容置疑的,不过如果一张表格的数据量非常庞大,那么Reaml先整张读取,再筛选的特点也就成了缺点。

(7)以'立志'取代SQLITE为出发点的Realm也有很多新特点,比如支持内存级别的持久化,支持以JSON和IO流的方式的存储,支持多线程间数据的同步和通知等.

总之,Realm胜在速率,同步和操作的简易性上,但对项目的侵入性较强,不管是引入或去除,对老项目都不够友好,ROOM则更有成熟的体系和更可靠的稳定性,引入或去除相对简便,开发时,可以根据两者的差异和实际项目需求作取舍。

Demo写的比较简单,如果想对ROOM有更深入的了解,可以去看看网上大虾们的翻译,个人认为ROOM对SQLite的做了一层抽象封装后,和以前那样的用法比起来,已经大大的简化了,还是值得事先了解的。

作者:lzt橘子链接:http://www.jianshu.com/p/bb8bd5bc8304來源:简书著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。