GreenDao3.2.0的使用

2017/3/31 posted in  Android

前言

我相信,在平时的开发过程中,大家一定会或多或少地接触到SQLite。然而在使用它时,我们往往需要做许多额外的工作,像编写 SQL语句与解析查询结果等。所以,适用于Android ORM框架也就孕育而生了,现在市面上主流的框架有OrmLite、SugarORM、Active Android、Realm与greenDAO。但是使用数量最多的还是Realm与greenDAO,所以为了目前的项目需要写个文档记录一下greenDAO的使用。简单的讲,greenDAO是一个将对象映射到SQLite 数据库中的轻量且快速的ORM解决方案。(greenDAO is a light & fast ORM solution that maps objects to SQLite databases.)。

20170406149144616018213.png
20170406149144616018213.png

这个图对于理解greenDAO是必不可少的。

greenDAO特点

  • 性能最大化,可能是Android平台上最快的ORM框架
  • 易于使用的API
  • 最小的内存开销
  • 依赖体积小
  • 支持数据库加密
  • 强大的社区支持

配置greenDAO

app的Gradle中配置:

apply plugin: 'org.greenrobot.greendao'

dependencies {
        compile 'org.greenrobot:greendao:3.2.0'
}

project的Gradle中配置:

buildscript {
    repositories {
        jcenter()
        mavenCentral()
    }
    dependencies {
        classpath 'org.greenrobot:greendao-gradle-plugin:3.2.1' 
    }
}

在app的Gradle配置数据库版本等信息

greendao {
    schemaVersion 1                         //对应当前数据库版本c
    daoPackage 'com.koma.greendao.gen'  //由GreenDao自动生成代码所在的包名,默认的是在项目包下面新建一个gen。
    targetGenDir 'src/main/java'        //设置自动生成代码的目录
}

使用greenDAO

使用流程

在项目中新建一个表字段的实体类,不需要自己写get和set方法,在生成表的时候会自动生成。不要忘了在类名上标记@Entity注解如图:

20170406149144836199963.png
20170406149144836199963.png

选择build ——Make Project 一切顺利的话则会在包名下生成数据库的操作类,如下图:

20170406149144839868744.png
20170406149144839868744.png

Ps:新建一个实体类,对项目进行Make Project,成功之后会在daoPackage目录下自动生成代码

在项目中定义实体类

@Entity
public class User  {
    @Id(autoincrement = true)
    private Long id;
    @Property(nameInDb = "USERNAME")
    private String name;
    private int age;
}

