从架构设计角度分析AAC源码-Room注解使用大全(基于2.4版本源码解析)(一)

前言

本篇单纯讲解Room源码,和前后篇章没有关系。

当前Room注解大全是基于Room2.4版本代码逻辑得来,如果有问题欢迎给予指正。当前Room源码学习目的:(1)主要原因:学习jetpack代码;(2)次要原因:网上看了很多相关Room注解使用,非常肤浅(可能个人眼界有限,没有找到真正比较全面的注解),所以想自己看代码去全面理解一下room注解;

之前有了dagger2(基于2.38.1版本)注解理解基础,所以这里相对来说理解起来较简单,也是相当来说而已,代码看起来还是头疼得很!!!

前提

理解Room注解前提条件:

必须!!!是必须理解sql数据库相关操作!!!如果想全面理解当前Room,那么不但对数据库基本操作熟悉,对其他如视图、索引也要有一定了解,这样才会事半功倍。

以下讲解围绕数据库创建数据库操作两部分讲解:

  1. 数据库创建
  • 1.1 通过Room注解实现数据库创建的规则
  • 1.2 如何通过Room注解创建数据库
  1. 操作数据库
  • 2.1 通过Room注解操作数据库规则
  • 2.2 Room注解如何操作数据库

还会细分,创建数据库有表、视图、关系、表字段等;操作数据库又分为对数据库增删改查,还有事务等。

下面根据数据库创建和操作来对Room注解一一解析,并且按照常用在前,不常用在后原则慢慢品味源码。

术语解释

这里是我自己命名的一些词语。相当于一个给下面的Room注解有些名词进行解释,这里先不需要看,等看到下面具体的名词的时候回到这里来看。

  1. 表字段:表或视图的字段;

  2. xx节点:表示使用@Xx注解修饰的节点,e.g.@Entity修饰的节点表示entity节点;

  3. fts节点:@Fts3或@Fts4修饰的 entity节点;

  4. pojo节点:下面有一个篇章专门介绍pojo对象的生成,表示能生成Pojo对象的节点;

  • pojo节点有效方法:非private修饰 && 非abstract修饰 && 非@Ignore修饰;

  • pojo节点setter方法:有且仅有一个参数,返回类型是void;

  • pojo节点getter方法:pojo节点有效方法中,无参,返回类型不是void;

  1. 表有效字段:pojo节点中所有字段(包括父级类中的字段),并且该字段满足 : 没有被@Ignore修饰 && 不是static修饰 && (没有使用transient修饰 || (使用了@ColumnInfo或@Embedded或Relation修饰));

  2. 表常规字段:表有效字段中使用@ColumnInfo修饰 或 没有使用transient修饰的表字段;如果当前字段表示的节点使用@Entity修饰,那么不存在于@Entity#ignoredColumns中;

  3. 表关系字段:表有效字段中使用@Relation修饰;

  4. 表嵌入字段:表有效字段中使用@Embedded修饰;如果当前字段表示的节点使用@Entity修饰,那么不存在于@Entity#ignoredColumns中;

  5. 嵌入表:表有效字段中使用@Embedded修饰的字段对象生成的表;

  6. 关系表:表有效字段中使用@Relation修饰的字段对象生成的表;

Room注解规则大全

感觉名字是不是有点大!!!可能有那么一点,但是我把源码中关于Room注解的逻辑全部提炼出来也就那么回事儿。

来来回回一遍遍去理解,花的都是心血啊,如果对读者有帮助,必须给个star

1. Room注解创建数据库规则

一切都是从@Database注解说起,使用当前注解拉开Room注解篇章,从此踏上不归路,呸呸呸…从此走上人生癫疯!!!嗯,癫疯!

前言:Pojo对象生成规则

本篇核心规则之一:必须先理解,而且这么去写。

用于创建Pojo的节点:

情况一:数据库创建过程中生成Pojo对象:

  1. entity节点:@Entity修饰的节点;

  2. fts节点:@Fts3或@Fts3修饰的节点同时使用@Entity修饰;

  3. databaseView节点:@DatabaseView修饰的节点;

  4. embedded修饰的节点类型:@Embedded修饰的节点类型,该表示的对象节点;

情况二:数据库操作过程中创建Pojo对象:

