GreenDao 3.0 基本使用 & 自定义数据库路径 & 数据库升级

greenDAO-orm-320.png

greenDAO是一款轻松快速的Android ORM解决方案,可将对象映射到SQLite数据库.
这里只是介绍其中的一部分功能, 想了解更多还是得去官网看看.
github地址: https://github.com/greenrobot/greenDAO

提示:使用前需要申请读写权限

一.导入greenDAO

1. 项目根目录下, build.gradle,如下图:
mavenCentral() 
classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2' //greendao
app_build.gradle.png
  1. 在 根目录/app/build.gradle文件,添加
apply plugin: 'org.greenrobot.greendao'

android 节点下:

android{
    greendao{
            schemaVersion 1  //数据库版本号
            daoPackage'greendao' //编译时生成文件 所在的包名
            targetGenDir'src/main/java'  //编译时生成文件 所在的目录
      }
}
implementation 'org.greenrobot:greendao:3.2.2' //数据库

2. 建立数据库实体模型

@Entity
public class User{
    @Id
    String name= ""; 
    String age= "0.00";
    String lunch = "0.00";
}

@Entity 标记该类为数据库实体模型,指示GreendDao生成代码
@Id 主键

三. 使用

1.默认方式
DbOpenHelper helper = new DaoMaster.DevOpenHelper(context, "数据库文件名", null);
DaoMaster daoMaster = new DaoMaster(helper.getWritableDatabase());
DaoSession daoSession = daoMaster.newSession();
//然后就可以用userDao 操作数据库了.
2. 自定义保存路径

发现问题: 数据库文件保存在哪儿? 可以自定义路径吗?
网上搜到, 默认保存的目录是 /data/data/Package Name/database. 可是却不知道为什么?
最终还是得跑去看看源码. 这里简单说一下:
①. 我们一开始就new DaoMaster.DevOpenHelper这个对象, 我们对着它的构造函数一层一层进去看, 发现最终调用的是android系统自带的 SQLiteOpenHelper.java这个类. 如下图:

SQLiteOpenHelper.png

从上图我们可以看到: 获取路径的方法是mContext.getDatabasePath(name), 这个时候我就想: 新建一个Context, 然后重写getDatabasePath就可以了, 结果发现崩掉了.
错误信息.png

②. 再试尝试, 重写Context中的openOrCreateDatabase, 结果成功了.

③. 看过源码的应该知道,我们默认传进去的Context实际上就是ContextWrapper, 于是有了以下写法:

最终解决方法:

public class DatabaseContext  extends ContextWrapper {

    public static String dbPath = "";

    public DatabaseContext(Context base, String dbPath) {
        super(base);
        if(!TextUtils.isEmpty(dbPath)){
            this.dbPath = dbPath;
        }
    }