注解:

  • @Entity——————————标识实体类,greenDAO会映射成sqlite的一个表,表名为实体类名的大写形式
    • schema:告知GreenDao当前实体属于哪个schema
    • active:标记一个实体处于活跃状态,活动实体有更新、删除和刷新方法
    • nameInDb:在数据库中使用的别名,默认使用的是实体的类名
    • indexes:定义索引,可以跨越多个列
    • createInDb:标记创建数据库表
  • 基础属性注解
    • @Id——————————标识主键,该字段的类型为long或Long类型,autoincrement设置是否自动增长
    • @Property——————————标识该属性在表中对应的列名称, nameInDb设置名称
    • @Transient——————————标识该属性将不会映射到表中,也就是没有这列
    • @NotNull——————————设置表中当前列的值不可为空
  • 索引注解
    • @Unique——————————向数据库列添加了一个唯一的约束
    • @Index——————————使用@Index作为一个属性来创建一个索引;定义多列索引(@link Entity#indexes())
  • 关系注解
    • @ToMany——————————定义与多个实体对象的关系
    • @ToOne——————————定义与另一个实体(一个实体对象)的关系
  • @Convert——————————指定自定义类型(@linkPropertyConverter)
  • @Generated——————————greenDAO运行所产生的构造函数或者方法,被此标注的代码可以变更或者下次运行时清除
  • @JoinEntity——————————定义表连接关系
  • @JoinProperty——————————定义名称和引用名称属性关系
  • @Keep——————————注解的代码段在GreenDao下次运行时保持不变
    • 注解实体类:默认禁止修改此类
    • 注解其他代码段,默认禁止修改注解的代码段
  • @OrderBy——————————指定排序

初始化GreenDao

一般建议在Application中初始化数据库

private void setupDataBase(Context context){
    DaoMaster.DevOpenHelper openHelper = new DaoMaster.DevOpenHelper(context,DATA_BASE_NAME);
    Database db = openHelper.getWritableDb();
    DaoMaster daoMaster = new DaoMaster(db);
    mDaoSession = daoMaster.new Session();
}

public static DaoSession getDaoSession(){
    return mDaoSession;
}

DevOpenHelper有两个重载方法:

  • DevOpenHelper(Context context,String name)
  • DevOpenHelper(Context context,String name,CursorFactory factory) context上下文这个不用多说,name数据库的名字,cursorFactory游标工厂,一般不用,传入null或者使用两个参数的方法即可。我们对外提供一个getDaoSession()的方法供外部使用。

对数据库的增、删、改、查操作

首先获得UserDao的对象

UserDao userDao = GreenDaoManager.getInstance().getNewSession().getUserDao();

增:

User user = new User(null, name, age, studentId);
userDao.insert(user);
insert(T entity)       当指定主键在表中存在时会发生异常
insertOrReplace(T entity)      当指定主键在表中存在时会覆盖数据
insertInTx(Iterable<T> entities)      批量插入数据

删:

User user = new User(null, name, age, studentId);    
userDao.delete(user)
 
delete(T entity)       删除数据
deleteByKey(K key)     指定主键删除数据
deleteInTx(Iterable<T> entities)      批量删除数据
deleteByKeyInTx(Iterable<K> keys)      批量按数据删除数据        
deleteAll()    删除所有数据

改:

User user = new User(id, name, age, studentId);
userDao.update(user);
 
update(T entity)       修改数据,主键需相同
updateInTx(Iterable<T> entities)      批量更新数据

查:

userDao.load(id)
 
load(K key)    根据id查找数据
loadByRowId(long rowId)        根据行号查找数据
loadAll()      查找全部数据
查询功能

GreeDAO提供了各种各种的查询API,参见http://greenrobot.org/greendao/documentation/queries/

20170406149144856315135.png
20170406149144856315135.png

如果需要在控制台查看GreenDao是怎么拼装SQL语句的,在OnCreate()方法中打开下面的开关:

20170406149144860095322.png
20170406149144860095322.png

条件查询
//查询所有数据  
public void queryAll() {  
    List<Person> persons = mPersonDao.queryBuilder().list();  
}  
  
//eq: equal 精确查询  名字等于jackie  
public void queryEq() {  
    Person person = mPersonDao.queryBuilder().where(PersonDao.Properties.Name.eq("jackie")).unique();  
}  
  
//notEq: not equal 精确查询  名字不等于jackie  
public void queryNotEq() {  
    Person person = mPersonDao.queryBuilder().where(PersonDao.Properties.Name.notEq("jackie")).unique();  
}  
  
//like  模糊查询  名字以jackie开头  
public void queryLike() {  
    Person person = mPersonDao.queryBuilder().where(PersonDao.Properties.Name.like("jackie")).unique();  
    //通配符  
    List<Person> persons = mPersonDao.queryBuilder().where(PersonDao.Properties.Name.like("jackie%")).list();  
}  
  
//between 区间查询 年龄在20到30之间  
public void queryBetween() {  
    List<Person> persons = mPersonDao.queryBuilder().where(PersonDao.Properties.Age.between(20, 30)).list();  
}  
  
//gt: greater than 半开区间查询,年龄大于18  
public void queryGt() {  
    List<Person> persons = mPersonDao.queryBuilder().where(PersonDao.Properties.Age.gt(18)).list();  
}  
  
//ge: greater equal 半封闭区间查询,年龄大于或者等于18  
public void queryGe() {  
    List<Person> persons = mPersonDao.queryBuilder().where(PersonDao.Properties.Age.ge(18)).list();  
}  
  
//lt: less than 半开区间查询,年龄小于18  
public void queryLt() {  
    List<Person> persons = mPersonDao.queryBuilder().where(PersonDao.Properties.Age.lt(18)).list();  
}  
  
//le: less equal 半封闭区间查询,年龄小于或者等于18  
public void queryLe() {  
    List<Person> persons = mPersonDao.queryBuilder().where(PersonDao.Properties.Age.le(18)).list();  
}  
  
//名字以jackie开头,年龄升序排序  
public void queryLikeAsc() {  
    //通配符  
    List<Person> persons = mPersonDao.queryBuilder().where(PersonDao.Properties.Name.like("jackie%")).orderAsc(PersonDao.Properties.Age).list();  
}  
  
//名字以jackie开头,年龄降序排序  
public void queryLikeDesc() {  
    List<Person> persons = mPersonDao.queryBuilder().where(PersonDao.Properties.Name.like("jackie%")).orderDesc(PersonDao.Properties.Age).list();  
}  

当上述查询满足不了业务逻辑时,可以自定义查询语句:

20170406149144866729492.png
20170406149144866729492.png

多线程查询

首先来看错误的方法:

2017040614914487051050.png
2017040614914487051050.png

错误截图:

2017040614914487315508.png
2017040614914487315508.png

从上面可以看出,只能在创建Query对象的线程中调用查询方法!

解决方法:

20170406149144875721207.png
20170406149144875721207.png

一对一查询

20170406149144879034921.png
20170406149144879034921.png

一对一、一对多关系

greenDAO目前只支持一对一和一对多关系,并不支持多对多关系

一对一:
private Long studentId;
@ToOne(joinProperty = "studentId")
private Studentstudent;
@Generated(hash = 313494093)
public Student getStudent() {
    Long __key = this.studentId;
    if (student__resolvedKey == null || !student__resolvedKey.equals(__key)) {
        final DaoSession daoSession = this.daoSession;
        if (daoSession == null) {
            throw new DaoException("Entity is detached from DAO context");
        }
        StudentDao targetDao = daoSession.getStudentDao();
        Student studentNew = targetDao.load(__key);
        synchronized (this) {
            student = studentNew;
            student__resolvedKey = __key;
        }
    }
    return student;
}

使用@ToOne标记,joinProperty指定一个字段,如果没有定义,会自动生成一个字段,get方法时自动生成的,使用@Generated标记,代表自动生成的,无需动代码。

一对多(有三种映射形式)
@ToMany(referencedJoinProperty = "teacherId")
private List<Teacher> teacherList;

referencedJoinProperty:指定目标实体中与源实体相对应的外键。

@ToMany(joinProperties = {
        @JoinProperty(name="studentTag",referencedName = "teacherTag")
})
private List<Teacher> teacherList;

JoinProperty: 对于复杂一点的关系可以定义一组@JoinProperty注解。每个@JoinProperty注解都需要有源实体中的源属性和对应实体中的引用属性。

@ToMany
@JoinEntity(
        entity = Teacher.class,
        sourceProperty = "teacherId",
        targetProperty = "id"
)
private List<Teacher> teacherList;

@JoinEntity:在做NM多对多映射的时候使用

数据库的升级

数据库升级最麻烦的就是数据保存的问题,下面是数据库升级时执行的代码。

/** WARNING: Drops all table on Upgrade! Use only during development. */
public static class DevOpenHelper extends OpenHelper {
    public DevOpenHelper(Context context, String name) {
        super(context, name);
    }

    public DevOpenHelper(Context context, String name, CursorFactory factory) {
        super(context, name, factory);
    }

    @Override
    public void onUpgrade(Database db, int oldVersion, int newVersion) {
        Log.i("greenDAO", "Upgrading schema from version " + oldVersion + " to " + newVersion + " by dropping all tables");
        dropAllTables(db, true);
        onCreate(db);
    }
}

第一句注释的话已经告诉我们数据库升级时会删除所有的表,重新创建。所以我们可以自定义一个类继承OpenHelper,在升级时先生成临时表,删除旧表创建新表后,再把临时表的数据导入进去。有牛人已经做出这个项目来了,我就厚颜无耻选择直接用了。

在project的gradle添加
allprojects {
    repositories {
        jcenter()
        maven { url "https://jitpack.io" }
    }
}
在app的gradle添加:

compile 'com.github.yuweiguocn:GreenDaoUpgradeHelper:v1.3.0'

新建一个类
public class MySQLiteOpenHelper extends DaoMaster.OpenHelper{
    public MySQLiteOpenHelper(Contextcontext,Stringname){
        super(context,name);
    }
    public MySQLiteOpenHelper(Contextcontext,String name,SQLiteDatabase.CursorFactoryfactory) {
        super(context,name,factory);
    }
    @Override
    public void onUpgrade(SQLiteDatabasedb,int oldVersion,intnewVersion) {
        Log.d("SQLite","数据库从"+oldVersion+"升级到"+newVersion);
        MigrationHelper.migrate(db,UserDao.class,StudentDao.class);
    }
}

使用时直接用MySQLiteOpenHelper代替DaoMaster.OpenHelper。

实战

下面就以购物车的实战来使用GreenDao,这里的购物车展示图如下

20170406149144890188892.jpg
20170406149144890188892.jpg


我们所知道的数据库操作需要:数据库名、表名、字段名,缺一不可,下面就是这三项的创建

一、创建Bean对象(表名和字段名)

GreenDao需要创建Bean对象之后,该Bean对象就是表名,而它的属性值就是字段名,其 实现是通过注释的方式来实现的,下面是购物车的Bean对象(每个Bean对象对应一张表)

@Entity
public class Shop{

    //表示为购物车列表
    public static final int TYPE_CART = 0x01;
    //表示为收藏列表
    public static final int TYPE_LOVE = 0x02;

    //不能用int
    @Id(autoincrement = true)
    private Long id;
    //商品名称
    @Unique
    private String name;
    //商品价格
    @Property(nameInDb = "price")
    private String price;
    //已售数量
    private int sell_num;
    //图标url
    private String image_url;
    //商家地址
    private String address;
    //商品列表类型
    private int type;
}

之后进行Make Project
生成Bean实体的构造方法和getset方法和DaoMasterDaoSessionDAOS

二、创建数据库

public class BaseApplication extends Application {

    private static DaoSession daoSession;

    @Override
    public void onCreate() {
        super.onCreate();
        //配置数据库
        setupDatabase();
    }

    /**
     * 配置数据库
     */
    private void setupDatabase() {
        //创建数据库shop.db"
        DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(this, "shop.db", null);
        //获取可写数据库
        SQLiteDatabase db = helper.getWritableDatabase();
        //获取数据库对象
        DaoMaster daoMaster = new DaoMaster(db);
        //获取Dao对象管理者
        daoSession = daoMaster.newSession();
    }

    public static DaoSession getDaoInstant() {
        return daoSession;
    }
}

GreenDao已经将我们的数据库创建缩成几句话,代码会自动将Bean对象创建成表,不再是传统的手写SQL语句。这里的数据库创建只需要在Application中执行一次即可,这里对几个类进行解释

  • DevOpenHelper:创建SQLite数据库的SQLiteOpenHelper的具体实现
  • DaoMaster:GreenDao的顶级对象,作为数据库对象、用于创建表和删除表
  • DaoSession:管理所有的Dao对象,Dao对象中存在着增删改查等API

由于我们已经创建好了DaoSession和Shop的Bean对象,编译后会自动生成我们的ShopDao对象,可通过DaoSession获得

ShopDao dao = daoSession.getShopDao();

这里的Dao(Data Access Object)是指数据访问接口,即提供了数据库操作一些API接口,可通过dao进行增删改查操作。

三、数据库的增删改查

public class LoveDao {

    /**
     * 添加数据,如果有重复则覆盖
     *
     * @param shop
     */
    public static void insertLove(Shop shop) {
        BaseApplication.getDaoInstant().getShopDao().insertOrReplace(shop);
    }

    /**
     * 删除数据
     *
     * @param id
     */
    public static void deleteLove(long id) {
        BaseApplication.getDaoInstant().getShopDao().deleteByKey(id);
    }

    /**
     * 更新数据
     *
     * @param shop
     */
    public static void updateLove(Shop shop) {
        BaseApplication.getDaoInstant().getShopDao().update(shop);
    }

    /**
     * 查询条件为Type=TYPE_LOVE的数据
     *
     * @return
     */
    public static List<Shop> queryLove() {
        return BaseApplication.getDaoInstant().getShopDao().queryBuilder().where(ShopDao.Properties.Type.eq(Shop.TYPE_LOVE)).list();
    }

    /**
     * 查询全部数据
     */
    public static List<Shop> queryAll() {
        return BaseApplication.getDaoInstant().getShopDao().loadAll();
    }
}

GreenDao的封装更加短小精悍,语义明朗,下面对GreenDao中Dao对象其他API的介绍

  • 增加单个数据
    • getShopDao().insert(shop);
    • getShopDao().insertOrReplace(shop);
  • 增加多个数据
    • getShopDao().insertInTx(shopList);
    • getShopDao().insertOrReplaceInTx(shopList);
  • 查询全部
    • List< Shop> list = getShopDao().loadAll();
    • List< Shop> list = getShopDao().queryBuilder().list();
  • 查询附加单个条件
    • .where()
    • .whereOr()
  • 查询附加多个条件
    • .where(, , ,)
    • .whereOr(, , ,)
  • 查询附加排序
    • .orderDesc()
    • .orderAsc()
  • 查询限制当页个数
    • .limit()
  • 查询总个数
    • .count()
  • 修改单个数据
    • getShopDao().update(shop);
  • 修改多个数据
    • getShopDao().updateInTx(shopList);
  • 删除单个数据
    • getTABUserDao().delete(user);
  • 删除多个数据
    • getUserDao().deleteInTx(userList);
  • 删除数据ByKey
    • getTABUserDao().deleteByKey();