生成Pojo对象规则:

  1. pojo节点生成pojo对象过程中,有可能pojo节点中的子节点又生成一个子pojo对象,正确情况下(肯定存在非正常情况,感兴趣自己看源码)不能出现pojo对象循环引用,e.g.该子pojo节点类型就是该父级pojo节点类型;

  2. 正常情况下,pojo节点的所有方法不允许使用@PrimaryKey, @ColumnInfo,@Embedded, @Relation修饰;但是如果pojo节点同时使用@AutoValue修饰,规则如下:

  • (1)pojo节点只允许存在无参abstract修饰的方法,并且允许使用@PrimaryKey, @ColumnInfo,@Embedded, @Relation修饰;
  • (2)pojo节点的方法如果使用了androidx.room包下的注解,那么该方法最好使用@CopyAnnotations注解,否则会提示警告;
  1. 如果pojo节点是entity节点,那 @Entity#ignoredColumns属性表示被忽略的字段,被忽略的字段只允许是表常规字段和表嵌入字段;

  2. 表常规字段和嵌入表常规字段不允许重复;

  3. pojo节点构造函数,筛选条件:

  • (1)pojo节点(没有使用@AutoValue修饰)构造函数条件:当前构造函数没有被@Ignore修饰 || 当前构造函数不是private修饰;如果pojo节点被@AutoValue修饰,那么 : 方法没有被@Ignore修饰 && 方法没有被private修饰 && 方法是static修饰 && 方法返回类型是当前类类型;

  • (2)构造函数参数筛选:

  • ① 如果pojo节点的构造函数参数是当前pojo生成的表常规字段、表嵌入字段或表关系字段,则没有问题;

  • ② 如果不满足条件①,那么:要么表常规字段、表嵌入字段或表关系字段都不存在;要么有且仅有一个表字段(常规、关系或嵌入中的一个),也就说该情况下允许构造函数参数不匹配表字段情况;否则都报错;

  • (3)如果经过以上筛选存在一个构造函数,并且该构造函数要有参数;如果存在多个构造函数,是kotlin语言直接返回主构造函数,不存在主构造函数,存在参数为空的构造函数则警告;多个构造函数筛选出第一个即可

  1. 校验pojo节点的常规字段的setter和getter方法:
  • 除了pojo节点常规字段,还有嵌入表的常规字段、关系表的常规字段和嵌入表中的关系表的常规字段

  • (1)如果pojo节点中的变量存在于构造函数中,那么不需要校验setter方法;否则按照如下顺序一步步校验;

  • (2)变量如果是public修饰,不需要校验setter和getter方法;

  • (3)如果setter和getter方法不是public修饰,那么当前变量不是private修饰,也表示校验成功;

  • (4)setter和getter方法分别最多只能存在一个;如果存在多个报错;

  • (5)除了以上情况,其他情况报错;

对象属性:

  1. element:用于创建Pojo的节点;如果节点同时使用@AutovAlue表示新生成的节点:Auto_原先节点;

  2. type:用于创建Pojo的节点类型;如果节点同时使用@AutovAlue表示新生成的节点类型:Auto_原先节点类型;

  3. fields:表常规字段 + 嵌入表常规字段;

  4. embeddedFields:pojo节点中的embedded节点生成的对象;

  5. relations:pojo节点中的relation节点生成的对象;

  6. constructor:pojo节点构造函数。

1.0 创建数据库文件规则

使用@Database注解的节点,表示生成一个数据库文件。

  1. 一个项目中@Database注解可以存着多个,表示生成多个数据库文件;

  2. @Database#entities属性表示在当前数据库文件中创建表信息:必须存在,属性值类型对象,必须使用@Entity修饰;

  3. @Database#views表示创建视图信息:属性值类型对象必须使用@DatabaseView修饰;

  4. @Database修饰的类必须继承androidx.room.RoomDatabase类;

  5. 一定要确保每个数据库文件中的@Database#entities和@Database#views中的表和视图名称不重复;

  6. @Database修饰的类必须是abstract抽象类,其abstract(非RoomDatabase继承过来的)抽象方法:

  • (1)不能使用@JvmName修饰,否则警告;
  • (2)当前方法返回类型对象必须被@Dao修饰,表示对该数据库文件的操作;

1.1 普通表规则

