GreenDao —— 简单快速操作 Android SQLite 数据库

GreenDao —— 简单快速操作 Android SQLite 数据库

GreenDao 是轻量快速的 SQLite 数据库 ORM 解决方案。(greenDAO is a light & fast ORM solution that maps objects to SQLite databases.)

GreenDao 层次

ORM(Object-Relationl Mapping)用于在关系型数据库与对象之间做一个映射。可以使数据库操作想对象一样使用,而避开使用复杂的SQL语句交互。

GreenDao 特点:

  • 性能强大。(可能是 Android 平台最快的 ORM 框架)
  • 简易便捷的 API
  • 开销小
  • 依赖体积小
  • 支持数据库加密
  • 强大的社区支持

此前接触 Android 的 SQLite 数据库操作,有感于直接使用 SQLite 繁琐且低效,使用 Android 官方的 Room 也感觉效果不佳。最后选择 GreenDao 总算满足预期。

GreenDao 环境配置

1. Project 下的 build.gradle 增加插件支持


buildscript {
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.2.1'
        classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2' // 版本建议最新
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

2. app 下的 build.gradle 增加插件依赖


apply plugin: 'com.android.application'
apply plugin: 'org.greenrobot.greendao'

android {
    ...

    // greendao 配置
    greendao {
        schemaVersion 1                                         // 数据库版本号
        daoPackage      'org.cvte.research.faceapi.greendao'    // greenDao 自动生成的代码保存的包名
        targetGenDir    'src/main/java'                         // 自动生成的代码存储的路径,默认是 build/generated/source/greendao.
    }

    ...
}

dependencies {
    ...

    // GreenDao  数据库ORM
    implementation 'org.greenrobot:greendao:3.2.2'
    // GreenDao 生成dao和model的generator的项目 发布时可以去掉
    implementation 'org.greenrobot:greendao-generator:3.2.2'
}

接下来可以使用 GreenDao 对数据库对象进行操作了。

创建 GreenDao 数据库对象实体

1. 创建数据库实体

创建实体需要了解 GreenDao 的注解:

注解 描述 其它参数
Entity 对应数据库中的表 nameInDb:表使用别名。默认为类名
active:标记一个实体是否处于活动状态,活动实体有 update、delete、refresh 方法。默认为 false
indexes:定义多列索引
Id 该数据库表的主键,只能是 Long 或 long 类型 autoincrement:设置是否自增,可以通过传入 null 自动分配
Unique 唯一。可以通过设置唯一的属性设为主键
Property 列名 nameInDb:列使用别名。默认为变量名
Index 索引 unique:设置唯一
name:设置索引的别名
NotNull 非空。该字段值不能为空
Transient 忽略。greendao 将不会创建对应的项
ToOne 表格映射关系一对一 joinProperty:外联实体与该实体主键的匹配成员
ToMany 表格映射关系一对多或多对多
Generated greendao 产生的部分,手动修改会报错
Keep 替换 Generated,greendao 不再生成和报错
Convert 数据类型转换。实体类型与数据库类型转换,实现存储和修改的便捷 converter:转换方法
columnType:数据库使用的数据类型

文件名为:UserBean.java


@Entity(nameInDb = "user_table")
public class UserBean {
    @Id(autoincrement = true)
    @Unique
    @Property(nameInDb = "user_id")
    private Long userId;

    @NotNull
    @Property(nameInDb = "group_id")
    private Long groupId;

    @NotNull
    @Property(nameInDb = "user_name")
    private String userName;

    @Unique
    @NotNull
    @Property(nameInDb = "user_number")
    private String userNumber;
}

另外增加一个 GroupBean.java 实体,内容如下:


@Entity(nameInDb = "group_table")
public class GroupBean {
    @Id(autoincrement = true)
    @Unique
    @Property(nameInDb = "group_id")
    private Long groupId;

    @Unique
    @NotNull
    @Property(nameInDb = "group_name")
    private String groupName;
}

2. 编译后,生成完善的数据库实体方法

编译后可以看到 UserBean.java 文件增加了不少接口。


@Entity(nameInDb = "user_table")
public class UserBean {
    @Id(autoincrement = true)
    @Unique
    @Property(nameInDb = "user_id")
    private Long userId;

    @NotNull
    @Property(nameInDb = "group_id")
    private Long groupId;

    @NotNull
    @Property(nameInDb = "user_name")
    private String userName;

    @Unique
    @NotNull
    @Property(nameInDb = "user_number")
    private String userNumber;

    @Generated(hash = 1853997691)
    public UserBean(Long userId, @NotNull Long groupId, @NotNull String userName,
            @NotNull String userNumber) {
        this.userId = userId;
        this.groupId = groupId;
        this.userName = userName;
        this.userNumber = userNumber;
    }

    @Generated(hash = 1203313951)
    public UserBean() {
    }

    public Long getUserId() {
        return this.userId;
    }

    public void setUserId(Long userId) {
        this.userId = userId;
    }

    public Long getGroupId() {
        return this.groupId;
    }

    public void setGroupId(Long groupId) {
        this.groupId = groupId;
    }

    public String getUserName() {
        return this.userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getUserNumber() {
        return this.userNumber;
    }

    public void setUserNumber(String userNumber) {
        this.userNumber = userNumber;
    }
}

自动生成的方法:

  1. 无参构造函数
  2. 有参构造函数
  3. getter / setter 方法

除此之外,还生成了 DaoMaster,DaoSession,UserBeanDao (GroupBean 对应生成 GroupBeanDao ) 等文件。
下面来介绍各自的功能。

3. DaoMaster,DaoSession,Dao 文件

文件 描述 相应文件
DaoMaster 保存数据库对象(SQLiteDatabase) DaoMaster
DaoSession 管理所有的 Dao 对象 DaoSession
Dao 数据访问对象(Data Access Object),可以通过 Dao 操作数据实体 UserBeanDao、GroupBeanDao
Entity 数据实体(每个实体对应数据库内的一个表) UserBean、GroupBean

通过 GreenDao 操作数据库。

1. 初始化数据库


private static DaoSession mDaoSession;

public initDatabase(Context context, String databaseFileName) {
    DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(context, databaseFileName, null);
    SQLiteDatabase db = helper.getWritableDatabase();
    DaoMaster daoMaster = new DaoMaster(db);
    mDaoSession = daoMaster.newSession();

    // 打开查询的LOG
    // QueryBuilder.LOG_SQL = true;
    // QueryBuilder.LOG_VALUES = true;
}

public static DaoSession getDaoSession() {
    return mDaoSession;
}

执行 initDatabase 初始化数据库后,可以通过 getDaoSession 拿到 DaoSession 对每个数据库实体进行处理。

2. 插入数据

以以下数据为例:

组织(GroupName) 名字(UserName) 编号(UserNumber)
CHN LiTianYu 1-0090
USA LiBill 10-8082
XYZ MiXue 2-720

public void insertData(String groupName, String userName, String userNumber) {
    GroupBean groupBean = new GroupBean(null, groupName);                       // Group 主键(groupId)是自动增加的,使用 null 就可以自增了。
    long keyGroupBean = getDaoSession().getGroupBeanDao().insert(groupBean);    // 返回值是插入库后的 Group 实体的 key
    groupBean = getDaoSession().getGroupBeanDao().load(keyGroupBean);           // 通过 key 可以获取到插入的 Group 数据
    
    UserBean userBean = new UserBean(null, groupBean.getGroupId(), userName, userNumber);  // User 主键(userId)也是自动增加的,但是 groupId 需要通过关联的 Group 获取。
    getDaoSession().getUserBeanDao().insert(userBean);
}

3. 删除数据


// 删除单个数据
getDaoSession().getUserBeanDao().delete(userBean);

// 删除多个数据 (userBeanList 类型为 List)
getDaoSession().getUserBeanDao().deleteInTx(userBeanList);

// 删除所有数据
getDaoSession().getUserBeanDao().deleteAll();

4. 查找数据


// 查询 userNumber 为 "1-0090" 的 user
getDaoSession().getUserBeanDao().queryBuilder().where(UserBeanDao.Properties.UserNumber.eq("1-0090")).unique();

// 查询 userName 形如 "Lixxx" 的 user ( 类似于 SQLite 的 like 模糊查询语法 )
getDaoSession().getUserBeanDao().queryBuilder().where(UserBeanDao.Properties.UserNumber.eq("Li%")).unique();

// 查询 groupName 为 "CHN" 的 user ( 关联表查询 )
QueryBuilder qb = getDaoSession().getUserBeanDao().queryBuilder();    // 需要获取 User 数据,所以 qb 为 User
Join join_UserBean = qb.join(UserBean.class, UserBeanDao.Properties.UserId);    // 设置 User 的关联规则(根据 UserBean.userId == UserBean.userId)
Join join_GroupBean = qb.join(join_UserBean, UserBeanDao.Properties.GroupId, GroupBean.class, GroupBeanDao.Properties.GroupId);     // 设置 Group 的关联规则(根据 UserBean.groupId == GroupBean.groupId)
join_GroupBean.where(GroupBeanDao.Properties.GroupName.eq("CHN"));      // 其它查找条件(User和Group已经关联起来了)
qb.list();      // 返回查找结果

使用 .unique() 为获取查询满足要求的第一个数据。
使用 .list() 为获取所有满足要求的数据(返回结果为 List<> 类型)

多表关联查询稍显复杂,可以通过 ToOne、ToMany 设置表与表之间的关系进行直接访问的查询。
但是大数据量时效率没有使用以上方法快。

5. 更改数据


// 更改单个数据
getDaoSession().getUserBeanDao().update(userBean);

// 更改多个数据 (userBeanList 类型为 List)
getDaoSession().getUserBeanDao().updateInTx(userBeanList);

其它

1. 类型转换

SQLite 数据库的数据类型有限(甚至不支持float),而作为对象则允许所有java的类型(数组、各种类等)。
因此在 GreenDao 中支持类型转换(从数据库数据类型转换为实体的数据类型),方便对实体进行修改查询。

请在对应的成员中加入 @Convert ,如:


// 以实体的 float[] 与 数据库的 TEXT 类型转换为例
@Convert(converter = ConvertFloatArrayToString.class, columnType = String.class)
private float[] featureData;

转换方法:


public class ConvertFloatArrayToString implements PropertyConverter {
    // 数据库类型 -> 实体类型
    public float[] convertToEntityProperty(String databaseValue) {
        String[] strList = databaseValue.split(",");
        float[] floatList = new float[strList.length];
        for (int i = 0, len = strList.length; i < len; ++ i) {
            floatList[i] = Float.parseFloat(strList[i]);
        }
        return floatList;
    }

    // 实体类型 -> 数据库类型
    public String convertToDatabaseValue(float[] entityProperty) {
        String str = "" + entityProperty[0];
        for (int i = 1, len = entityProperty.length; i < len; ++ i) {
            str += "," + entityProperty[i];
        }
        return str;
    }
}

这样就可以读取直接操作 float[] 进行读写,不需要每次都手动转为 TEXT(对应 Java 中的 String)或者解析 TEXT 了。

2. 其它的其它

比如加密,比如缓存,比如懒加载。。。有空再补吧

你可能感兴趣的:(Android)