数据库学习之 GreenDAO 实战

前言

上一篇 对 greenDAO 进行了基本的介绍,包括配置、注解含义、基本的操作。本篇主要对 greenDAO 的增删改查的一些高级用法进行介绍,有时可能业务需求,简单的增删改查可能满足不了,比如数据较大、多表联查,这时就需要学一下 greenDAO 的高级用法。

本篇主要内容,就在这个粗略的草图中。

GreenDAO_Structure

1. 建表

对于移动端来说,数据库不会很复杂,毕竟空间和处理能力有限,基本上用到的是一些单独的一张表,或者一对一,一对多这样的表。那我们就来试一下一对多的表。

两张表,一张里面存储班级信息,另一张里面存储学生信息,班级里面有多个学生,这样就建立起一对多的关系。

班级:名字和编号

学生:学号,名字,班级,年龄

班级实体类


@Entity
public class Clazz {

    @Unique
    @Id(autoincrement = true)
    private Long id;

    private String name;

    // 一对多 注解,采用 referencedJoinProperty 方式,clazID 为学生的班级号
    @ToMany(referencedJoinProperty = "clazID")
    private List<Student> studentList;
    
    // hash 值由编译时自动生成,使用时只需要 对构造函数注解  @Generated 即可
    @Generated(hash = 1166360579)
    public Clazz() {
    }

    @Generated(hash = 582074864)
    public Clazz(Long id, String name) {
        this.id = id;
        this.name = name;
    }

学生实体类


@Entity(nameInDb = "Student")
public class Student {

    @Unique
    @Id(autoincrement = true)
    private Long id;

    // 修改字段在数据库中的名字
    @Property(nameInDb = "studentID")
    private int studentID;

    private String name;
    
    // 一对多 字段关联 属性
    private long clazID;
    private int age;

    @Generated(hash = 1556870573)
    public Student() {
    }

    @Generated(hash = 435762767)
    public Student(Long id, int studentID, String name, long clazID, int age) {
        this.id = id;
        this.studentID = studentID;
        this.name = name;
        this.clazID = clazID;
        this.age = age;
    }

造数据