@Entity注解修饰的类表示数据库中的表信息。

  1. 表名:使用@Entity#tableName;如果不存在,使用@Entity修饰的类名;
  • (1)一个数据库文件中的表名唯一;
  • (2)表名不允许使用“sqlite_”前缀;
  • (3)表名不允许使用 ` 或 " 特殊字符;
  1. @Entity#indices:索引;
  • (1) 嵌入表常规字段不要创建索引,否则警告
  • (2)当前索引字段如果是entity节点继承过来的,那么必须设置当前entity的@Entity#inheritSuperIndices = true;否则警告,并且该索引无效;
  1. @Entity#inheritSuperIndices:默认false;如果为true,表示当前@Entity修饰的类的父类如果也使用了@Entity修饰,那么继承其父类的@Entity#indices索引;

  2. @Entity#foreignKeys:表外键;

  3. entity节点的所有方法不允许使用@PrimaryKey、 @ColumnInfo、@Embedded和@Relation修饰;但是:如果entity节点同时使用了@AutoValue修饰,那么:

  • (1)entity节点下的方法允许使用@PrimaryKey、 @ColumnInfo、@Embedded和@Relation修饰;

  • (2)entity节点下的无参抽象方法(该类只允许存在无参抽象方法)如果使用了androidx.room包下的注解,那么最好无参抽象方法也是用@CopyAnnotations修饰,否则报警告;

  1. @Entity#ignoredColumns表示忽略的表字段;哪些表字段可以被忽略:①表常规字段;②表嵌入字段;

  2. @Entity#primaryKeys表示表的主键;

  3. @Entity修饰的节点可以生成pojo对象,所以需要按照参照生成pojo对象规则;

  4. @Entity修饰的类中不允许出现@Relation修饰的有效字段;

1.1.1 fts表规则

entity节点同时使用@Fts3或@Fts4修饰的节点,称之为fts表。

fts表属性和规则如下

  1. @Fts3#tokenizer:FTS3表中使用的标记器;

  2. @Fts3#tokenizerArgs:FTS3表中用于配置定义的标记器的可选参数;

  3. @Fts4#tokenizer:FTS4表中使用的标记器;

  4. @Fts4#tokenizerArgs:FTS4表中用于配置定义的标记器的可选参数;

  5. @Fts4#contentEntity:FTS4表映射表的外部内容实体将用作FTS表的内容;

  • (1)当前属性必须存在,并且是使用@Entity注解修饰的类;

  • (2)fts表中除了rowid主键和languageId(@Fts4#languageId)字段,其他字段必须存在于@Fts4#contentEntity中属性对象生成的表常规字段或嵌入表常规字段中;

  • (3)@Fts4#contentEntity中的对象必须存在于@Database#entities中

  1. @Fts4#languageId:FTS4表要用作“languageid”的字段;
  • (1)languageid字段必须存在于fts表常规字段或嵌入表常规字段中;
  • (2)languageid字段必须是int类型;
  1. @Fts4#matchInfo:Fts版本,这里只有FTS3和FTS4两个版本;

  2. @Fts4#notIndexed:FTS4表上不会被创建索引的表字段;

  • 该表字段必须存在于fts表常规字段或嵌入表常规字段中;
  1. @Fts4#prefix:FTS4表索引前缀;
  • 必须大于0
  1. @Fts4#order:FTS表的首选“rowid”顺序;

  2. 表名::@Entity#tableName属性值如果存在,则使用该属性值;否则使用@Entity修饰的类名;

  3. fts表不允许创建索引,不允许使用外键

  • 对于索引,代码中仅仅支出不允许使用@Entity#indices来使用索引;
  1. fts节点也可以生成pojo对象,所以fts节点必须满足pojo对象生成规则;

  2. fts表不允许存在表关系字段(@Relation修饰的有效变量);

  3. 影子表名:

  • (1)如果是@Fts3修饰,那么使用:表名 + “_content”;
  • (2)如果是@Fts4修饰,那么使用:@Fts4#contentEntity中
    的类型对象生成的表名;
  1. fts表必须存在主键是rowid的表字段,并且当前字段类型是int;主键有且仅有一个,(原则上主键可以有多个字段,但是必须保证第一个是rowid);

1.2 视图规则

@DatabaseView注解修饰的类表示数据库中视图信息。

视图如何使用:

  1. @Database#views;

  2. @Relation修饰的表关系对象使用@DatabaseView;

  3. @Relation#associateBy#value中的对象使用@DatabaseView,表示多对多关系,e.g.

      @Relation(
                   parentColumn = "playlistId",
                   entity = Song::class,
                   entityColumn = "songId",
                   associateBy = @Junction(
                          value = PlaylistSongXRef::class,
                           parentColumn = "pId",
                           entityColumn = "sId")
           )
    

注①:@Relation注解不能修饰@Entity修饰的类的有效字段;

注②:外键指向的必须是@Entity修饰的对象表;

视图属性和使用规则如下:

  1. @DatabaseView#vlue属性表示select查询:当前属性必须存在,并且是正确的select语句;

  2. @DatabaseView#viewName属性表示视图名称:如果当前属性不存在,则使用@DatabaseView修饰的节点名称作为视图名称;

  3. 视图名称不能使用"sqlite_"前缀;

  4. 视图节点可以用于床架pojo对象,所以必须满足生成pojo对象规则。

1.3 表字段规则

pojo节点中的变量用于生成表字段,有三种类型:表常规字段,表嵌入字段和表关系字段(看术语解释)。

1.3.1 表常规字段

表有效字段中使用@ColumnInfo修饰 或 没有使用transient修饰的表字段;如果当前字段表示的节点使用@Entity修饰,那么不存在于@Entity#ignoredColumns中

@ColumnInfo注解属性:

  1. @ColumnInfo#name作为表字段名称;

  2. @ColumnInfo#typeAffinity表示表字段偏向类型;

  3. @ColumnInfo#index表示表字段是否创建索引;默认false

  4. @ColumnInfo#collate表示当前表字段排序规则;

  5. @ColumnInfo#defaultValue表示当前表字段默认值;

表常规字段规则如下

  1. 表名:使用@ColumnInfo#name(非默认[field-name])属性值;如果不存在则直接使用当前变量名作为表名,还存在一种情况:
  • (1)如果当前表字段是嵌入表中的字段,那么表名 = @Embedded#prefix + 当前表名;
  • (2)表常规字段不允许使用 ` 或 " 特殊字符
  1. 表字段支持类型(重要)
  • 2.1 如果表字段类型是泛型,那么必须是实体泛型,例如List是正确的泛型格式,List< T>格式错误;

  • 2.2 表字段类型,根据当前变量类型确定生成的表字段类型,如果不存在于:

  • (1) TEXT-文本类型:

  • ① String类型;

  • (2) INTEGER-int类型:

  • ① int、short、byte、long和char四种基本类型;

  • ② Integer、Short、Byte和Long四种基本类型的包装类型;

  • ③ boolean或Boolean,当前源码会自动将其转换成int类型,true使用1表示,false使用0表示,所以boolean和Boolean生成INTEGER类型表字段

  • (3) REAL-长int类型:

  • ① float和double基本类型;

  • ② Float和Double基本类型的包装类型;

  • (4) BLOB-长文本类型:

  • ① byte[]

  • ② ByteBuffer类型

  • (5)使用@ColumnInfo修饰表常规字段,@ColumnInfo#typeAffinity表示当前表字段偏向类型,最好不设置

  • ① 设置了@ColumnInfo#typeAffinity,首先根据变量类型去匹配表字段类型,如果匹配成功,再根据匹配的表字段类型是否等于我们设置的表字段偏向类型,如果没有匹配上,则表示当前适配不成功,继续往下匹配;

  • ② 没有设置@ColumnInfo#typeAffinity,就不会存在(5)①中的再次匹配偏向类型的情况;

  • (6)如果变量类型不属于以上四种类型,那么还会通过在@TypeConverters修饰的节点实现类型转换(自行查看下面类型转换篇章),最终也是转换成以上四种类型;

  • (7)以上条件都不满足,还有一种特例,变量类型是枚举或UUID类型,有可能(是有可能,这种情况基本不可能)也会生成对应的类型;

  • (8)以上条件都不满足,那么肯定会报错;

