Android白银篇-GreenDao3

GreenDao

GreenDao 是一个将对象映射到 SQLite 数据库中的轻量且快速的 ORM 解决方案。它的本质就是提供一个面向对象的接口,使得开发者更加方便地将数据存储到数据库SQLite之中。我们只需要定义数据模型,GreenDao就会为我们生成实体类以及DAOs(data access objects),(在3.0之后不需要我们编写generator而是编写实体类并添加注解,GreenDao会为我们生成schema,以及DAOs)从而避免了开发者编写较多枯燥的数据存储和加载的代码。



GreenDao传送门


GreenDao介绍

在GreenDao中,默认会为每一个实体类建立一张数据表,实体类的每一个属性对应数据表中的一列。

优点

  • Android精简的依赖库,方便集成
  • 性能最大化
  • 内存开销最小化
  • 易于使用的APIs
  • 对Android进行高度优化

性能比较

配置

GreenDao3 采用注解方式来定义实体类,通过gradle插件生成相应的代码。

配置插件

在工程根目录下的build.gradle文件里,添加代码:

1
2
3
4
5
dependencies {
classpath 'com.android.tools.build:gradle:2.3.3'
// 添加GreenDao插件
classpath 'org.greenrobot:greendao-gradle-plugin:3.2.0'
}

配置依赖

Module下的build.gradle文件里,添加代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
apply plugin: 'com.android.application'
// 添加
apply plugin: 'org.greenrobot.greendao'

dependencies {
// 添加
compile 'org.greenrobot:greendao:3.2.0'
}

// 添加
greendao {
schemaVersion 1
daoPackage 'com.excellence.medical.greendao'
targetGenDir 'src/main/java'
}

  • schemaVersion: 数据库版本号,默认为1
  • daoPackage: 生成的DAOs、DaoMaster、DaoSession包名;默认为entities(数据库实体类)所在的包名
  • targetGenDir: 生成的DAOs、DaoMaster、DaoSession的目录,默认为build/generated/source/greendao
  • generateTests: 设置true自动生成单元测试。
  • targetGenDirTests: 设置生成单元测试目录。默认为src/androidTest/java

基本用法

实体

创建带注解@Entity的实体类,实体类即数据表;通常(除开带@Transient注解的成员)实体类中成员就是数据库中对应的字段。然后make project编译项目,实体类会自动生成get、set方法,并且在targetGenDir目录下的daoPackage包里,如src/main/java/com/excellence/medical/greendao,生成DaoMaster、DaoSession、以及AccountDao。

如果想增加或减少数据库字段,删除实体类中自动生成的代码,然后进行增加或减少实体类中的成员。

1
2
3
4
5
6
7
8
9
10
@Entity(nameInDb = "account")
public class Account {

@Id(autoincrement = true)
private Long id;
@Unique
private String accountId;
private String accountName;
private String accountPwd;
}

注解

  • @Entity
    修饰实体类名

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    @Entity(
    // schema 名,多个 schema 时设置关联实体。插件产生不支持,需使用产生器
    // schema = "myschema",

    // 标记一个实体是否处于活动状态,活动实体有 update、delete、refresh 方法。默认为 false
    active = false,

    // 表名,默认为类名
    nameInDb = "Account",

    // 定义多列索引
    indexes = {
    @Index(value = "name DESC", unique = true)
    },

    // 标记是否创建表,默认 true。多实体对应一个表或者表已创建,不需要 greenDAO 创建时设置 false
    createInDb = true,

    // 是否产生所有参数构造器。默认为 true。无参构造器必定产生
    generateConstructors = true,

    // 如果没有 get/set 方法,是否生成。默认为 true
    generateGettersSetters = true
    )
  • 修饰实体类成员

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    // 主键,autoincrement设置自增,注意类型是Long,而不是long
    @Id(autoincrement = true)

    // 唯一,默认索引
    @Unique

    // 列名-字段名,默认使用变量名。变化:customName --> CUSTOM_NAME
    @Property(nameInDb = "USERNAME")

    // 索引,unique设置唯一,name设置索引别名
    @Index(unique = true)

    // 非空
    @NotNull

    // 忽略,不持久化,即数据表不创建该字段,可用关键字transient替代
    @Transient

    // 对一,实体属性 joinProperty 对应外联实体ID
    @ToOne(joinProperty = "fk_dogId")

    // 对多。实体ID对应外联实体属性 referencedJoinProperty
    @ToMany(referencedJoinProperty = "fk_userId")

    // 对多。@JoinProperty:name 实体属性对应外联实体属性 referencedName
    @ToMany(joinProperties = {@JoinProperty(name = "horseName", referencedName = "name")})

    // 对多。@JoinEntity:entity 中间表;中间表属性 sourceProperty 对应实体ID;中间表属性 targetProperty 对应外联实体ID
    @ToMany
    @JoinEntity(entity = JoinUserWithSheep.class, sourceProperty = "uId", targetProperty = "sId")

初始化

