本文共 10506 字,大约阅读时间需要 35 分钟。
一般文章中介绍数据存储有这么几种:
文件
SharedPreferences
数据库
网络
ContentProvider
其实,在内存存储也可以算是一种存储,比如,有些时候我们用 static
变量存储一些共享数据,只不过与上面的数据不用,上面的是持久化数据存储,但是也是一种数据存储方式,需要根据需求来决定使用哪种方式。加上内存存储构成三级缓存策略,
内存—>本地—>网络,不同级别的数据一般情况下,使用频率也是逐级降低的,这也是一种规律,因为对于移动端来说,提高用户体验的其中一点也是加载速度快,那么当然从内存中拿出数据来效率更高,但是不可能所有数据都能放在内存中,毕竟内存有限,而且有些数据也是需要长久记录下来的,设备重启或者应用重启后,保存在进程中的数据也就释放掉了。
下面说一说持久化存储的特点:
文件
文件,这种类型的数据一般都是文件格式,比如图片,word,text等但是大小和数量也不能太多,毕竟移动设备的存储空间有限。另外这种数据一般官方的建议是存储在外部私有存储空间,因为内部存储空间有限,对于外部存储空间,如果存在公有空间,则删除应用时文件不会被删除,如果保存在外部私有空间,删除应用时数据也会一并被删除,这样也能够减少一些用户喷来的口水!
SP
对于 SP 来说,一般也比较简单与方便,常常我们都是拿 SP 工具类来进行数据存储的,对于 SP 存储的数据,也是少量的,但是同为本地数据存储,有哪些特点,换句话说,在哪些场景下使用 SP ?
只支持存储Java基础数据类型(Boolean、Int、Float、String、Long等)不支持自定义数据类型。
本质是一种Map,通过键值对的形式进行数据存储。
不支持查找功能。
从这几个特点中可以看出,实际上用 SP 存储一些基本的数据类型,虽然用你也可以将数据放在本地文件中,但是没有 SP 方便,SP 可以根据 key 直接取出。同样,SP 是放在内存存储空间的,数据量也是相对较小的。
数据库
很明显,数据库就是要解决 SP 不能存储自定义对象的烦恼。SQL 数据库叫做“结构化查询语言”,也就是说是用来存储结构化数据的,这里可以理解为对象,所以数据库一般是关系型数据库。对于数据存储的数据也可以存储到文件中,只不过我们需要将对象数据序列化,存储到文件中,然后读取的时候在反序列化,这对于开发者来说,很麻烦数据库能够很好地解决这个问题。开发者只管存储就行,不需要关心序列化和反序列化的过程。
对于移动端的数据库,一般都是轻量级的,像安卓端的 Sqlite,毕竟是移动端,不能搞得像 PC 上那么大。对于移动端的数据库一般也有以下几个特点
自定义数据类型,一般是对象。
存储在本地的内部存储空间,数据也不会太大
支持查找。
数据库现在也有很多优秀的开源框架可以使用,不同的框架也有不同的特点,那么对于开发者来说如何选择?其实无非也就这么几点:
满足性能要求,比如速度快,数据量大时,稳定
简单易操作,能不写代码最好~~
轻量级,引入的包很大的话,也会对APK的大小有影响,以及初始化的速度等
为啥本篇主要来说一说 greenDao 呢?原因很简答,之前没用过,当然来尝试一下了。
原生的 sqlite 就不说了,应用大家都不会用原生的数据库,如果原生的数据库好用的话,估计也不会出现各种开源框架了!
对于数据库,特别是移动端的,涉及的操作不会很多,也就是增删改查,不熟悉的可以在 上复习一下。
SELECT - 从数据库表中获取数据UPDATE - 更新数据库表中的数据DELETE - 从数据库表中删除数据INSERT INTO - 向数据库表中插入数据
大神们喜欢使用原始的 SQL 语句,对于我这种小菜鸟,还是喜欢更简单的框架,所以对于我来说,好的数据库框架,一般是越无脑越好,性能越高越好,怎么平衡它?看你自己了。
之前使用 LiteOrm,这个框架比较简单,有兴趣的可以看看这篇文章-。至于性能和 greenDAO 对于,没有亲子测过(主要是不会测~),可以看看 这篇文章。
greenDAO,官网链接 。
LiteOrm,官网链接 。
对月 greenDAO,官方是这么说它的特点的:
(1) Rock solid: greenDAO has been around since 2011 and is used by countless famous apps 非常稳定,自从 2011 年开始就被无数有名气的 App 使用(2) Super simple: concise and straight-forward API, in V3 with annotations 超级简单(倒是觉得没有 LiteOrm简单),有简单的使用 API,在 v3 版本中引入注解(3) Small: The library is <150K and it's just plain Java jar (no CPU dependent native parts) 很小,库是于 150 K 的 jar 包(不含有依赖 CPU 的本地代码部分,指的是 so库)(4) Fast: Probably the fastest ORM for Android, driven by intelligent code generation 速度快,可能是 android 上最快的 ORM,可以自动生成代码(5) Safe and expressive query API: QueryBuilder uses property constants to avoid typos 安全和容易记忆的查询 API:QueryBuilder 使用合适的常量来避免拼写错误(6) Powerful joins: query across entities and even chain joins for complex relations 有强大的联合查询:支持实体间的交叉查询,以及支持复杂的链式查询(7) Flexible property types: use custom classes or enums to represent data in your entity 拥有更灵活的类型,可以使用类类型或者枚举,来表示你的实体类(8) Encryption: supports SQLCipher encrypted databases 可以加密:支持 SQLCipher 加密数据库
好,看它吹完牛和我唠叨完,我们还是实际使用一下,来感受感受!
官方也是给出了
工程目录下 build.gradle 添加如下配置:
buildscript { repositories { google() jcenter() } dependencies { classpath 'com.android.tools.build:gradle:3.1.0' classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2' // add plugin }}
在 app 的 build.gradle 下配置:
apply plugin: 'com.android.application'apply plugin: 'org.greenrobot.greendao' // apply pluginandroid { ...}dependencies { ... implementation 'org.greenrobot:greendao:3.2.2'}
@Entitypublic class Note { @Id private Long id; private String name; private int age; //下面省去了 setter/getter}
常用注解:
public @interface Entity { /** * 在数据库中表的名称,默认为实体的类名 */ String nameInDb() default ""; /** * 定义索引,可以跨越多个列(默认为实体类成员变量的个数) */ Index[] indexes() default {}; /** * 标记创建数据库表 * 如果是有多个实体都关联这个表,可以把多余的实体里面设置为false避免重复创建(默认是true) */ boolean createInDb() default true; /** * 告知GreenDao当前实体属于哪个schema */ String schema() default "default"; /** * 实体活动状体标志位(默认为false) * 若设置为true,实体有更新、删除和刷新方法 */ boolean active() default false; }
@Generated 这个是build后greendao自动生成的,这个注解理解为防止重复,每一块代码生成后会加个hash作为标记。 官方不建议你去碰这些代码,改动会导致里面代码与hash值不符。
@Generated(hash = 1272611929) public Note() { } public Note(Long id) { this.id = id; } @Generated(hash = 1686394253) public Note(Long id, @NotNull String text, String comment, java.util.Date date, NoteType type) { this.id = id; this.text = text; this.comment = comment; this.date = date; this.type = type; }
@NotNull – 设置表中当前列的值不可为空
@Convert– 指定自定义类型,将自定义的数据类型,转换为数据库中所存储列的值的类型
@Convert(converter = NoteTypeConverter.class, columnType = String.class) private NoteType type;
@Id private Long id;
6.索引属性-两种方法
(1)@Index– 使用@Index作为一个属性来创建一个索引;
@Index 通过这个字段建立索引
@Unique 添加唯一约束,上面的括号里unique=true作用相同
@Entitypublic class User { @Id private Long id; @Index(unique = true) private String name;}@Entitypublic class User { @Id private Long id; @Unique private String name;}
(2)定义多列索引(@link Entity#indexes())
通过逗号间隔创建表的属性索引,例如 “propertyA,propertyB,propertyC” ,
若要指定排序, 需在列明以后添加 ASC(升序) 或者DESC(降序) , 例如 “propertyA DESC, propertyB ASC” ,只有实体类中使用 {@link Entity#indexes()} 才可设置@Entity(indexes = { @Index(value = "text, date DESC", unique = true)})public class Note { ...}
@Entitypublic class Order { @Id private Long id; private long customerId; @ToOne(joinProperty = "customerId") private Customer customer;}@Entitypublic class Customer { @Id private Long id;}
@ToOne 是将自己的一个属性与另一个表建立关联,如果不设置joinProperty
,在Customer
表中会自动创建外键,值是自动增加的。
(2)一对多
public @interface ToMany { /** * 目标实体持有源实体的名称 * 如果没设置,否则有 {@link JoinProperty} or {@link JoinEntity} 指定 */ String referencedJoinProperty() default ""; /** * 将源表列索引->目标列 * 如果没设置,否则有 {@link JoinProperty} or {@link JoinEntity} 指定 */ JoinProperty[] joinProperties() default {}; }
上面的可能不是很好理解,其中也涉及了其他注解,@JoinProperty
和 @JoinEntity
referencedJoinProperty
作为另一个表的外键,可以不设置 @JoinProperty
和 @JoinEntity
,否则就要设置这两个注解。举个例子。 方式一:
@Entitypublic class User { @Id private Long id; @ToMany(referencedJoinProperty = "ownerId") private ListownedSites;}@Entitypublic class Site { @Id private Long id; private long ownerId;}
一个 User 中可能有多个 Site ,那么对于 Site 和 User 联系起来是通过 ownerId
方式二:
如果不指定 ownerId,通过 @JoinProperty
。
@Entitypublic class User { @Id private Long id; @Unique private String authorTag; @ToMany(joinProperties = { @JoinProperty(name = "authorTag", referencedName = "ownerTag") }) private ListownedSites;}@Entitypublic class Site { @Id private Long id; @NotNull private String ownerTag;}
从上面看到通过 User 中的 authorTag 和 Site 中的 ownerTag 联系起来。
方式三:
(3) 多对多
@Entitypublic class Site { @Id private Long id; @ToMany @JoinEntity( entity = JoinSiteToUser.class, sourceProperty = "siteId", targetProperty = "userId" ) private Listauthors;}@Entitypublic class JoinSiteToUser { @Id private Long id; private Long siteId; private Long userId;}@Entitypublic class User { @Id private Long id;}
单独定义一个关系类 JoinSiteToUser ,通过该类将 Site 和 User 联系起来。
一般也是使用在 @toMany
中.
public @interface OrderBy { /** * 通过逗号间隔创建表的属性索引,例如 “propertyA,propertyB,propertyC” * 若要指定排序, 需在列明以后添加 ASC(升序) 或者DESC(降序) , 例如 "propertyA DESC, propertyB ASC" * 默认按升序排序 * 若不设置默认根据主键排序 */ String value() default ""; }
9.@Property– 设置数据库中的列名,默认是的使用字段名
举例:@Property (nameInDb=”name”)
public @interface Property { /** * 默认是的使用字段名 */ String nameInDb() default ""; }
(1)注解实体类:默认禁止修改此类(2)注解其他代码段,默认禁止修改注解的代码段
编写完实体类之后,进行编译,会生成 DaoMaster,DaoSession,和一个实体 DAO类,如实体类是 User,则对应的是 UseDao 类。
然后在 Application 中初始化,看到代码中加了加密的设置。greenDao 允许加密, 若要使用加密,需要在 build.gradle 中加入以下依赖。// This is only needed if you want to use encrypted databases implementation 'net.zetetic:android-database-sqlcipher:3.5.6'
Application 中初始化
public class MyApplication extends Application { /** A flag to show how easily you can switch from standard SQLite to the encrypted SQLCipher. */ public static final boolean ENCRYPTED = false; private DaoSession daoSession; @Override public void onCreate() { super.onCreate(); DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(this, ENCRYPTED ? "notes-db-encrypted" : "notes-db"); Database db = ENCRYPTED ? helper.getEncryptedWritableDb("super-secret") : helper.getWritableDb(); daoSession = new DaoMaster(db).newSession(); } public DaoSession getDaoSession() { return daoSession; }}
DaoMaster:使用greenDAO的切入点。 DaoMaster保存数据库对象(SQLiteDatabase)并管理特定模式的DAO类(而不是对象)。 它有静态方法来创建表或删除它们。 它的内部类OpenHelper和DevOpenHelper都是SQLiteOpenHelper的实现,用来在SQLite数据库中创建模式。
DaoSession:管理特定模式的所有可用DAO对象,你可以使用其中一个的getter方法获取DAO对象。 DaoSession还为实体提供了一些通用的持久性方法,如插入,加载,更新,刷新和删除。 最后,DaoSession对象也跟踪identity scope。
DAO:数据访问对象(DAO),用于实体的持久化和查询。 对于每个实体,greenDAO会生成一个DAO。 它比DaoSession拥有更多的持久化方法,例如:count,loadAll和insertInTx。
greendao { targetGenDir 'src/main/java' daoPackage 'com.XXXX.dao.db'}
schemaVersion:数据库模式的当前版本。 这由* OpenHelpers类用于在模式版本之间迁移。 如果更改实体/数据库模式,则必须增加此值。 默认值为1。
daoPackage:生成的DAO,DaoMaster和DaoSession的包名称。 默认为源实体的包名称。
targetGenDir:生成的源码应存储在的位置。 默认为生成目录(build / generated / source / greendao)中生成的源文件夹。
generateTests:设置为true以自动生成单元测试。
targetGenDirTests:生成的单元测试应存储在的基本目录。 默认为src / androidTest / java。
DaoSession daoSession = ((MyApplication) getApplication()).getDaoSession(); userDao = daoSession.getUserDao(); ... QueryBuilderqb = userDao.queryBuilder(); qb.where(Properties.FirstName.eq("Joe"),// 查询条件 qb.or(Properties.YearOfBirth.gt(1970), // 或 条件 qb.and(Properties.YearOfBirth.eq(1970), Properties.MonthOfBirth.ge(10)))); // 与 条件 List youngJoes = qb.list();
User user = new User(); user.setName("Nick"); user.setAge(25); user.setId(12); userDao.insert(user);
user = userLists.get(10); user.setName(Ralf); user.setAge(29); userDao.update(user);
Long userId = user.getId(); userDao.deleteByKey(userId);
以上就是 greenDao 的基本介绍,数据库也是很复杂的一部分,但是对于移动端来说,一般会一些基本的操作大部分场景都能够满足,如果需要更多操作可以参考官方的说明文档 .
另外需要关注的是,greenrobot 又出了 ObjectBox,使用更加单,也是 greenrobot 大力推荐的,后面会做一下介绍。下一篇会对 greenDao 进行封装使用,然后自己设计稍微复杂的表进行实践一下,也不能总是对着一张表增删改查,要不有点那啥。。。
好,就到这里,敬请期待!
转载地址:http://zidws.baihongyu.com/