1.3.2 表嵌入字段

表嵌入字段,@Embedded修饰的字段。

@Embedded#prefix属性,表示当前@Embedded修饰的变量对象中的变量生成表字段的需要添加的前缀。

规则如下

  1. @Embedded修饰的有效字段类型必须是类或接口;

  2. @Embedded修饰的有效字段类型,不能存在递归引用。e.g.@Entity修饰的节点和@Entity节点中@Embedded修饰的节点类型一致,则表示递归,肯定不被允许;

  3. 当前@Embedded修饰的节点类型会生成Pojo对象;

1.3.3 表关系字段

表关系字段,@Relation修饰的字段。

@Relation注解属性:

  1. @Relation#entity属性:存放对象类型必须使用@Entity修饰或@DatabaseView修饰;表示当前表或视图关联的表或视图对象;

  2. @Relation#parentColumn属性:当前表字段(一般是外键),用于关联@Relation#entity对象(一般关联的的是主键)信息;

  3. @Relation#entityColumn属性:存在于@Relation#entity对象对象中的表字段(一般是主键),该表字段被@Relation#parentColumn(一般是外键)关联;

  4. @Relation#associateBy属性:如果当前对象和@Relation#entity对象是多对多关系,则还需要使用当前属性关联;

  5. @Relation#projection属性:@Relation#entity对象的表或视图中提取需要的字段,如果为空,表示提取表或视图中的全部字段。