在Application中进行初始化,GreenDao打开数据库两种方式:①打开内部(/data/data/xxxpackageNamexxx/)数据库,②打开外部(其他目录下)数据库

  • 内部数据库

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // 注意
    DaoMaster.OpenHelper helper = new DaoMaster.DevOpenHelper(this, VOD_DB, null);
    mDaoMaster = new DaoMaster(helper.getWritableDatabase());

    // 如果想使用Dao,直接用mDaoSession获取对应的Dao来操作
    mDaoSession = mDaoMaster.newSession();
    // Dao,执行增删改查操作
    AccountDao dao = mDaoSession.getAccountDao();

    // 如果想使用Sql语句,就使用mDaoDatabase操作
    mDaoDatabase = mDaoSession.getDatabase();

    // GreenDao有特殊的线程来处理数据库的耗时操作
    mAsyncSession = mDaoSession.startAsyncSession();
  • 外部数据库

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    public class DatabaseContext extends ContextWrapper {

    private String mDBPath = null;

    private DatabaseContext(Context base) {
    super(base);
    }

    public DatabaseContext(Context base, String dbPath) {
    super(base);
    mDBPath = dbPath;
    }

    @Override
    public File getDatabasePath(String name) {
    String dbPath = mDBPath + name;
    if (FileUtils.isFileExists(dbPath)) {
    return new File(dbPath);
    } else {
    return null;
    }
    }

    @Override
    public SQLiteDatabase openOrCreateDatabase(String name, int mode, SQLiteDatabase.CursorFactory factory) {
    int flags = SQLiteDatabase.CREATE_IF_NECESSARY | SQLiteDatabase.NO_LOCALIZED_COLLATORS;
    return SQLiteDatabase.openDatabase(getDatabasePath(name).getAbsolutePath(), factory, flags, null);
    }

    @Override
    public SQLiteDatabase openOrCreateDatabase(String name, int mode, SQLiteDatabase.CursorFactory factory, DatabaseErrorHandler errorHandler) {
    int flags = SQLiteDatabase.CREATE_IF_NECESSARY | SQLiteDatabase.NO_LOCALIZED_COLLATORS;
    return SQLiteDatabase.openDatabase(getDatabasePath(name).getAbsolutePath(), factory, flags, errorHandler);
    }
    }
1
2
3
4
// 重写传入DaoMaster.DevOpenHelper的Context,即改变数据库的路径
DatabaseContext databaseContext = new DatabaseContext(this, DB_PATH);
DaoMaster.OpenHelper helper = new DaoMaster.DevOpenHelper(databaseContext, VOD_DB, null);
其他类似打开内部数据库
  • 注意:

    • DaoMaster.DevOpenHelper在数据库升级的时候,会删除所有的表,只能用于Debug调试,正式项目需要封装处理,GreenDao升级请参考:GreenDaoUpgradeHelper
    • 如果GreenDao想使用打开多个数据库,可以创建多个DaoMaster.OpenHelper和DaoSession;同时重写onCreate方法,否则每个数据库的表是一样的。例如:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      DaoMaster.OpenHelper helper = new DaoMaster.OpenHelper(this, DB_NAME) {
      @Override
      public void onCreate(Database db) {
      ConfigDao.createTable(db, false);
      MemberDao.createTable(db, false);
      }
      };
      mDaoSession = new DaoMaster(helper.getWritableDatabase()).newSession();

      DaoMaster.OpenHelper encryptedHelper = new DaoMaster.OpenHelper(this, ENCRYPTED_DB_NAME) {
      @Override
      public void onCreate(Database db) {
      AccountDao.createTable(db, false);
      }
      };
      mEncryptedDaoSession = new DaoMaster(encryptedHelper.getEncryptedWritableDb(getUniquePseudoID())).newSession();

增删改查

  • 使用Dao操作时,数据库里必须有主键,操作才会成功;否则操作无效或达不到预期的结果

  • GreenDao有一个缓存机制,即把用户插入,更改或查找的实体保存在内存中,当用户下一次查找时先从内存中查找,如果不存在再从数据库中查找,清除缓存使用:DaoSession.clear()

  • 如果没有主键,则只能使用Sql语句操作数据库,可以参考:Android黄金篇-SQLite数据库

Dao增加

1
2
3
4
5
6
7
8
9
10
11
12
long     insert(T entity)  // 插入指定实体
void insertInTx(T... entities)
void insertInTx(java.lang.Iterable<T> entities)
void insertInTx(java.lang.Iterable<T> entities, boolean setPrimaryKey)
long insertWithoutSettingPk(T entity) // 插入指定实体,无主键
long insertOrReplace(T entity) // 插入或替换指定实体
void insertOrReplaceInTx(T... entities)
void insertOrReplaceInTx(java.lang.Iterable<T> entities)
void insertOrReplaceInTx(java.lang.Iterable<T> entities, boolean setPrimaryKey)
void save(T entity) // 依赖指定的主键插入或修改实体
void saveInTx(T... entities)
void saveInTx(java.lang.Iterable<T> entities)

Dao删除

1
2
3
4
5
6
7
void     deleteAll()  // 删除所有
void delete(T entity) // 删除指定的实体
void deleteInTx(T... entities)
void deleteInTx(java.lang.Iterable<T> entities)
void deleteByKey(K key) // 删除指定主键对应的实体
void deleteByKeyInTx(K... keys)
void deleteByKeyInTx(java.lang.Iterable<K> keys)