    @Override
    public File getDatabasePath(String name){
        File dbDir = new File(dbPath);
        if(!dbDir.exists()){
            dbDir.mkdir();
        }

        File dbFile = new File(dbPath, name);
        if(!dbFile.exists()){
            try {
                dbFile.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return dbFile;
    }

    @Override
    public SQLiteDatabase openOrCreateDatabase(String name, int mode, SQLiteDatabase.CursorFactory factory) {
        SQLiteDatabase result = SQLiteDatabase.openOrCreateDatabase(getDatabasePath(name), null);
        return result;
    }

    @Override
    public SQLiteDatabase openOrCreateDatabase(String name, int mode, SQLiteDatabase.CursorFactory factory, DatabaseErrorHandler errorHandler) {
        SQLiteDatabase result = SQLiteDatabase.openOrCreateDatabase(getDatabasePath(name), null);
        return result;
    }
}

使用

DbOpenHelper helper = new DaoMaster.DevOpenHelper(new DatabaseContext(getApplication(), "数据库目录"), "数据库文件名", null);
DaoMaster daoMaster = new DaoMaster(helper.getWritableDatabase());
DaoSession daoSession = daoMaster.newSession();
UserDao userDao = daoSession.getUserDao()
3. 数据升级出现的问题: 源数据会被覆盖

解决办法: 复制一份数据备份, 被覆盖后重新保存
具体做法: 新增两个辅助类: MigrationHelper.java 和 DbOpenHelper.java

/**
 * @Description: 数据库升级辅助
 * @Author: liys
 * @CreateDate: 2019/6/17 14:15
 * @UpdateUser: 更新者
 * @UpdateDate: 2019/6/17 14:15
 * @UpdateRemark: 更新说明
 * @Version: 1.0
 */
public class MigrationHelper {
    public static void migrate(SQLiteDatabase sqliteDatabase, Class<? extends AbstractDao<?, ?>>... daoClasses) {
        StandardDatabase db = new StandardDatabase(sqliteDatabase);
        generateNewTablesIfNotExists(db, daoClasses);
        generateTempTables(db, daoClasses);
        dropAllTables(db, true, daoClasses);
        createAllTables(db, false, daoClasses);
        restoreData(db, daoClasses);
    }

    public static void migrate(StandardDatabase db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
        generateNewTablesIfNotExists(db, daoClasses);
        generateTempTables(db, daoClasses);
        dropAllTables(db, true, daoClasses);
        createAllTables(db, false, daoClasses);
        restoreData(db, daoClasses);
    }

    private static void generateNewTablesIfNotExists(StandardDatabase db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
        reflectMethod(db, "createTable", true, daoClasses);
    }

    private static void generateTempTables(StandardDatabase db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
        for (int i = 0; i < daoClasses.length; i++) {
            DaoConfig daoConfig = new DaoConfig(db, daoClasses[i]);
            String tableName = daoConfig.tablename;
            String tempTableName = daoConfig.tablename.concat("_TEMP");
            StringBuilder insertTableStringBuilder = new StringBuilder();
            insertTableStringBuilder.append("CREATE TEMP TABLE ").append(tempTableName);
            insertTableStringBuilder.append(" AS SELECT * FROM ").append(tableName).append(";");
            db.execSQL(insertTableStringBuilder.toString());
        }
    }

    private static void dropAllTables(StandardDatabase db, boolean ifExists, @NonNull Class<? extends AbstractDao<?, ?>>... daoClasses) {
        reflectMethod(db, "dropTable", ifExists, daoClasses);
    }

    private static void createAllTables(StandardDatabase db, boolean ifNotExists, @NonNull Class<? extends AbstractDao<?, ?>>... daoClasses) {
        reflectMethod(db, "createTable", ifNotExists, daoClasses);
    }

    /**
     * dao class already define the sql exec method, so just invoke it
     */
    private static void reflectMethod(StandardDatabase db, String methodName, boolean isExists, @NonNull Class<? extends AbstractDao<?, ?>>... daoClasses) {
        if (daoClasses.length < 1) {
            return;
        }
        try {
            for (Class cls : daoClasses) {
                Method method = cls.getDeclaredMethod(methodName, Database.class, boolean.class);
                method.invoke(null, db, isExists);
            }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    private static void restoreData(StandardDatabase db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
        for (int i = 0; i < daoClasses.length; i++) {
            DaoConfig daoConfig = new DaoConfig(db, daoClasses[i]);
            String tableName = daoConfig.tablename;
            String tempTableName = daoConfig.tablename.concat("_TEMP");
            // get all columns from tempTable, take careful to use the columns list
            List<String> columns = getColumns(db, tempTableName);
            ArrayList<String> properties = new ArrayList<>(columns.size());
            for (int j = 0; j < daoConfig.properties.length; j++) {
                String columnName = daoConfig.properties[j].columnName;
                if (columns.contains(columnName)) {
                    properties.add(columnName);
                }
            }
            if (properties.size() > 0) {
                final String columnSQL = TextUtils.join(",", properties);

                StringBuilder insertTableStringBuilder = new StringBuilder();
                insertTableStringBuilder.append("INSERT INTO ").append(tableName).append(" (");
                insertTableStringBuilder.append(columnSQL);
                insertTableStringBuilder.append(") SELECT ");
                insertTableStringBuilder.append(columnSQL);
                insertTableStringBuilder.append(" FROM ").append(tempTableName).append(";");
                db.execSQL(insertTableStringBuilder.toString());
            }
            StringBuilder dropTableStringBuilder = new StringBuilder();
            dropTableStringBuilder.append("DROP TABLE ").append(tempTableName);
            db.execSQL(dropTableStringBuilder.toString());
        }
    }

    private static List<String> getColumns(StandardDatabase db, String tableName) {
        List<String> columns = null;
        Cursor cursor = null;
        try {
            cursor = db.rawQuery("SELECT * FROM " + tableName + " limit 0", null);
            if (null != cursor && cursor.getColumnCount() > 0) {
                columns = Arrays.asList(cursor.getColumnNames());
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (cursor != null)
                cursor.close();
            if (null == columns)
                columns = new ArrayList<>();
        }
        return columns;
    }
}
/**
 * @Description: 数据库升级辅助
 * @Author: liys
 * @CreateDate: 2019/6/17 14:11
 * @UpdateUser: 更新者
 * @UpdateDate: 2019/6/17 14:11
 * @UpdateRemark: 更新说明
 * @Version: 1.0
 */
public class DbOpenHelper extends DaoMaster.DevOpenHelper {

    public DbOpenHelper(Context context, String name) {
        super(context, name);
    }

    @Override
    public void onUpgrade(Database db, int oldVersion, int newVersion) {
        //切记不要调用super.onUpgrade(db,oldVersion,newVersion)
        if (oldVersion < newVersion) {
            MigrationHelper.migrate((StandardDatabase) db,
                    SpendMoneyBeanDao.class //具体操作数据库的dao类, 如果需要多个,用逗号隔开
                    //TestDao1.class,
                    //TestDao2.class
            );
        }
    }
}
使用:
DbOpenHelper helper = new DbOpenHelper(new DatabaseContext(context, "数据库目录"), "数据库文件名");
DaoMaster daoMaster = new DaoMaster(helper.getWritableDatabase());
DaoSession daoSession = daoMaster.newSession();
UserDao userDao = daoSession.getUserDao()
4. 增删改查
UserDao userDao = daoSession.getUserDao()
//增
userDao.insert(userBean);
//删
userDao.delete(userBean);
//改
userDao.update(userBean)
//查
//1. 查询所有
userDao.queryBuilder().list();
//2. 单条件查询(名字=liys)
userDao.queryBuilder()
      .where(UserDaoDao.Properties.Name.eq("liys"))
      .list();
//3. 多条件查询, 同时满足(name=liys  && age = 18)
userDao.queryBuilder()
      .where(UserDaoDao.Properties.Name.eq("liys"),
            UserDaoDao.Properties.Age.eq("18"))
      .list();
//4. 多条件查询(或)
userDao.queryBuilder()
      .whereOr(UserDaoDao.Properties.Name.eq("liys"),
            UserDaoDao.Properties.Age.eq("18"))
//5. 查出来排序
serDao.queryBuilder()
      //.orderDesc(UserDaoDao.Properties.Age) //降序
      .orderAsc(UserDaoDao.Properties.Age) //升序
      .list();
5. 查询条件总结

eq : 等于

notEq : 不等于

like: 模糊查询 记住模糊查询,string要用夹在%key%中间。

//查询Name包含liys的人。
xxDao.queryBuilder().where(Properties.Name.like("%liys%")).list();

IN(..., ..., ...) 在给出的value的范围内的符合项

gt: 大于

ge 大于等于

lt 小于

le 小于等于

isNull 不是空的

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