@Junction注解属性:

@Relation(
         parentColumn = "playlistId",
         entity = Song::class,
         entityColumn = "songId",
         associateBy = @Junction(
                  value = PlaylistSongXRef::class,
                  parentColumn = "pId",
                  entityColumn = "sId")
   )
   val songs: List

--------------------- 分割线:上下是两个不同类------------------

@Entity(primaryKeys = {"pId", "sId"})
public class PlaylistSongXRef {
     val pId: Int,
     val sId: Int
 }
  1. @Junction#parentColumn属性:对应的是@Relation#parentColumn字段, 如果@Junction#parentColumn没有设置,那么使用@Relation#parentColumn中的字段;

  2. @Junction#entityColumn属性:对应@Relation#entityColumn字段,如果@Junction#entityColumn没有设置,那么使用@Relation#entityColumn中的属性;

  3. @Junction#value属性:必须使用@Entity 或 @DatabaseView修饰的类,该类表示用于存储当前表主键和受关联表主键;

  • 如果是@Entity修饰,表示仙剑一个多对多关系表,那么当前表中的parentColumn字段和entityColumn字段最好创建索引,否则警告。

规则如下

  1. @Relation#parentColumn的属性值必须存在,而且必须包含在当前表常规字段或当前表的嵌入表常规字段中;

  2. 表关系字段类型,如果是集合,那么当前集合只允许是List或Set集合,并且只允许List< T>,List或Set< T>,Set

  3. 如果表关系类型是集合,T作为返回类型校验;否则返回类型校验直接校验表关系类型;返回类型是实际类型,e.g.String可以,?或者 T不可以;

  4. @Relation#entity中的属性必须是类或接口,作为entity关系节点;如果当前@Relation#entity属性不存在,使用当前@Relation修饰的节点作为entity关系节点;

  • entity关系节点必须使用@Entity或@DatabaseView修饰
  1. @Relation修饰的有效字段类型,不能存在递归引用。e.g.pojo节点和pojo节点中@Relation修饰的节点类型一致,则表示递归,肯定不被允许;

  2. @Relation#entityColumn必须存在,并且该字段存在于entity关系节点类型中的所有有效字段中;

1.3.3.1 多对多关系对象

如果关系对象是多对多关系,还需要使用@Junction注解,在@Relation#associateBy属性中设置。规则如下:

  1. @Relation#associateBy的属性@Junction,@Junction#value的属性值类型必须是@Entity 或 @DatabaseView修饰;

  2. parentColunm属性:在@Relation#associateBy属性中,如果@Junction#parentColumn存在,使用当前字段作为连接父级表的列属性;否则使用@Relation#parentColunm;

  3. entityColumn属性:在@Relation#associateBy属性中,如果@Junction#entityColumn存在,使用当前字段作为连接实体表的列属性;否则使用@Relation#entityColumn;

  4. parentColunm属性或entityColumn属性校验:

  • (1)字段必须存在于@Junction#value属性类型的表常规字段中;
  • (2) 如果当前@Junction#value的属性值类型是entity节点(表信息),那么当前表的主键和索引字段应该包含parentColunm属性和entityColumn属性,否则警告-因为会导致全表扫描,影响效率;
  1. @Relation#projection表示在关系表中获取哪些字段;如果不设置表示获取关系表全部信息;