Dao修改

1
2
3
void     update(T entity)
void updateInTx(T... entities)
void updateInTx(java.lang.Iterable<T> entities)

Dao查询

1
2
3
java.util.List<T>     loadAll()
T load(K key)
T loadByRowId(long rowId)

QueryBuilder查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
QueryBuilder<T>     queryBuilder()  // Dao

// QueryBuilder
QueryBuilder<T> where(WhereCondition cond, WhereCondition... condMore) // 条件,AND 连接
QueryBuilder<T> whereOr(WhereCondition cond1, WhereCondition cond2, WhereCondition... condMore) // 条件,OR 连接
QueryBuilder<T> distinct() // 去重,例如使用联合查询时
QueryBuilder<T> limit(int limit) // 限制返回数
QueryBuilder<T> offset(int offset) // 偏移结果起始位,配合limit(int)使用
QueryBuilder<T> orderAsc(Property... properties) // 排序,升序
QueryBuilder<T> orderDesc(Property... properties) // 排序,降序
QueryBuilder<T> orderCustom(Property property, java.lang.String customOrderForProperty) // 排序,自定义
QueryBuilder<T> orderRaw(java.lang.String rawOrder) // 排序,SQL 语句
QueryBuilder<T> preferLocalizedStringOrder() // 本地化字符串排序,用于加密数据库无效
QueryBuilder<T> stringOrderCollation(java.lang.String stringOrderCollation) // 自定义字符串排序,默认不区分大小写

WhereCondition and(WhereCondition cond1, WhereCondition cond2, WhereCondition... condMore) // 条件,AND 连接
WhereCondition or(WhereCondition cond1, WhereCondition cond2, WhereCondition... condMore) // 条件,OR 连接

示例

1
mAccountDao.queryBuilder().orderDesc(Properties.Date).limit(1).unique()

DaoSession异步操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
DaoSession().startAsyncSession().runInTx(new Runnable() {

@Override
public void run() {
// insert
// delete
// update
// query
}
});
DaoSession.startAsyncSession().insertInTx
DaoSession.startAsyncSession().deleteInTx
DaoSession.startAsyncSession().updateInTx
DaoSession.startAsyncSession().insertInTx
DaoSession.startAsyncSession().insertOrReplaceInTx

DaoSession增删改查

1
2
3
4
5
6
7
8
9
10
11
// DaoSession 的方法转换成 Dao 的对应方法执行
<T> long insert(T entity)
<T> long insertOrReplace(T entity)
<T> void delete(T entity)
<T> void deleteAll(java.lang.Class<T> entityClass)
<T> void update(T entity)
<T,K> T load(java.lang.Class<T> entityClass, K key)
<T,K> java.util.List<T> loadAll(java.lang.Class<T> entityClass)
<T> QueryBuilder<T> queryBuilder(java.lang.Class<T> entityClass)
<T,K> java.util.List<T> queryRaw(java.lang.Class<T> entityClass, java.lang.String where, java.lang.String... selectionArgs)
<T> void refresh(T entity)

Query重复查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// QueryBuilder
Query<T> build()
CursorQuery buildCursor()
CountQuery<T> buildCount()
DeleteQuery<T> buildDelete()

// Query
// 设置查询参数,从 0 开始
Query<T> setParameter(int index, java.lang.Object parameter)
Query<T> setParameter(int index, java.lang.Boolean parameter)
Query<T> setParameter(int index, java.util.Date parameter)
void setLimit(int limit) // 限制返回数
void setOffset(int offset) // 偏移结果起始位,配合limit(int)使用

// Query 绑定线程,执行非本线程的 Query 抛异常,调用获取本线程 Query
Query<T> forCurrentThread() // 获取本线程 Query

查询结果

1
2
3
4
5
6
7
8
9
10
11
// QueryBuilder、Query
T unique() // 返回唯一结果或者 null
T uniqueOrThrow() // 返回唯一非空结果,如果 null 则抛异常
java.util.List<T> list() // 返回结果集进内存
// 懒加载,须在 try/finally 代码中关闭。
LazyList<T> listLazy() // 第一次使用返回结果集,所有数据使用后会自动关闭
LazyList<T> listLazyUncached() // 返回虚拟结果集,数据库读取不缓存
CloseableListIterator<T> listIterator() // 懒加载数据迭代器,不缓存,所有数据使用后会自动关闭

// QueryBuilder、CountQuery
long count() // 获取结果数量

混淆

在混淆文件proguard-rules.pro中添加

1
2
3
4
5
6
7
8
9
10
### greenDAO 3
-keepclassmembers class * extends org.greenrobot.greendao.AbstractDao {
public static java.lang.String TABLENAME;
}
-keep class **$Properties

# If you do not use SQLCipher:
-dontwarn org.greenrobot.greendao.database.**
# If you do not use RxJava:
-dontwarn rx.**

@ToOne、@ToMany,1:1、1:n、n:m等多张表关联待续^_^

谢谢老板,请尽情用红包来蹂躏我吧!!!
0%