GreenDao 3.2使用简介

2019-01-03  本文已影响0人  MorisWang

前言

Android开发中,经常用到的三种本地数据持久化的方式为:

  1. 使用原生数据库SQLite直接操作;
  2. 使用第三方数据库,如GreenDao。

GreenDao使用简单,加上其本身存取快、体积小、支持缓存、支持加密等优点,使得它成为了一个受欢迎的ORM解决方案,今天我们就简要介绍一下GreenDao的用法和使用过程中一些坑。

基本使用

github地址

GreenDao

项目中引入

buildscript {
    repositories {
        jcenter()
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.1.1'
        classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2' // 添加GreenDao依赖
    }
}
apply plugin: 'com.android.application'
apply plugin: 'org.greenrobot.greendao' // 添加GreenDao Plugin

dependencies {
    implementation 'org.greenrobot:greendao:3.2.2' // 添加GreenDao依赖
}
greendao{
    schemaVersion 1  //数据库版本号
    daoPackage 'com.example.greendaotest.dao'//DaoMaster、DaoSession、XXDao的包名
    targetGenDir 'src/main/java' //DaoMaster、DaoSession、XXDao的路径
    generateTests false //设置为true以自动生成单元测试。
    targetGenDirTests 'src/androidTest/java' //应存储生成的单元测试的基本目录。
}

其中,schemaVersion为数据库版本号必填;若不需要单元测试,generateTests和targetGenDirTests可以删除;daoPackage和targetGenDir选填,如果填写会根据填写的路径生成相关的代码,否则会在build/generated/source/greendao中自动生成相关的类。
到此,GreenDao的引入工作已经完成,可以再代码中使用GreenDao进行数据库相关开发了。

构建实体类

@Entity
public class UserBean {
    @Id(autoincrement = true)
    private Long id;
    @NotNull
    private String name;
    private int age;
}
GreenDao注解

实体类创建后,Build --> Make Module 'app', 会自动生成相应的类,build后刚刚创建的实体类如下:

@Entity
public class UserBean {
    @Id(autoincrement = true)
    private Long id;
    @NotNull
    private String name;
    private int age;
    @Generated(hash = 1420883130)
    public UserBean(Long id, @NotNull String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }
    @Generated(hash = 1203313951)
    public UserBean() {
    }
    public Long getId() {
        return this.id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getName() {
        return this.name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return this.age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}

并且,在刚刚build.gradle 中配置的daoPackage目录下生成了三个java文件,分别为:

初始化数据库

  1. 创建数据库管理类DbManager,也可以不创建,直接在Application中初始化:
public class DbManager {

    private static final String DB_NAME = "search";
    private static DbManager mDbManager;
    private static DaoMaster.DevOpenHelper mDevOpenHelper;
    private static DaoMaster mDaoMaster;
    private static DaoSession mDaoSession;

    private Context mContext;

    private DbManager(Context context) {
        this.mContext = context;
        // 初始化数据库信息
        mDevOpenHelper = new DaoMaster.DevOpenHelper(context, DB_NAME);
        getDaoMaster(context);
        getDaoSession(context);
    }

    public static DbManager getInstance(Context context) {
        if (null == mDbManager) {
            synchronized (DbManager.class) {
                if (null == mDbManager) {
                    mDbManager = new DbManager(context);
                }
            }
        }
        return mDbManager;
    }

    /**
     * 获取可读数据库
     *
     * @param context
     * @return
     */
    public static SQLiteDatabase getReadableDatabase(Context context) {
        if (null == mDevOpenHelper) {
            getInstance(context);
        }
        return mDevOpenHelper.getReadableDatabase();
    }

    /**
     * 获取可写数据库
     *
     * @param context
     * @return
     */
    public static SQLiteDatabase getWritableDatabase(Context context) {
        if (null == mDevOpenHelper) {
            getInstance(context);
        }

        return mDevOpenHelper.getWritableDatabase();
    }

    /**
     * 获取DaoMaster
     *
     * @param context
     * @return
     */
    public static DaoMaster getDaoMaster(Context context) {
        if (null == mDaoMaster) {
            synchronized (DbManager.class) {
                if (null == mDaoMaster) {
                    mDaoMaster = new DaoMaster(getWritableDatabase(context));
                }
            }
        }
        return mDaoMaster;
    }

    /**
     * 获取DaoSession
     *
     * @param context
     * @return
     */
    public static DaoSession getDaoSession(Context context) {
        if (null == mDaoSession) {
            synchronized (DbManager.class) {
                mDaoSession = getDaoMaster(context).newSession();
            }
        }
        return mDaoSession;
    }
}

2.初始化GreenDao:

public class GreenDaoApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        //初始化GreenDao
        DbManager.getInstance(this);
    }
}

增删改查

为了方便介绍,单独建立一个单例类,创建UserBeanDaoManager,如下:

package com.example.greendaotest;

import android.content.Context;

import com.example.greendaotest.bean.UserBean;
import com.example.greendaotest.dao.UserBeanDao;

import java.util.List;

/**
 * 1 * FileName: UserBeanDaoManager
 * 2 * Author: WangDongDong
 * 3 * Date: 2019/1/3 4:34 PM
 * 4 * Description:
 * 5 * History:
 * 6 * <author> <time> <version> <desc>
 * 7 * 作者姓名 修改时间 版本号 描述
 */
public class UserBeanDaoManager {
    private static UserBeanDaoManager mManger;
    private static UserBeanDao mUserBeanDao;

    public static UserBeanDaoManager getInstance(Context context) {
        synchronized (UserBeanDaoManager.class) {
            if (mManger == null) {
                mManger = new UserBeanDaoManager(context);
            }
            return mManger;
        }
    }