    List<Clazz> clazzList = new ArrayList<>();
    List<Student> studentList = new ArrayList<>();
    ...
private void initData() {
    int studenId = 10;
    long id = 0;
    for (int i = 1; i < 4; i++) {
        Clazz clazz = new Clazz();
        clazz.setId(new Long(i));
        clazz.setName(i + "班");
        for (int j = 0; j < 5; j++) {
            Student student = new Student();
            student.setAge(18 + j);
            student.setId(id++);
            student.setStudentID(studenId++);
            student.setName("Ralf");
            student.setClazID(clazz.getId());
            studentList.add(student);
        }
        clazzList.add(clazz);
    }
}

建表和准备好数据之后,就可以将数据插入到数据库中。可以使用 insertInTx 这个方法,将数据批量插入到数据库中,后面会讲到封装

/**
     * 插入多条数据
     *
     * @param entities
     */
    public <T> void inster(List<T> entities) {
        isDaoNull();
        mAbstractDao.insertInTx(entities);
    }

2. 调试

之前调试使用 adb 命令调试,使用原生的查询数据,“select * from table” ,类似于这样的语句。有点麻烦。这里推荐的一个工具 **Android-Debug-Database **

使用这个开源库调试数据库非常方便。

特色:

可视化查看数据库

能够直接对数据库增删改查

可以查看你的应用中所有的shared preferences

使用

在你的build.gradle添加如下:

debugCompile 'com.amitshekhar.android:debug-db:1.0.0'

debugCompile的作用:只在你debug编译时起作用,当你release的时候就没必要使用它了。
下面当你在App启动的时候,你要注意查看下你的logcat,会有这么一行:
D/DebugDB: Open http://XXX.XXX.X.XXX:8080
点击打开浏览器,你就可以看到你的App中的数据库,和 shared preferences

3. 增删改查

3.1 增

// 单条数插入
public long insert(T entity) {}

// 批量插入数据
public void insertInTx(T... entities) {}

public void insertInTx(Iterable<T> entities) {}

public void insertInTx(Iterable<T> entities, boolean setPrimaryKey) {}
// 插入单条数据,如果存在,则替换
public long insertOrReplace(T entity) {}

// 批量插入数据,如果存在,则替换
public void insertOrReplaceInTx(T... entities) {}

public void insertOrReplaceInTx(Iterable<T> entities) {}

public void insertOrReplaceInTx(Iterable<T> entities, boolean setPrimaryKey) {}
// 类似于insertOrReplace()方法,但比它更有效率,因为当key已经存在时,不需要查询直接更新
public void save(T entity) {}

3.2 删

// 删除相应表的全部数据
public void deleteAll() {}

// 删除给定PK的实体。目前,仅支持惟一PK实体。
public void deleteByKey(K key) {}

// 使用事务删除数据库中给定键的所有实体。批量删除。
public void deleteByKeyInTx(K... keys) {}
public void deleteByKeyInTx(Iterable<K> keys) {}

// 删除给定的实体
public void delete(T entity) {}

//使用事务删除数据库中给定的实体。批量删除。每个entity的id属性必须有值,否则报错。其实就是删除与entity主键值相同的数据,entity的其他数据值与表中数据值不一致时,也会删除。
public void deleteInTx(T... entities) {}
public void deleteInTx(Iterable<T> entities) {}

3.3 改

// 更新一条实体数据,entity的id属性必须有值,否则报错。若entity的id值表中不存在时,不会报错。
public void update(T entity) {}

// 更新多条数据,每个entity的id属性必须有值,否则报错。若entity的id值表中不存在时,不会报错。
public void updateInTx(T... entities) {}

public void updateInTx(Iterable<T> entities) {}

3.4 查

普通查询

// 删除指定主键实体
public T load(K key) {}

// 加载给定RowId的实体。传入:key or null。返回:该实体 或 null。
public T loadByRowId(long rowId) {}

// 从数据库加载所有可用的实体
public List<T> loadAll() {}

高级查询

// 原始查询语句
public List<T> queryRaw(String where, String... selectionArg) {}

QueryBuilder

图片

构建 QueryBuilder

QueryBuilder<T> queryBuilder = mDaoSession.queryBuilder(claz);

QueryBuilder 有一系列的方法,其中注意 build() 和 list()方法

QueryBuilder

其中,如果使用 build 方法,则可以将得到的 Query 对象作为参数传递给查询方法

// 异步查询,后面详细讲
public AsyncOperation queryList(Query<?> query) {}

另外,也可以使用 QueryBuilder 对象直接查询,使用 list 方法

// 执行查询并将返回一个包含所有entity的list为结果,直接加载在内存中。
public List<T> list() {}

例子

List<Student> list = dbUtil.queryBuilder(Student.class)
                        .limit(3)
                        .distinct()
                        .build()
                        .list();

WhereCondition
WhereCondition 对象可以使用下面的对象构建,然后作为参数,WhereCondition 相当于使用原始的查询语句。

between(Object value1, Object value2): BETWEEN … AND …
eq(Object value): equal (‘=’)
notEq(Object value): not equal (‘<>’)
gt(Object value): than (‘>’)
lt(Object value): less than (‘<’)
ge(Object value): greater or equal (‘>=’)
le(Object value): less or equal (‘<=’)
like(String value): LIKE
isNotNull(): IS NOT NULL
isNull(): IS NULL
in(Object… inValues): IN (…, …, …)
notIn(Object… notInValues): NOT IN (…, …, …)
in(Collection< ?> inValues): IN (…, …, …)
notIn(Collection< ?> notInValues): NOT IN (…, …, …)

例子

List<Student> list = dbUtil.queryBuilder(Student.class)
        .limit(3)
        .distinct()
        .where(StudentDao.Properties.Id.eq(1))
        .list();

join 多表联查

join 方法很多,下面针对给出的join方法逐个分析,使用时根据查询的需求来决定。

// 通过联合另一个表扩展查询,sourceProperty 和需要联合的表中的 destinationEntityClass 的主键匹配
public <J> Join<T, J> join(Property sourceProperty, Class<J> destinationEntityClass) {}

例子

// 构建 QueryBuilder
QueryBuilder<Student> queryBuilder = dbUtil.create(Student.class).queryBuilder(Student.class);
//Properties.ClazID和Clazz.class的主键匹配
queryBuilder.join(StudentDao.Properties.ClazID, Clazz.class)
        .where(ClazzDao.Properties.Id.eq(1));

List<Student> studentList1 = queryBuilder.list();
// 和上面的 join 方法类似,多了一个参数 destinationProperty,这个属性相当于自己指定主键;也就是说 sourceProperty 和 destinationEntityClass表中的 destinationProperty 字段匹配;但不限与destinationEntityClass的主键,也可以是其他字段
public <J> Join<T, J> join(Property sourceProperty, Class<J> destinationEntityClass, Property destinationProperty) {}

例子

// 构建 QueryBuilder
QueryBuilder<Student> queryBuilder = dbUtil.create(Student.class).queryBuilder(Student.class);
// 指定主键 ClazzDao.Properties.Id
queryBuilder.join(StudentDao.Properties.ClazID, Clazz.class,ClazzDao.Properties.Id)
        .where(ClazzDao.Properties.Id.eq(1));

List<Student> studentList1 = queryBuilder.list();
// 需要查询的表中的主键和联合表中字段匹配,即 destinationProperty
public <J> Join<T, J> join(Class<J> destinationEntityClass, Property destinationProperty) {

例子

QueryBuilder<Clazz> queryBuilder1 = dbUtil.create(Clazz.class).queryBuilder(Clazz.class);
// Clazz 班级的主键是学生 Student 表中的外键,即 StudentDao.Properties.ClazID
queryBuilder1.join(Student.class,StudentDao.Properties.ClazID)
        .where(StudentDao.Properties.StudentID.eq(11));

List<Clazz> clazzList = queryBuilder1.list();

多表联查

public <J> Join<T, J> join(Join<?, T> sourceJoin, Property sourceProperty, Class<J> destinationEntityClass,
                               Property destinationProperty) {}

例子

//查询在欧洲人口超过100000的城市
QueryBuilder qb = cityDao.queryBuilder().where(Properties.Population.ge(1000000));
Join country = qb.join(Properties.CountryId, Country.class);
Join continent = qb.join(country, CountryDao.Properties.ContinentId,
Continent.class, ContinentDao.Properties.Id);
continent.where(ContinentDao.Properties.Name.eq("Europe"));
List bigEuropeanCities = qb.list();

3.5 异步的增删改查

当数据量很大时 ,需要考虑使用异步方式,这样不至于导致线程堵塞等问题。

异步操作相对也比较简单 ,主要使用 有这么几步:

1. 获取 AsyncSession
AsyncSession asyncSession = mDaoSession.startAsyncSession();
2. 设置回调

回调方法是在主线程,查询是在子线程

asyncSession.setListenerMainThread(new AsyncOperationListener() {
            @Override
            public void onAsyncOperationCompleted(AsyncOperation operation) {
                ...
            }
        });
3. 执行

asyncSession 也有一系列的增删改查

asyncSession.loadAll(object);

下面看一个完成的异步查询的例子

public <T> void queryAsyncAll(Class<T> object, Query<T> query) {
    AsyncSession asyncSession = mDaoSession.startAsyncSession();
    asyncSession.setListenerMainThread(new AsyncOperationListener() {
        @Override
        public void onAsyncOperationCompleted(AsyncOperation operation) {
            if (operation.isCompleted() && mCallBack != null) {
                List<T> result = (List<T>) operation.getResult();
                mCallBack.onSuccess(result);
            } else if (operation.isFailed() && mCallBack != null) {
                mCallBack.onFailed();
            }
        }
    });
    if (query == null) {
        asyncSession.loadAll(object);
    } else {
        asyncSession.queryList(query);
    }
}

4 封装

4.1 构造单例

下面这部分不难理解,构造 DbUtil 的单例,三个重载函数,可以指定名字,可以设置数据库密码,或者直接使用默认的名字,不加密。

主要是创建 DaoMaster 和 DaoSession 对象,然后使用 DaoSession 对象来获得每个 Entity 对应的 EntityDao 对象,常用的增删改查都是使用 EntityDao 对象来完成的。DaoSession 也具有增删改查功能,而且具有缓存功能,当查询一个对象2次时,取出来的对象是同一个,即使 Entity 的数据有变动,所以这时需要清除一下缓存 DaoSession.clear(),上文提到的异步操作,也是需要 DaoSession 来完成。

private static final String DEFAULT_DATABASE_NAME = "Database.db";
private static final String DEFAULT_DATABASE_PASSWORD = "hahaha";
private DaoSession mDaoSession;
private DaoMaster mDaoMaster;
private AbstractDao mAbstractDao;
private static DbUtil mDbUtils;
private DaoMaster.DevOpenHelper mHelper;
private DbCallBack mCallBack;
    
private DbUtil(Application application, String dbName, String passWord) {
    if (mHelper == null) {
        mHelper = new DaoMaster.DevOpenHelper(application, dbName);
    }
    if (passWord == null || passWord.isEmpty()) {
        mDaoMaster = new DaoMaster(mHelper.getWritableDb());
    } else {
        mDaoMaster = new DaoMaster(mHelper.getEncryptedReadableDb(passWord));
    }
    mDaoSession = mDaoMaster.newSession();

}

public static DbUtil getInstance(Application application) {
    return getInstance(application, DEFAULT_DATABASE_NAME);
}

public static DbUtil getInstance(Application application, String dbName) {

    return getInstance(application, dbName, "");
}

public static DbUtil getInstance(Application application, String dbName, String passWord) {

    if (mDbUtils == null) {
        synchronized (DbUtil.class) {
            if (mDbUtils == null) {
                mDbUtils = new DbUtil(application, dbName, passWord);
            }
        }
    }

    return mDbUtils;
}

4.2 AbstractDao

这里封装的工具类里面没有使用具体的 EntityDao对象,因为EntityDao对象 是在编译后自动生成的,所以使用基类 AbstractDao,这样就可以不用做类型判断。


    /**
     * 插入一条数据
     *
     * @param dbEntity
     */
    public <T> void insert(T dbEntity) {
        setCurrentDao(dbEntity.getClass());
        mAbstractDao.insert(dbEntity);
    }
    
    private<T> void setCurrentDao(Class<T> entityClass) {
        if (mHelper == null) {
            throw new NullPointerException("You need to init mHelper first!");
        }
        mAbstractDao = mDaoSession.getDao(entityClass);
    }

4.3 增删改查(主线程)

下面的增删改查方法,是在主线程运行的,在数据量较小时,影响很小,不会造成线程堵塞或者 ANR。

    /**
     * 获取数据库Item的数量
     *
     * @return
     */
    public <T> Long count(Class<T> entityClaz) {
        setCurrentDao(entityClaz);
        return mAbstractDao.count();
    }

    /**
     * 插入一条数据
     *
     * @param dbEntity
     */
    public <T> void insert(T dbEntity) {
        setCurrentDao(dbEntity.getClass());
        mAbstractDao.insert(dbEntity);
    }

    /**
     * 插入一条数据
     *
     * @param dbEntity
     */
    public <T> void insertOrReplace(T dbEntity) {
        setCurrentDao(dbEntity.getClass());
        mAbstractDao.insertOrReplace(dbEntity);
    }

    /**
     * 插入多条数据
     *
     * @param entities
     */
    public <T> void insertTx(List<T> entities) {

        if (entities == null || entities.size() < 1) {
            return;
        }
        setCurrentDaoOfList(entities);
        mAbstractDao.insertInTx(entities);
    }

    public <T> void insertOrReplaceInTx(List<T> entities) {
        if (entities == null || entities.size() < 1) {
            return;
        }
        setCurrentDaoOfList(entities);
        mAbstractDao.insertOrReplaceInTx(entities);
    }

    /**
     * 删除单条数据
     *
     * @param entity
     */
    public <T> void delete(T entity) {
        setCurrentDao(entity.getClass());
        mAbstractDao.delete(entity);
    }

    /**
     * 删除特定ID的数据
     *
     * @param id
     */
    public <T> void deleteById(Class<T> entityClaz, long id) {
        setCurrentDao(entityClaz);
        mAbstractDao.deleteByKey(id);
    }

    /**
     * 删除多条数据
     *
     * @param entities
     */
    public <T> void deleteList(List<T> entities) {
        setCurrentDaoOfList(entities);
        mAbstractDao.deleteInTx(entities);
    }

    /**
     * 全部删除
     */
    public<T> void deleteAll(Class<T> claz) {
        setCurrentDao(claz);
        mAbstractDao.deleteAll();
    }

    /**
     * 更新单条数据
     *
     * @param entity
     */
    public <T> void updateData(final T entity) {
        setCurrentDao(entity.getClass());
        mAbstractDao.update(entity);
    }
    
    /**
     * 更新多条数据
     *
     * @param entities
     */
    public <T> void updateListData(Collection<T> entities) {
        setCurrentDaoOfList(entities);
        mAbstractDao.updateInTx(entities);
    }

    /**
     * 查询特定ID的数据
     *
     * @param id
     * @return
     */
    public <T> T queryById(Class<T> claz,long id) {
        setCurrentDao(claz);
        return (T) mAbstractDao.load(id);
    }

    /**
     * 查询全部数据
     *
     * @return
     */
    public <T> List<T> queryAll(Class<T> claz) {
        setCurrentDao(claz);
        return mAbstractDao.loadAll();
    }
    
    /**
     *  原生查询
     * @param claz
     * @param whereString
     * @param params
     * @param <T>
     * @return
     */
    public <T> List<T> queryRaw(Class<T> claz,String whereString, String[] params) {
        setCurrentDao(claz);
        return mAbstractDao.queryRaw(whereString, params);
    }

4.4 异步查询

异步操作需要设置回调,回调中可以将结果返回来,比如查询时,拿到查询的结果。

/**
     * 异步操作的回调设置
     * @param callBack
     * @param <T>
     * @return
     */
    public <T> DbUtil setDbCallBack(DbCallBack<T> callBack) {
        mCallBack = callBack;
        return this;
    }
    /**
     * 条件查询数据
     *
     * @param cls
     * @return
     */
    public <T> void queryAsync(Class<T> cls, WhereCondition whereCondition) {
        setCurrentDao(cls);
        AsyncSession asyncSession = mDaoSession.startAsyncSession();
        asyncSession.setListenerMainThread(new AsyncOperationListener() {
            @Override
            public void onAsyncOperationCompleted(AsyncOperation operation) {
                if (operation.isCompletedSucessfully() && mCallBack != null) {
                    List<T> list = new ArrayList<>();
                    list.add(((T) operation.getResult()));
                    mCallBack.onSuccess(list);
                } else if (operation.isFailed()) {
                    mCallBack.onFailed();
                }
            }
        });
        Query query = mDaoSession.queryBuilder(cls).where(whereCondition).build();
        asyncSession.queryUnique(query);
    }

    /**
     * @param claz
     * @param query 可使用 getQuery 方法获取 Query
     * @param <T>
     */
    public <T> void queryAsyncAll(Class<T> claz, Query<T> query) {
        setCurrentDao(claz);
        AsyncSession asyncSession = mDaoSession.startAsyncSession();
        asyncSession.setListenerMainThread(new AsyncOperationListener() {
            @Override
            public void onAsyncOperationCompleted(AsyncOperation operation) {
                if (operation.isCompleted() && mCallBack != null) {
                    List<T> result = (List<T>) operation.getResult();
                    mCallBack.onSuccess(result);
                } else if (operation.isFailed() && mCallBack != null) {
                    mCallBack.onFailed();
                }
            }
        });
        if (query == null) {
            asyncSession.loadAll(claz);
        } else {
            asyncSession.queryList(query);
        }
    }

    /**
     * 删除
     */
    public <T> void deleteAsyncSingle(T entry) {
        setCurrentDao(entry.getClass());
        AsyncSession asyncSession = mDaoSession.startAsyncSession();
        asyncSession.setListenerMainThread(new AsyncOperationListener() {
            @Override
            public void onAsyncOperationCompleted(AsyncOperation operation) {
                if (operation.isCompletedSucessfully() && mCallBack != null) {
                    mCallBack.onNotification(true);
                } else if (operation.isFailed() && mCallBack != null) {
                    mCallBack.onNotification(false);
                }
            }
        });
        asyncSession.delete(entry);
    }

    /**
     * 批量删除
     */
    public <T> void deleteAsyncBatch(Class<T> cls, final List<T> list) {
        setCurrentDao(cls);
        AsyncSession asyncSession = mDaoSession.startAsyncSession();
        asyncSession.setListenerMainThread(new AsyncOperationListener() {
            @Override
            public void onAsyncOperationCompleted(AsyncOperation operation) {
                if (operation.isCompletedSucessfully() && mCallBack != null) {
                    mCallBack.onNotification(true);
                } else if (operation.isFailed() && mCallBack != null) {
                    mCallBack.onNotification(false);
                }
            }
        });
        asyncSession.deleteInTx(cls, list);
    }


    /**
     * 根据Id批量删除
     */
    public<T> void deleteByIdBatch(Class<T> claz,List<Long> longList) {
        setCurrentDao(claz);
        mAbstractDao.deleteByKeyInTx(longList);
    }

    /**
     * 删除所有数据
     */
    public <T> void deleteAsyncAll(Class<T> cls) {
        setCurrentDao(cls);
        final AsyncSession asyncSession = mDaoSession.startAsyncSession();
        asyncSession.setListenerMainThread(new AsyncOperationListener() {
            @Override
            public void onAsyncOperationCompleted(AsyncOperation operation) {
                if (operation.isCompletedSucessfully() && mCallBack != null) {
                    mCallBack.onNotification(true);
                } else if (operation.isFailed() && mCallBack != null) {
                    mCallBack.onNotification(false);
                }
            }
        });
        asyncSession.deleteAll(cls);
    }

    /**
     * 插入一条数据
     */
    public <T> void insertAsyncSingle(final T entity) {
        setCurrentDao(entity.getClass());
        AsyncSession asyncSession = mDaoSession.startAsyncSession();
        asyncSession.setListenerMainThread(new AsyncOperationListener() {
            @Override
            public void onAsyncOperationCompleted(AsyncOperation operation) {
                if (operation.isCompletedSucessfully() && mCallBack != null) {
                    mCallBack.onNotification(true);
                } else if (operation.isFailed() && mCallBack != null) {
                    mCallBack.onNotification(false);
                }
            }
        });
        asyncSession.runInTx(new Runnable() {
            @Override
            public void run() {
                mDaoSession.insert(entity);
            }
        });
    }

    /**
     * 批量插入
     */
    public <T> void insertAsyncBatch(final Class<T> cls, final List<T> userList) {
        setCurrentDao(cls);
        AsyncSession asyncSession = mDaoSession.startAsyncSession();
        asyncSession.setListenerMainThread(new AsyncOperationListener() {
            @Override
            public void onAsyncOperationCompleted(AsyncOperation operation) {
                if (operation.isCompletedSucessfully() && mCallBack != null) {
                    mCallBack.onNotification(true);
                } else if (operation.isFailed() && mCallBack != null) {
                    mCallBack.onNotification(false);
                }
            }
        });
        asyncSession.runInTx(new Runnable() {
            @Override
            public void run() {
                for (T object : userList) {
                    mDaoSession.insertOrReplace(object);
                }
            }
        });
    }

    /**
     * 更新一个数据
     */
    public <T> void updateAsyncSingle(Class<T> cls, T entry) {
        setCurrentDao(cls);
        AsyncSession asyncSession = mDaoSession.startAsyncSession();
        asyncSession.setListenerMainThread(new AsyncOperationListener() {
            @Override
            public void onAsyncOperationCompleted(AsyncOperation operation) {
                if (operation.isCompletedSucessfully() && mCallBack != null) {
                    mCallBack.onNotification(true);
                } else if (operation.isFailed() && mCallBack != null) {
                    mCallBack.onNotification(false);
                }
            }
        });
        asyncSession.update(entry);
    }

    /**
     * 批量更新数据
     */
    public <T> void updateAsyncBatch(final Class<T> cls, final List<T> tList) {
        setCurrentDao(cls);
        AsyncSession asyncSession = mDaoSession.startAsyncSession();
        asyncSession.setListenerMainThread(new AsyncOperationListener() {
            @Override
            public void onAsyncOperationCompleted(AsyncOperation operation) {
                if (operation.isCompletedSucessfully() && mCallBack != null) {
                    mCallBack.onNotification(true);
                } else if (operation.isFailed() && mCallBack != null) {
                    mCallBack.onNotification(false);
                }
            }
        });
        asyncSession.updateInTx(cls, tList);
    }

上面的方法异步操作没什么可说的,只是看到异步查询的方法中有些参数是 WhereCondition ,有些是 Query。上面提到过 mDaoSession.queryBuilder(claz); 来获取 QueryBuilder,通过 QueryBuilder 再获取 Query。WhereCondition 是查询条件,下面看这样一个使用,你就明白了。

// 把 WhereCondition 附加到 QueryBuilder 上,作为 where 的查询条件设置,最终得到 Query
Query query = mDaoSession.queryBuilder(cls).where(whereCondition).build();

这里重点强调一下,WhereCondition,QueryBuilder,Query 都是 greenDAO 中的类,我们在依赖时有两种方式:
(Android Studio 3.0 以后用法)

  • api
  • implementation
implementation 只能在内部使用此模块,比如一个 libiary 中使用 implementation 依赖了 greenDAO,然后主项目依赖了 libiary,那么,我的主项目就无法访问 greenDAO 中的方法。这样的好处是编译速度会加快,推荐使用 implementation 的方式去依赖,如果你需要提供给外部访问,那么就使用 api 依赖即可。

所以:

(1)如果使用这个工具类时,放在你的主工程下,直接使用 WhereCondition,QueryBuilder,Query 这个几个类是没有问题的。

(2)如果你是将该工具类放在 libiary 中,采用 api 方式依赖,同样也没有问题,直接使用这几个类的方法,因为能够引入这几个类。

(3)关键是这点,将该工具类放在 libiary 中,采用 implementation 方式依赖,这时,你的主工程中将看不到 WhereCondition,QueryBuilder,Query 这个几个类,这时候该怎么办呢?这一点目前么有好的办法,想把 WhereCondition,QueryBuilder,Query再封装一层,有些麻烦,干脆还是采用 (2)中的方式来得简单。

5. greenDAO 工具类封装

5.1 设置

对于 Entity 可以放在 数据库模块中,同时将生成的 DAO 也放在该模块下, 这样能够减少主模块中类的数量,如果有多个模块引用数据库模块,Entity 可以增加包来区分属于不同的模块。以下是数据模块目录结构的示例(Entity 未细分包)

gradle设置DAO生成的路径,以及应用数据的方式,采用 api 方式

5.2 数据路工具类

上面已经提到增删改查的封装,下面给出完整的代码。
工具类代码

    public class DbUtil {

    private static final String DEFAULT_DATABASE_NAME = "Database.db";
    private static final String DEFAULT_DATABASE_PASSWORD = "hahaha";
    private DaoSession mDaoSession;
    private DaoMaster mDaoMaster;
    private AbstractDao mAbstractDao;
    private static DbUtil mDbUtils;
    private DaoMaster.DevOpenHelper mHelper;
    private DbCallBack mCallBack;

    private DbUtil(Application application, String dbName, String passWord) {
        if (mHelper == null) {
            mHelper = new DaoMaster.DevOpenHelper(application, dbName);
        }
        if (passWord == null || passWord.isEmpty()) {
            mDaoMaster = new DaoMaster(mHelper.getWritableDb());
        } else {
            mDaoMaster = new DaoMaster(mHelper.getEncryptedReadableDb(passWord));
        }
        mDaoSession = mDaoMaster.newSession();

    }

    public static DbUtil getInstance(Application application) {
        return getInstance(application, DEFAULT_DATABASE_NAME);
    }

    public static DbUtil getInstance(Application application, String dbName) {

        return getInstance(application, dbName, "");
    }

    public static DbUtil getInstance(Application application, String dbName, String passWord) {

        if (mDbUtils == null) {
            synchronized (DbUtil.class) {
                if (mDbUtils == null) {
                    mDbUtils = new DbUtil(application, dbName, passWord);
                }
            }
        }

        return mDbUtils;
    }

    public <T> Query<T> getQuery(Class<T> claz){
        return getQueryBuilder(claz).build();
    }

    public <T> QueryBuilder<T> getQueryBuilder(Class<T> claz){
        return mDaoSession.queryBuilder(claz);
    }
    /**
     * 获取数据库Item的数量
     *
     * @return
     */
    public <T> Long count(Class<T> entityClaz) {
        setCurrentDao(entityClaz);
        return mAbstractDao.count();
    }

    /**
     * 插入一条数据
     *
     * @param dbEntity
     */
    public <T> void insert(T dbEntity) {
        setCurrentDao(dbEntity.getClass());
        mAbstractDao.insert(dbEntity);
    }

    /**
     * 插入一条数据
     *
     * @param dbEntity
     */
    public <T> void insertOrReplace(T dbEntity) {
        setCurrentDao(dbEntity.getClass());
        mAbstractDao.insertOrReplace(dbEntity);
    }

    /**
     * 插入多条数据
     *
     * @param entities
     */
    public <T> void insertTx(List<T> entities) {

        if (entities == null || entities.size() < 1) {
            return;
        }
        setCurrentDaoOfList(entities);
        mAbstractDao.insertInTx(entities);
    }

    public <T> void insertOrReplaceInTx(List<T> entities) {
        if (entities == null || entities.size() < 1) {
            return;
        }
        setCurrentDaoOfList(entities);
        mAbstractDao.insertOrReplaceInTx(entities);
    }

    /**
     * 删除单条数据
     *
     * @param entity
     */
    public <T> void delete(T entity) {
        setCurrentDao(entity.getClass());
        mAbstractDao.delete(entity);
    }

    /**
     * 删除特定ID的数据
     *
     * @param id
     */
    public <T> void deleteById(Class<T> entityClaz, long id) {
        setCurrentDao(entityClaz);
        mAbstractDao.deleteByKey(id);
    }

    /**
     * 删除多条数据
     *
     * @param entities
     */
    public <T> void deleteList(List<T> entities) {
        setCurrentDaoOfList(entities);
        mAbstractDao.deleteInTx(entities);
    }

    /**
     * 全部删除
     */
    public <T> void deleteAll(Class<T> claz) {
        setCurrentDao(claz);
        mAbstractDao.deleteAll();
    }

    /**
     * 更新单条数据
     *
     * @param entity
     */
    public <T> void updateData(final T entity) {
        setCurrentDao(entity.getClass());
        mAbstractDao.update(entity);
    }

    /**
     * 更新多条数据
     *
     * @param entities
     */
    public <T> void updateListData(Collection<T> entities) {
        setCurrentDaoOfList(entities);
        mAbstractDao.updateInTx(entities);
    }

    /**
     * 查询特定ID的数据
     *
     * @param id
     * @return
     */
    public <T> T queryById(Class<T> claz, long id) {
        setCurrentDao(claz);
        return (T) mAbstractDao.load(id);
    }

    /**
     * 查询全部数据
     *
     * @return
     */
    public <T> List<T> queryAll(Class<T> claz) {
        setCurrentDao(claz);
        return mAbstractDao.loadAll();
    }

    public <T> List<T> queryAll(Class<T> claz,WhereCondition whereCondition) {
        setCurrentDao(claz);
        return mDaoSession.queryBuilder(claz)
                .where(whereCondition)
                .list();
    }

    public <T> List<T> queryAll(Class<T> claz,QueryBuilder<T> queryBuilder) {
        setCurrentDao(claz);
        return mDaoSession.queryBuilder(claz).list();
    }

    public <T> List<T> queryAll(Class<T> claz,Query<T> query) {
        setCurrentDao(claz);
       return query.list();
    }


    /**
     * 原生查询
     *
     * @param claz
     * @param whereString
     * @param params
     * @param <T>
     * @return
     */
    public <T> List<T> queryRaw(Class<T> claz, String whereString, String[] params) {
        setCurrentDao(claz);
        return mAbstractDao.queryRaw(whereString, params);
    }

    /**
     * 异步操作的回调设置
     *
     * @param callBack
     * @param <T>
     * @return
     */
    public <T> DbUtil setDbCallBack(DbCallBack<T> callBack) {
        mCallBack = callBack;
        return this;
    }

    /**
     * 条件查询数据
     *
     * @param cls
     * @return
     */
    public <T> void queryAsync(Class<T> cls, WhereCondition whereCondition) {
        setCurrentDao(cls);
        AsyncSession asyncSession = mDaoSession.startAsyncSession();
        asyncSession.setListenerMainThread(new AsyncOperationListener() {
            @Override
            public void onAsyncOperationCompleted(AsyncOperation operation) {
                if (operation.isCompletedSucessfully() && mCallBack != null) {
                    List<T> list = new ArrayList<>();
                    list.add(((T) operation.getResult()));
                    mCallBack.onSuccess(list);
                } else if (operation.isFailed()) {
                    mCallBack.onFailed();
                }
            }
        });
        Query query = mDaoSession.queryBuilder(cls).where(whereCondition).build();
        asyncSession.queryUnique(query);
    }

    /**
     * 异步条件查询,通过使用 QueryBuilder 构造 Query
     * @param claz
     * @param builder
     * @param <T>
     */
    public <T> void queryAsyncAll(Class<T> claz, QueryBuilder<T> builder) {
        setCurrentDao(claz);
        AsyncSession asyncSession = mDaoSession.startAsyncSession();
        asyncSession.setListenerMainThread(new AsyncOperationListener() {
            @Override
            public void onAsyncOperationCompleted(AsyncOperation operation) {
                if (operation.isCompleted() && mCallBack != null) {
                    List<T> result = (List<T>) operation.getResult();
                    mCallBack.onSuccess(result);
                } else if (operation.isFailed() && mCallBack != null) {
                    mCallBack.onFailed();
                }
            }
        });
        if (builder == null || builder.build() == null) {
            asyncSession.loadAll(claz);
        } else {
            asyncSession.queryList(builder.build());
        }
    }

    /**
     * 删除
     */
    public <T> void deleteAsyncSingle(T entry) {
        setCurrentDao(entry.getClass());
        AsyncSession asyncSession = mDaoSession.startAsyncSession();
        asyncSession.setListenerMainThread(new AsyncOperationListener() {
            @Override
            public void onAsyncOperationCompleted(AsyncOperation operation) {
                if (operation.isCompletedSucessfully() && mCallBack != null) {
                    mCallBack.onNotification(true);
                } else if (operation.isFailed() && mCallBack != null) {
                    mCallBack.onNotification(false);
                }
            }
        });
        asyncSession.delete(entry);
    }

    /**
     * 批量删除
     */
    public <T> void deleteAsyncBatch(Class<T> cls, final List<T> list) {
        setCurrentDao(cls);
        AsyncSession asyncSession = mDaoSession.startAsyncSession();
        asyncSession.setListenerMainThread(new AsyncOperationListener() {
            @Override
            public void onAsyncOperationCompleted(AsyncOperation operation) {
                if (operation.isCompletedSucessfully() && mCallBack != null) {
                    mCallBack.onNotification(true);
                } else if (operation.isFailed() && mCallBack != null) {
                    mCallBack.onNotification(false);
                }
            }
        });
        asyncSession.deleteInTx(cls, list);
    }


    /**
     * 根据Id批量删除
     */
    public <T> void deleteByIdBatch(Class<T> claz, List<Long> longList) {
        setCurrentDao(claz);
        mAbstractDao.deleteByKeyInTx(longList);
    }

    /**
     * 删除所有数据
     */
    public <T> void deleteAsyncAll(Class<T> cls) {
        setCurrentDao(cls);
        final AsyncSession asyncSession = mDaoSession.startAsyncSession();
        asyncSession.setListenerMainThread(new AsyncOperationListener() {
            @Override
            public void onAsyncOperationCompleted(AsyncOperation operation) {
                if (operation.isCompletedSucessfully() && mCallBack != null) {
                    mCallBack.onNotification(true);
                } else if (operation.isFailed() && mCallBack != null) {
                    mCallBack.onNotification(false);
                }
            }
        });
        asyncSession.deleteAll(cls);
    }

    /**
     * 插入一条数据
     */
    public <T> void insertAsyncSingle(final T entity) {
        setCurrentDao(entity.getClass());
        AsyncSession asyncSession = mDaoSession.startAsyncSession();
        asyncSession.setListenerMainThread(new AsyncOperationListener() {
            @Override
            public void onAsyncOperationCompleted(AsyncOperation operation) {
                if (operation.isCompletedSucessfully() && mCallBack != null) {
                    mCallBack.onNotification(true);
                } else if (operation.isFailed() && mCallBack != null) {
                    mCallBack.onNotification(false);
                }
            }
        });
        asyncSession.runInTx(new Runnable() {
            @Override
            public void run() {
                mDaoSession.insert(entity);
            }
        });
    }

    /**
     * 批量插入
     */
    public <T> void insertAsyncBatch(final Class<T> cls, final List<T> userList) {
        setCurrentDao(cls);
        AsyncSession asyncSession = mDaoSession.startAsyncSession();
        asyncSession.setListenerMainThread(new AsyncOperationListener() {
            @Override
            public void onAsyncOperationCompleted(AsyncOperation operation) {
                if (operation.isCompletedSucessfully() && mCallBack != null) {
                    mCallBack.onNotification(true);
                } else if (operation.isFailed() && mCallBack != null) {
                    mCallBack.onNotification(false);
                }
            }
        });
        asyncSession.runInTx(new Runnable() {
            @Override
            public void run() {
                for (T object : userList) {
                    mDaoSession.insertOrReplace(object);
                }
            }
        });
    }

    /**
     * 更新一个数据
     */
    public <T> void updateAsyncSingle(Class<T> cls, T entry) {
        setCurrentDao(cls);
        AsyncSession asyncSession = mDaoSession.startAsyncSession();
        asyncSession.setListenerMainThread(new AsyncOperationListener() {
            @Override
            public void onAsyncOperationCompleted(AsyncOperation operation) {
                if (operation.isCompletedSucessfully() && mCallBack != null) {
                    mCallBack.onNotification(true);
                } else if (operation.isFailed() && mCallBack != null) {
                    mCallBack.onNotification(false);
                }
            }
        });
        asyncSession.update(entry);
    }

    /**
     * 批量更新数据
     */
    public <T> void updateAsyncBatch(final Class<T> cls, final List<T> tList) {
        setCurrentDao(cls);
        AsyncSession asyncSession = mDaoSession.startAsyncSession();
        asyncSession.setListenerMainThread(new AsyncOperationListener() {
            @Override
            public void onAsyncOperationCompleted(AsyncOperation operation) {
                if (operation.isCompletedSucessfully() && mCallBack != null) {
                    mCallBack.onNotification(true);
                } else if (operation.isFailed() && mCallBack != null) {
                    mCallBack.onNotification(false);
                }
            }
        });
        asyncSession.updateInTx(cls, tList);
    }

    /**
     * 关闭DaoSession
     */
    private void closeDaoSession() {
        if (mDaoSession != null) {
            mDaoSession.clear();
            mDaoSession = null;
        }
    }

    /**
     * 关闭Helper
     */
    private void closeHelper() {
        if (mHelper != null) {
            mHelper.close();
            mHelper = null;
        }
    }

    /**
     * 关闭所有的操作
     */
    public void closeConnection() {
        closeDaoSession();
        closeHelper();
    }

    /**
     * 数据库不加密
     *
     * @param entityClass 根据 entityClass 获取相应的 xxDao
     * @return mDbUtils
     */
    @Deprecated
    public DbUtil create(Class<?> entityClass) {

        if (mHelper == null) {
            throw new NullPointerException("You need to init mHelper first!");
        }

        mAbstractDao = mDaoSession.getDao(entityClass);
        return mDbUtils;
    }

    private <T> void setCurrentDao(Class<T> entityClass) {
        if (mHelper == null) {
            throw new NullPointerException("You need to init mHelper first!");
        }
        mAbstractDao = mDaoSession.getDao(entityClass);
    }

    private <T> void setCurrentDaoOfList(Collection<T> entities) {
        if (entities != null && entities.size() > 1) {
            Iterator<T> iterator = entities.iterator();
            T next = iterator.next();
            setCurrentDao(next.getClass());
        }
    }
}

回调接口

public interface DbCallBack<T> {

    void onSuccess(List<T> result);
    void onFailed();
    void onNotification(boolean result);
}

5.3 使用示例

下面看一个示例:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private DbUtil dbUtil;
    ...
    
    // 异步查询回调
    DbCallBack<Student> studentDbCallBack = new DbCallBack<Student>() {
        @Override
        public void onSuccess(List<Student> result) {
            if (result.size() > 0) {
                for (Student student1 : result) {
                    Log.e(TAG, "[4] " + student1.toString());
                }
            }
        }

        @Override
        public void onFailed() {

        }

        @Override
        public void onNotification(boolean result) {

        }
    };
    
   @Override
    public void onClick(View v) {
        int id = v.getId();
        switch (id) {
            case R.id.delete_btn:
                // 删除
                if (dbUtil.count(Clazz.class) > 0) {
                    dbUtil.deleteAll(Clazz.class);
                }

                break;

            case R.id.insert_btn:

                // 批量插入
                dbUtil.insertOrReplaceInTx(clazzList);
                dbUtil.insertOrReplaceInTx(studentList);
                break;

            case R.id.query_btn:
                // 单条查询
                Clazz cls = dbUtil.queryById(Clazz.class,1);

                // 查询所有的班级
                List<Clazz> clazList = dbUtil.queryAll(Clazz.class);
                for (Clazz clazz : clazList) {
                    Log.e(TAG, "[1] " + clazz.toString());
                }
                // 查询所有学生
                List<Student> studentList = dbUtil.queryAll(Student.class);
                for (Student student : studentList) {
                    Log.e(TAG, "[2] " + student.toString());
                }
                
                //条件查询
                final List<Student> student = dbUtil.queryRaw(Student.class, "where studentID = ?", new String[]{"12"});
                Log.e(TAG, "[3] " + student.get(0).toString());

                // 异步查询
                DbCallBack<Student> studentDbCallBack = new DbCallBack<Student>() {
                    @Override
                    public void onSuccess(List<Student> result) {
                        if (result.size() > 0) {
                            for (Student student1 : result) {
                                Log.e(TAG, "[4] " + student1.toString());
                            }
                        }
                    }

                    @Override
                    public void onFailed() {

                    }

                    @Override
                    public void onNotification(boolean result) {

                    }
                };

                dbUtil.setDbCallBack(studentDbCallBack)
                        .queryAsync(Student.class, StudentDao.Properties.StudentID.eq(19));

                // 批量异步条件查询
                QueryBuilder<Student> builder = dbUtil.getQueryBuilder(Student.class)
                        .where(StudentDao.Properties.StudentID.between(12, 16));

                dbUtil.setDbCallBack(studentDbCallBack)
                        .queryAsyncAll(Student.class, builder);

                // 批量同步条件查询
                QueryBuilder<Student> builder1 = dbUtil.getQueryBuilder(Student.class)
                        .limit(3)
                        .distinct()
                        .offset(2);

                List<Student> list = dbUtil.queryAll(Student.class, builder1);
                for (Student student1 : list) {
                    Log.e(TAG, "[4] " + student1.toString());
                }

                break;

            case R.id.join_btn:

                // 多表查询
                QueryBuilder<Student> builder2 = dbUtil.getQueryBuilder(Student.class);
                builder2.join(StudentDao.Properties.ClazID, Clazz.class)
                        .where(ClazzDao.Properties.Id.eq(1));
                List<Student> studentList1 = builder2.list();
                for (Student student2 : studentList1) {
                    Log.e(TAG, "" + student2.toString());
                }
                break;

        }

注意

对于 WhereCondition,一般使用 DAO 来构造

    StudentDao.Properties.StudentID.eq(19)

5 总结

greenDAO 使用起来稍微有一点麻烦,但是效率高,这个我也是看其他文章这么写的,没有亲自试过,有兴趣的小伙伴可以尝试测试测试。至于它的封装,目前我也没有想到更好的方式,如果有更好的方式可以拿出来分享,或者对的上面的封装有改进的地方,也可以提出优化的意见!

代码地址

参考:

greenDAO 官网

greendao 3.0集成和使用封装

GreenDao3.0 高级用法

数据库调试工具

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容

  • greenDAO greenDAO 是一个将对象映射到 SQLite 数据库中的轻量且快速的 ORM 解决方案。它...
    蕉下孤客阅读 16,090评论 18 104
  • 前段时间工作中接触到了数据库greendao,将项目中所有原生sqlite替换成为了greendao数据库封装框架...
    ya_nn阅读 14,928评论 6 33
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,825评论 25 707
  • GreenDao 介绍:greenDAO是一个对象关系映射(ORM)的框架,能够提供一个接口通过操作对象的方式去操...
    小董666阅读 730评论 0 1
  • 自从鲁滨逊从荒岛回来,破了许多案子,10月7日局长授予他大侦探的名誉,这一次鲁滨逊发现了一场海上集体失踪案,鲁滨逊...
    我个学霸阅读 606评论 2 1