1.3.4 索引字段

如何使用索引

  1. @ColumnInfo#index = true,表示当前表常规字段创建索引;

  2. @Entity#indices,表示当前表中创建索引的表字段;

  3. @Entity#inheritSuperIndices = true,并且@Entity修饰的父类也是用了@Entity修饰,其#indices属性集合;

其中,2和3使用的是@Index注解。

@Index注解属性

  1. 索引名称:@Index#name属性值用于表示新建的索引名称;@Index#name属性值不存在,则使用 index_表名_表字段

  2. @Index#value属性值表示索引字段;

  3. @Index#orders属性值表示表字段在当前索引中的排序;

  • 如果当前属性值不为空,那么当前索引的@Index#orders的个数和索引字段个数一定相同;
  1. @Index#unique属性表示是否唯一索引;默认主键为唯一索引,但是一个表并不一定只有一个唯一索引;当前属性默认是false。

如果表中存在索引,那么索引规则如下:

  1. 一个表中新建的索引名称只能出现一次;

  2. 嵌入表中最好不要存在使用索引,否则会报警告,表示当前索引无效;

1.3.5 主键字段

使用主键方式有两个:

  1. @PrimaryKey注解修饰的表字段,一般是表常规字段;
  • (1)当前修饰的主键不允许是嵌入表(@Embedded修饰的节点)的常规字段,否则警告,并且无效;
  • (2)如果修饰的是表嵌入字段,即@PrimaryKey和@Embedded同时修饰的节点:@PrimaryKey#autoGenerate = false || @Embedded修饰对象的表常规字段有且仅有一个;
  1. @Entity#primaryKeys中设置;还可以通过entity节点的父entity节点继承过来,即@Entity修饰的节点的父节点也是@Entity修饰,并且该父entity节点存在@Entity#primaryKeys属性;

@PrimaryKey注解属性

@PrimaryKey#autoGenerate属性值:表示是否允许自动生成当前主键字段,默认是false;

