今日科技快讯
美国总统唐纳德·特朗普今天在匹兹堡发表公开演讲,他表示,他已经亲自打电话给苹果首席执行官蒂姆·库克,称他是个“了不起的人”,并感谢他在美国的投资。苹果此前曾称,会将大量海外现金转回美国。特朗普承认,他最初以为库克的意思是数亿美元,而不是数千亿美元,他也没想到一家公司竟然能对一个国家有这么大规模的投资。
作者简介
明天就是周六啦, 提前祝大家周末愉快!
本篇文章来自DthFish的投稿,详细地分析了View的绘制流程,希望对大家有所帮助。
DthFish的博客地址:
https://www.jianshu.com/u/ea86fdbeb6c2
简介
为啥要写数据库呢?公司的项目一直在进行着调整,整个项目的 module 已经超过 20,为了做到彻底解耦,我们的组的扛把子可谓是大刀阔斧,还多次向我表达了我们的 storage 模块需要调整的感慨。先说说现状,目前项目中使用的是 Ormlite,总体的感觉就是使用起来还是要写很多代码,看着之前的升级版本的逻辑头大。由于构建和升级的逻辑都在 storage 模块,所以要添加和修改表的话就要一定会修改到这个模块的代码,说白了还是耦合。
所以需求就是两点:
使用简单,升级方便。
多模块使用,让各个 module 负责各自的表。(这是不是就是一个 module 对应一个数据库来着)
上面两点是我学习了解数据库框架的目的,所以 DBFlow 也只是学习和尝试的框架之一,其他的后续再说。
简单说明一下,本文使用的是 DBFlow 的 4.1.2 版,也是截止目前为止的最新版本,在 github 上也可以看到,目前 DBFlow 还提供了一系列拓展,包括对 kotlin 的支持,Rxjava 的支持,数据库加密等。当然我是带着目的来的,所以我还是会主要关注上面提出来的两点需求,至于一些详细的使用还是尽力吧。
配置
为了后面多 Module 使用方便,先在项目中创建 config.gradle 文件,当然这个不是必须的。
对项目的 build.gradle 文件做以下修改:
ddd
在 app Module 中的 build.gradle 中添加依赖
到这里已经配置完毕了,下面我们开始愉快的创建数据库了。
创建数据库与表
新建 App 继承 Application,并在其中初始化:
新建数据库
新建表
新建 Product 类,并用注解@Table标注,指定它的数据库为 AppDatabase;用@PrimaryKey标注 id 为主键,并且为自增长;用@Column标注 name 为表中的一列。
这里我们的表算是建完了,只需要点击 AndroidStudio 的 Build -> Make Project 之后,就可以在 app\build\generated\source\apt\debug 目录下找到生成的类 Product_Table,在里面可以看到一些 SQL 语句。
注意:这里我们继承的 BaseModel 是 DBFlow 给我们提供的,并不是我们自己项目中的,所以有人可能有疑问:我可不可以不继承它?答案是可以的,差别仅仅增删改查的操作上有所不同。
CRUD
DBFlow 对数据的增删改查已经做了封装,使用起来比较简单,也不很容易理解。
Insert
对于向数据库插入数据的操作,对于已经继承了 BaseModel 的 bean,我们可以直接 new 一个出来,给相应的属性赋值之后,直接调用 save() 方法,数据就保存完毕了,代码如下。
另外,这里我们并没有给 Product 的主键 id 赋值,但是在保存完之后这个 id 就被赋值了。
Query
数据的查询,这里举了一个简单但是平时使用较为频繁的例子。从前文我们可以知道 Product_Table 是 apt 给我们生成的,使用起来也很灵活易懂,当然其他的更为复杂的建议大家可以查看一下官方文档。
Update
更新和删除可以为先查询后操作,只要查到对应的数据,在 bean 上做修改,然后调用 update() 方法,数据库就能修改完成。还有另一中更接近 sql 语法的方式。
Delete
与更新操作类似:
对于未继承 BaseModel bean 的 CRUD
对于没有继承 BaseModel 的 bean,我们可以用以下方式进行数据操作,事实上前面的 save() 等方法最终也是通过这样处理的。当然定义表的时候的注解不可或缺。
这里,考虑到更新和删除的第二种方法,尝试了一下,果然还可以写出类似的插入方法。
版本升级
因新建表升级
好了现在我们已经清楚了 DBFlow 的基本使用了,但是一张表不能满足我们的需求啊!我还想创建一张 Category 表,那么赶紧再创建个类加下注解吧。
简单归简单,但是还是试一下增删改查功能吧!
运行,之后就会发现了崩溃信息:
这里我们遗漏了数据库版本的升级,对于增加表格来说,DBFlow 版本升级其实很简单,我们只要找到我们的数据库类,并且把他的版本号加 1。
再重新运行下,我们就能在不影响 Product 表的前提下,成功新建了 Category 表了。
因修改表结构升级
DBFlow 的表结构修改是通过 Migration(https://github.com/agrosner/DBFlowDocs/blob/master/Migrations.md) 进行的,通过对它的实现,来进行对表的操作。
虽然看到这里,还是不知道怎么使用它,不过不用担心,DBFlow 已经有它的几个现成的实现提供给我们进行使用。
AlterTableMigration用于重命名表,增加列
IndexMigration/IndexPropertyMigration用于索引创建和删除
UpdateTableMigration升级数据库的时候更新数据
下面我们就举一个相对常见的列子来看一下如何进行表结构的修改。
首先我们先修改一下之前创建的 Product 表:
这里我给加上了两列 price 和 manufactuer,然后希望价格默认为 100 元,接着写 Migration。
因为是添加表的列,所以继承 AlterTableMigration;
修改 AppDatabase 的版本为 3,因为我们之前添加过 Category 表 version 为 2;
添加注解,注解中 version 为现在的版本号 3,database 为我们的 AppDatabase 表;
重写 onPreMigrate() 方法添加 addColumn() 就是我们在 Product 中新加的字段;
有疑问!如果一次升级我们不止改了一处,还有涉及到其他的修改咋办?
看到@Migration注解中有个 priority 这里我们姑且多建几个 Migration 用 priority 区分优先级来试试吧!
输出结果为:
可以看到 priority 小的执行顺序优先,如果想要指定两个Migration 的 priority 相同的同学,就不要找不痛快了,因为没法保证执行顺序。
接下来看一下我们更新的表格内容:
1.查询之前的数据,已经成功的添加了两个属性,但是 price = 0;
2.新插入一条数据后,查询——然并软,说好的 defaultValue 呢!你还我的 100 块!
关于失效的defaultValue
好吧关于 defaultValue 失效我是始料未及的,在 github 上的 Issues 中查看了一下,虽然有类似的问题但是还是没有找到正确的处理方法(希望不是我英文水平的问题),如果有同学知道正确的方法请务必联系我!以免我误人子弟!
但是我尝试出了我自己的方法。我的数据库要升级版本 4 啦!
去掉了 defaultValue 直接给 price 一个默认值;
对版本 3 的补救措施,写了 Migration4,在 onPreMigrate 方法中对历史数据进行了处理。
删掉表格的一列
见 githubIssue #467
创建一个没有那一列的临时的表
把数据复制进去
删掉旧的表
重新建个表
令我不禁想起了郭神的LitePal,对删除列做的良心处理。
多module使用
其实说了这么多,我最关心的还是多 module 的使用,毕竟我最初的目的还是这个。接下来的过程有些曲折,我会把过程中出现的错误以及处理方法都记录下来。
创建多个Module添加依赖
这里除去 app,我又创建了 base,special,ship 三个 module,下面看一下他们的依赖配置。
在 ship module 中新建数据库和表
我没有把 AppDataBase 移到 base module 中,因为我希望各个 module 维护自己的表的时候不要修改到底层的 module,所以在 ship 中创建自己的 ShipDataBase。
开始痛苦的解决问题
和前文一样,添加插入和查询方法,build 报错:
What a f**k! 好吧强大的 google 指引我到 githubIssue #266。里面信息一堆,但是我隐约找到了我要的答案:
另外还有人提出 apt 已经不维护了:
就是说,在使用 apt(咋配置就不提了,大家可以查一下) 的情况下,可以在 Ship 的 module 的 build.gradle 中添加:
那么就可以解决问题,通过 build 的话,最终生成的类名会是 ShipGeneratedDatabaseHolder,与 app module 中的GeneratedDatabaseHolder 区别。
但是现在的问题是 apt 已经不维护了,我们是否还能通过啥方法进行处理呢?
Android注解使用之注解编译android-apt如何切换到annotationProcessor
https://www.cnblogs.com/whoislcj/p/6148410.html
按照上文,我又修改了 Ship 的 build.gradle 文件:
编译,报错:
好吧再改!AndroidStudio 告诉了我们解决方案:
这次终于成功了跑起来了,我们试试插入吧!报错:(我已经习惯了)
还好,通过错误我们看到说我们没有在 Database 中注册,可是事实上我们已经加了@Table(database = ShipDatabase.class)注解。实际上,是我们新生成的 ShipGeneratedDatabaseHolder 需要注册。
到这里,多 module 的使用已经介绍完啦,我先去 Stackoverflow 上替外国友人解答下这个问题!
外键
终于到这里了,原来解决完上边的多 module 使用的问题其实已经感觉篇幅有点长了,但是考虑到外键的重要性觉得还是有必要讲一下的。
升级数据库:给表添加外键
事实上一上来就会有疑问:更新数据库的时候添加普通的一列和添加外键一样吗?
对于我们的 bean 来说添加仅仅是添加了一个成员变量,但是更新数据库就不一样了,因为数据库里面这些自定义的数据结构是通过外键关联的。好吧我们又回到了数据库版本更新的问题,但是现在这个情况都不包含在之前提过的里面。下边看一下我最终尝试过后的结构:
在 Product 类中添加 category 字段,毕竟我们的产品是按品类划分的
给 category 字段添加 @ForeignKey 注解,注解里面的参数,暂时先放一下,一会说
编写 Migration5,修改版本号
虽然列了简单的三步但是还是有值得思考的地方:onPreMigrate() 方法中的代码该怎么写?这个 category_id 哪里来的?
事实上 addForeignKeyColumn() 方法是在我写版本升级的时候在 AlterTableMigration 类中发现的:
虽然注释中有说各个参数的含义,但是事实上看了还是不知道怎么填写,SQLiteType 中只有一些基本的类型,最终给我提示的还是 apt 生成的 Product_Table 类。里面有这么一段:
结果就是把里面的内容截取出来当参数了,运行结果也令人满意。ps:如果这里有误请及时联系我,给我留言啊,我也是摸索出来的。
一对一外键
两个方法分别执行,得到:
我们发现 query 操作查出来的 category 字段,只有 id,name 却等于 null,修改如下:
结果:
这里我们多执行了一步操作,调用了 Category 的 load() 方法,然后就查到了 id 为 1 的 Category 所有的信息了。
最后
说实话在写这篇文章之前我自己写过另一个 Demo,但是那个就没有这篇文章举得例子这样简单明了。当然 DBFlow 的还有很多细节我没有讲到,包括但不限于加密,Rxjava这些的支持。正因为我自己在学习 DBFlow 的过程中也去找了相关文章,要么说的都比较简单,要么版本不是最新的,最后还是自己看文档进行尝试,总结一篇出来。结果也是有很多意外收获,总之在没有开始写的时候我知道篇幅会很长,但是实际上更长,所以最后有几个想讲的地方也就懒得完善了,哈哈!
或者扫一扫关注我的公众号
领取专属 10元无门槛券
私享最新 技术干货