    public UserBeanDaoManager(Context context) {
        mUserBeanDao = DbManager.getDaoSession(context).getUserBeanDao();
    }

    /**
     * 新增数据
     * @param bean
     */
    public void insertData(UserBean bean) {
        mUserBeanDao.insert(bean);
    }

    /**
     * 增加一组数据
     * @param list
     */
    public void insertData(List<UserBean> list) {
        mUserBeanDao.insertInTx(list);
    }

    /**
     * 修改数据
     * @param bean
     */
    public void updateData(UserBean bean) {
        mUserBeanDao.update(bean);
    }

    /**
     * 查询数量
     * @return
     */
    public long getUserCount() {
        return mUserBeanDao.count();
    }

    /**
     * 按年龄降序查询所有数据
     * @return
     */
    public List<UserBean> queryAll() {
        return mUserBeanDao
                .queryBuilder()
                .orderDesc(UserBeanDao.Properties.Age)
                .list();
    }

    /**
     * 按条件查询
     * @param name
     * @return
     */
    public List<UserBean> queryByName(String name) {
        List<UserBean> list = mUserBeanDao
                .queryBuilder()
                .where(UserBeanDao.Properties.Name.eq(name))
                .list();
        return list;
    }

    /**
     * 删除数据
     * @param bean
     */
    public void deleteData(UserBean bean) {
        mUserBeanDao.delete(bean);
    }

    /**
     * 删除全部
     */
    public void deleteAll() {
        mUserBeanDao.deleteAll();
    }
}

以上是GreenDao基本的增删改查操作,GreenDao提供了更加丰富的API,这些API可以通过java doc直接看到,再此不做更多介绍。

联合查询

创建班级类,ClassBean.java:

@Entity
public class ClassBean {
    @Id(autoincrement = true)
    private Long id;
    private String className;
    @Generated(hash = 2143102101)
    public ClassBean(Long id, String className) {
        this.id = id;
        this.className = className;
    }
    @Generated(hash = 1395092832)
    public ClassBean() {
    }
    public Long getId() {
        return this.id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getClassName() {
        return this.className;
    }
    public void setClassName(String className) {
        this.className = className;
    }
}

在UserBeanDaoManager中新增方法:

/**
     * 联合查询,查询班级为一班的学生
     * @param name
     * @param age
     * @return
     */
    public List<UserBean> queryByNameAndAge(String name, int age) {
        QueryBuilder builder = mUserBeanDao.queryBuilder();
        builder.join(ClassBean.class, ClassBeanDao.Properties.Id)
                .where(ClassBeanDao.Properties.ClassName.eq("一班"));
        List<UserBean> list = builder.list();
        return list;
    }
查询条件

数据库升级

build.gradle 的greendao配置中,有schemaVersion字段:

greendao{
    schemaVersion 1  //数据库版本号
    daoPackage 'com.example.greendaotest.dao'//DaoMaster、DaoSession、XXDao的包名
    targetGenDir 'src/main/java' //DaoMaster、DaoSession、XXDao的路径
    generateTests false //设置为true以自动生成单元测试。
//    targetGenDirTests 'src/androidTest/java' //应存储生成的单元测试的基本目录。
}

升级数据库需要将该字段值加1。
但是!但是!!但是!!!如果只是版本+1 ,数据库字段确实添加成功了,但所有的数据都没了,这在实际应用中,是一个很严重的问题。
原因可见自动生成的DaoMaster类中,onUpgrade处的注释:

/** 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);
        }
    }

原来,GreenDao在升级时,首先会删除所有table,然后重新创建,因此数据在升级是会丢。
因此,我们可以通过以下的方式进行升级:

@Override
public void onUpgrade(Database db, int oldVersion, int newVersion) {
            if (oldVersion < 2) {
                //添加性别字段
                db.execSQL(String.format("ALTER TABLE %s ADD %s varchar", DbManager.DB_NAME, "gender"));
            }
            if (oldVersion < 3) {
                //添加成绩字段
                db.execSQL(String.format("ALTER TABLE %s ADD %s integer default 0", DbManager.DB_NAME, "score"));
            }
        }

数据库升级方案可以参考GreenDaoUpgradeHelper,这里提供了完整的数据库升级解决方案,在此处就不搬运了。

混淆

#greendao3.2.0,此是针对3.2.0,如果是之前的,可能需要更换下包名
-keep class org.greenrobot.greendao.**{*;}
-keepclassmembers class * extends org.greenrobot.greendao.AbstractDao {
public static java.lang.String TABLENAME;
}
-keep class **$Properties

一些坑

  1. 自动生成的类(DaoMaster.java、DaoSession.java,XXDao.java)是动态编译的,因此在多人协作开发时,会出现在Git更改记录中,总是需要提交。因此,该部分应放在gitignore中。
  2. JavaBean在编译之后,会生成@Generated(hash = xxxxxxxxx)样式的注解,如:
 @Generated(hash = 1420883130)
    public UserBean(Long id, @NotNull String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

该hash值也是动态生成的,因此如果动态编译之后,hash值有可能会改变,会发生编译错误

java.lang.RuntimeException: Constructor (see UserBean:24) has been changed after generation.
Please either mark it with @Keep annotation instead of @Generated to keep it untouched,
or use @Generated (without hash) to allow to replace it.

因此,此部分需要在生成之后,使用@Keep注解替换:

@Keep
    public UserBean(Long id, @NotNull String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }
  1. 数据库升级问题,如果需要升级数据库,一定记得重写DaoMaster中onUpgrade的方法,升级数据库的过程中,数据会直接丢失,造成严重后果。
上一篇下一篇

猜你喜欢

热点阅读