主键使用规则如下

  1. 表主键必须存在;主键有且仅有一个,但是可以由多个表字段组成;

  2. 如果@PrimaryKey#autoGenerate = true,那么主键字段不是int类型,必须使用@NonNull修饰,表示不允许null值;

  3. 主键默认是txt类型,如果主键是自动生成(@PrimaryKey#autoGenerate= true)的表字段 ,那么当前表字段必须是int类型;

1.3.6 外键字段

只能通过@Entity#foreignKeys中的属性值来设置@ForeignKey外键;

@ForeignKey注解属性和规则如下:

  1. @Entity#foreignKeys属性表示当前表外键;

  2. @ForeignKey#entity属性必须存在,表示当前外键指向的表;

  • 必须存在,并且属性值中的对象必须使用@Entity修饰;
  1. @ForeignKey#parentColumns属性表示当前表外键字段指向的外键表字段,必须存在与@ForeignKey#entity对象中;

  2. @ForeignKey#childColumns属性,表示当前表外键字段,该字段指向@ForeignKey#entity对象的表字段(一般是主键);必须存在

  • @ForeignKey#parentColumns和 @ForeignKey#childColumns一定是等长;
  1. @ForeignKey#deferred属性,如果为true表示当前外键约束存在于事务中,事务全部结束才会生效;

  2. @ForeignKey#onDelete,@ForeignKey#onUpdate表示外键删除还是更新,有以下几种状态

  • (1)默认NO_ACTION,当前外键约束不做任何改动;
  • (2)RESTRICT:不允许对当前外键约束进行任何改动,删除或修改操作;
  • (3)SET_NULL:父键表对表字段(当前表字段是子键表的外键约束)进行删除或修改操作时,子键表的外键值设置为null;
  • (4)SET_DEFAULT:类似于SET_NULL,只不过子键表的外键值设置为默认值;
  • (5)CASCADE:父键表对表字段(当前表字段是子键表的外键约束)进行删除或修改操作时,子键表根据进行相应的更改;
  • (6)如果不存在以上几种类型范围,则报错;
  1. 表外键字段指向外键表字段:该外键表字段要么是主键,要么创建了唯一性索引;否则会报警告;

  2. 外键指向的表必须存在于@DatabaseView#entities中;

1.4 其他

1.4.1 @SkipQueryVerification注解

表示是否跳过数据库版本校验。规则如下:

  1. 如果@SkipQueryVerification和@Database同时使用,表示当前数据库版本不做校验;

  2. 如果@SkipQueryVerification和@DatabaseView,则仅仅表示不对当前@DatabaseView#vlue的sql校验;

  3. 同理,如果@SkipQueryVerification和@Insert(@Delete、@Query或@Update)一起使用,不对当前@Insert#value的sql校验;

  4. @RawQuery修饰的方法无论有没有使用@SkipQueryVerification都不会去做当前方法的sql校验;

1.4.2 类型转换

@TypeConverters修饰的注解实现类型转换,转换成表字段支持的类型。

当前表常规字段类型不支持boolean(或Boolean)类型的,但是我们却可以正常使用boolean或Boolean:因为Room系统为我们内部做了类型转换,boolean变量转换成表字段时,boolean = true转换成int = 1;boolean = false转换成int = 0;从表字段中提取数据时,同理,int转换成boolean。

先给个案例:

@Database(entities = News.class)
class Database extends RoomDatabase{
	xxx;
}
  1. 实体类:

     @Entity
     @TypeConverters(ThumbConverter::class)
     data class News(
             @PrimaryKey
             var row: String,
             var title: String = "",
             var type: Int = 2,
             val thumb: List?,
             var content_time: String? = "",
             var source: String? = "",
             var hot: Int = 0
     )
    
  2. 类型转换器:@TypeConverter的两个方法是成对出现的,方法名称可以任意命名,重点在入参和出参类型,必须是需要转换的类型和最终转换后的类型。

     class ThumbConverter {
    
     	//从表字段中提取时转换成list
         @TypeConverter
         fun getThumbFromString(value: String):List? {
             return value.split(",")
         }
     
     	//存入表字段时list抓好string
         @TypeConverter
         fun storeThumbToString(list: List): String {
             val str = StringBuilder(list[0])
             list.forEach {
                 str.append(",").append(it)
             }
             return str.toString()
         }
     }
    

哪些情况下可以和@TypeConverters一起使用,实现类型转换:

可以理解为**@TypeConverters可以在任何场合下使用,当然前提条件是必须支持修饰那种类型**。

@TypeConverters#value中的对象我们称之为typeConverters对象

@TypeConverter修饰的方法我们称之为typeConverter方法

规则如下:

  1. typeConverters对象必须是一个类;

  2. typeConverters对象中必须存在被@TypeConverter修饰的方法,并且是成对的,一个表示转入,一个表示转出;

  3. typeConverters对象是内部类,除非使用@ProvidedTypeConverter修饰,否则必须使用static修饰;

  4. typeConverters对象除非使用@ProvidedTypeConverter修饰,否则支持一下条件中的至少一个条件:

  • (1)typeConverters对象是objectcompanion objectkotlin类型;
  • (2)typeConverter方法必须全部static修饰;
  • (3)typeConverters对象构造函数为空
  • (4)typeConverters对象构造函数存在,但是参数为空;
  1. typeConverter方法必须public修饰;

  2. typeConverter方法返回类型不允许void(error ,none)类型;

  3. typeConverter方法返回类型如果是泛型,必须是实体类型(如List),不允许出现List< T>或List类型;

  4. typeConverter方法参数必须有且仅有一个;

  5. typeConverter方法参数类型如果是泛型,必须是实体类型(如List),不允许出现List< T>或List类型;

  6. @TypeConverters#value中的typeConverters对象可以有多个,这些对象的所有typeConverter方法不允许出现方法返回类型和方法参数类型都一致的情况;

  7. typeConverters对象中只有@TypeConverter修饰的方法有效,其他方法无任何意义,也不会报错。

  8. 还有一种情况(本不想说的,估计用的不大,或者不能用,可以验证一下),已上面的案例为例:@TypeConverters(ThumbConverter::class)在database节点上修饰,在entity节点上没有使用,好像大概可能也是可以的。

感觉篇幅太大,所以额外在写文章继续dao操作数据库。

你可能感兴趣的:(源码实际应用,数据库,java,mysql)