GreenDao使用CRUD及数据库结构升级

来源:互联网 发布:数据交换方式有哪些 编辑:程序博客网 时间:2024/05/02 02:09

今天要说的是众所周知的greenddao,之前一直都是手动sql,不仅在操作数据库的时候比较麻烦,而且还容易出错,朋友推荐了greendao这个轻量级的数据库框架。废话不多说,进入正题!

greendao的构建,是参见这篇文章:greenDao构建

还是把这步骤也说一下,方便大家也方便自己。

studio用户添加依赖:

 compile 'de.greenrobot:greendao:1.3.7'
 sourceSets {        main {            java.srcDirs = ['src/main/java', 'src/main/java-gen']        }    }

1.在 .src/main 目录下新建一个与 java 同层级的「java-gen」目录,用于存放由 greenDAO 生成的 Bean、DAO、DaoMaster、DaoSession 等类。 

2.新建「GREENDAO GENERATOR」模块 (纯 JAVA 工程)

1)通过 File -> New -> New Module -> Java Library -> 填写相应的包名与类名 -> Finish.(注意此处是javalibrary)

2)在新创建的module的gradle中,配置 daoexamplegenerator 工程的 build.gradle,添加 dependencies.

compile 'de.greenrobot:greendao-generator:1.3.1'

3.编写ExampleDaoGenerator 这个类:

关于这个类的编写,直接上代码,注释详细:

首先贴出main方法:


public static void main(String [] args) throws Exception {        //注意导包        // 正如你所见的,你创建了一个用于添加实体(Entity)的模式(Schema)对象。        // 两个参数分别代表:数据库版本号与自动生成代码的包路径。//        Schema schema = new Schema(1, "me.itangqi.greendao");//      当然,如果你愿意,你也可以分别指定生成的 Bean 与 DAO 类所在的目录,只要如下所示:      Schema schema = new Schema(2, "me.itangqi.bean");       schema.setDefaultJavaPackageDao("me.itangqi.dao");        // 模式(Schema)同时也拥有两个默认的 flags,分别用来标示 entity 是否是 activie 以及是否使用 keep sections。        // schema2.enableActiveEntitiesByDefault();        // schema2.enableKeepSectionsByDefault();        // 一旦你拥有了一个 Schema 对象后,你便可以使用它添加实体(Entities)了。        addNote(schema);        addPerson(schema);// 最后我们将使用 DAOGenerator 类的 generateAll() 方法自动生成代码,此处你需要根据自己的情况更改输出目录(既之前创建的 java-gen)。        // 其实,输出目录的路径可以在 build.gradle 中设置,有兴趣的朋友可以自行搜索,这里就不再详解。        new DaoGenerator().generateAll(schema, "D:/StudioProjects/GreenDaoDemo/app/src/main/java-gen");    }

private static void addPerson(Schema schema) {        Entity person=schema.addEntity("Person");        person.addIdProperty();        person.addStringProperty("name").notNull();        person.addIntProperty("sex");        person.addIntProperty("grade");        person.addLongProperty("birthTime");        person.addIntProperty("age");    }

private static void addNote(Schema schema) {        // 一个实体(类)就关联到数据库中的一张表,此处表名为「Note」(既类名)        Entity note = schema.addEntity("Note");        // 你也可以重新给表命名        // note.setTableName("NODE");        // greenDAO 会自动根据实体类的属性值来创建表字段,并赋予默认值        // 接下来你便可以设置表中的字段:        note.addIdProperty();        note.addStringProperty("text").notNull();        // 与在 Java 中使用驼峰命名法不同,默认数据库中的命名是使用大写和下划线来分割单词的。        // For example, a property called “creationDate” will become a database column “CREATION_DATE”.        note.addStringProperty("comment");        note.addDateProperty("date");    }


以上方法决定表中字段,以及类型。

  在此需要注意的时,在做数据库升级的时候需要修改这里的版本号:

Schema schema = new Schema(2, "me.itangqi.bean"); //数据库这里修改版本号

最后执行此java文件,及可生成对应的xxxDao,xxx实体。

二、CRUD操作:

首先oncreate中初始化:

private void setupDatabase() {        // 通过 DaoMaster 的内部类 DevOpenHelper,你可以得到一个便利的 SQLiteOpenHelper 对象。        // 可能你已经注意到了,你并不需要去编写「CREATE TABLE」这样的 SQL 语句,因为 greenDAO 已经帮你做了。        // 注意:默认的 DaoMaster.DevOpenHelper 会在数据库升级时,删除所有的表,意味着这将导致数据的丢失。        // 所以,在正式的项目中,你还应该做一层封装,来实现数据库的安全升级。        DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(this, "test.db", null);        db = helper.getWritableDatabase();        // 注意:该数据库连接属于 DaoMaster,所以多个 Session 指的是相同的数据库连接。        daoMaster = new DaoMaster(db);        daoSession = daoMaster.newSession();    }

获取你需要操作的表XXXdao

 daoSession.getPersonDao().insert(person);
 下面来说说CRUD方法,以及常用的实现方式:

插入:

private long add(){        long age=3000;        Person person=new Person(null,"张大",1,age,18);        //添加一些对象//        daoSession.getPersonDao().insertInTx(persons);        //添加单个对象//        daoSession.getPersonDao().insert(person);        return daoSession.getPersonDao().insert(person);    }
更新:

private void update(){        //按照某一条件更新数据        Person person = persons.get(persons.size() - 1);        person.setName("张大大");        daoSession.getPersonDao().update(person);        //批量更新        /*public void updateBatch(List<Note> notes) throws Exception {            daoSession.getNoteDao().updateInTx(notes);        }*/    }

删除:

private void delete(){        //根据条件删除?        daoSession.getPersonDao().delete(persons.get(0));        Long key = daoSession.getPersonDao().getKey(persons.get(persons.size() - 1));        Log.e("key=",""+key);       /* //删除全部1        daoSession.deleteAll(Person.class);        //删除全部2        daoSession.getPersonDao().deleteAll();*/        /*//批量删除        public void deleteBatch(List<Person> notes) {            daoSession.getNoteDao().deleteInTx(notes);        }        // 批量按主键删除        public void deleteBatchByKey(List<Long> pkIds) {            daoSession.getNoteDao().deleteByKeyInTx(pkIds);        }*/        //sql删除//        daoSession.getDatabase().execSQL(deletesql);    }

查询:

 private void query(){        //根据条件查询//        Query<Person> build = daoSession.getPersonDao().queryBuilder()//                .where(PersonDao.Properties.Sex.eq(1))//                .build();//        PersonDao.Properties.Age.gt() 大于  lt小于//        PersonDao.Properties.Age.ge() 大于等于 le小于等于//        PersonDao.Properties.Name.eq()//        PersonDao.Properties.Name.notEq("")//不等于//        List <Integer> list=new ArrayList<>();//        list.add(1);//        list.add(2);//        PersonDao.Properties.Age.in(list); 查询条件在集合中有//        PersonDao.Properties.Name.between(1,5);  在某个区间//        PersonDao.Properties.Name.isNull();  为null//        PersonDao.Properties.Name.like("%g"); 以g结尾  _g%//        PersonDao.Properties.Name.primaryKey 判断是否是主键//        QueryBuilder<Person> builder = daoSession.getPersonDao().queryBuilder();//        builder.where(PersonDao.Properties.Age.gt(0))//                .limit(2)//取的条数//                .offset(0) //开始位置//                .orderAsc(PersonDao.Properties.Age);//升序//        List<Person> list = builder.build().list();        List<Person> list = daoSession.getPersonDao().queryRaw("where "+PersonDao.Properties.Age.columnName + " >= ? and " + PersonDao.Properties.Sex.columnName + " = ?", new String[]{"2", "0"});//       daoSession.getDatabase().execSQL();        for (Person person:list                ) {            Log.e("条件查询",person.toString());        }    }
查询全部:

private void queryAll(){        //查询全部的第一种方式      /*  Query<Person> build = daoSession.getPersonDao().queryBuilder().build();        persons.clear();        persons.addAll(build.list());        for (Person person:persons                ) {            Log.e("查询全部",person.toString());        }*/        //查询全部的第二种方式        /*List<Person> p = daoSession.getPersonDao().loadAll();        for (Person person:p             ) {            Log.e("查询全部",person.toString());        }*/        //查询全部的第三种方式        /*List<Person> p = daoSession.loadAll(Person.class);        for (Person person:p                ) {            Log.e("查询全部",person.toString());        }*/        //查询全部的第四种方式        List<Person> p = daoSession.queryBuilder(Person.class).build().list();        for (Person person:p                ) {            Log.e("查询全部",person.toString());            persons.add(person);        }    }
greendao中没有提供全面的api根据某一条件进行,更新或者删除等,如需要可按照原生的数据库操作方式进行操作:

 daoSession.getDatabase().delete()
三、数据库的升级:

查看DaoMaster中的升级方法,你会发现,在做数据库升级的方法中是删除所有的数据,然后在创建表,那么这样就无疑会导致数据库中的原有数据丢失。

/** * WARNING: Drops all table on Upgrade! Use only during development. */public static class DevOpenHelper extends OpenHelper {      public DevOpenHelper(Context context, String name, CursorFactory factory) {             super(context, name, factory);      }      @Override      public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {                    Log.i("greenDAO", "Upgrading schema from version " + oldVersion + " to " + newVersion + " by dropping all tables");                dropAllTables(db, true);                onCreate(db);      }}

在网上查看了一些提供的数据库升级方法,感觉都是很复杂或者不可用,下面介绍一种可用简单的数据库升级方式,此方法也是墙外找的,嘿嘿,还是拿来主义,其思路与一般数据库升级也一样,创建一个临时的表,将原来的数据保存到临时的表中,在删除原来的表,创建新表把临时表中的数据迁移到新的结构表中,删除临时表的过程。下面首先看看一个辅助类MigrationHelper:

package com.choe.greendaodemo.utils;import android.database.Cursor;import android.database.sqlite.SQLiteDatabase;import android.text.TextUtils;import android.util.Log;import java.util.ArrayList;import java.util.Arrays;import java.util.List;import de.greenrobot.dao.AbstractDao;import de.greenrobot.dao.internal.DaoConfig;import me.itangqi.dao.DaoMaster;/** * Created by cyk  */public class MigrationHelper {    private static final String CONVERSION_CLASS_NOT_FOUND_EXCEPTION = "MIGRATION HELPER - CLASS DOESN'T MATCH WITH THE CURRENT PARAMETERS";    private static MigrationHelper instance;    public static MigrationHelper getInstance() {        if(instance == null) {            instance = new MigrationHelper();        }        return instance;    }    public void migrate(SQLiteDatabase db, Class<? extends AbstractDao<?, ?>>... daoClasses) {        generateTempTables(db, daoClasses);        DaoMaster.dropAllTables(db, true);        DaoMaster.createAllTables(db, false);        restoreData(db, daoClasses);    }    /**     * 生成临时列表     * @param db     * @param daoClasses     */    private void generateTempTables(SQLiteDatabase db, Class<? extends AbstractDao<?, ?>>... daoClasses) {        for(int i = 0; i < daoClasses.length; i++) {            DaoConfig daoConfig = new DaoConfig(db, daoClasses[i]);            String divider = "";            String tableName = daoConfig.tablename;            String tempTableName = daoConfig.tablename.concat("_TEMP");            ArrayList<String> properties = new ArrayList<>();            StringBuilder createTableStringBuilder = new StringBuilder();            createTableStringBuilder.append("CREATE TABLE ").append(tempTableName).append(" (");            for(int j = 0; j < daoConfig.properties.length; j++) {                String columnName = daoConfig.properties[j].columnName;                if(getColumns(db, tableName).contains(columnName)) {                    properties.add(columnName);                    String type = null;                    try {                        type = getTypeByClass(daoConfig.properties[j].type);                    } catch (Exception exception) {                        exception.printStackTrace();                    }                    createTableStringBuilder.append(divider).append(columnName).append(" ").append(type);                    if(daoConfig.properties[j].primaryKey) {                        createTableStringBuilder.append(" PRIMARY KEY");                    }                    divider = ",";                }            }            createTableStringBuilder.append(");");            db.execSQL(createTableStringBuilder.toString());            StringBuilder insertTableStringBuilder = new StringBuilder();            insertTableStringBuilder.append("INSERT INTO ").append(tempTableName).append(" (");            insertTableStringBuilder.append(TextUtils.join(",", properties));            insertTableStringBuilder.append(") SELECT ");            insertTableStringBuilder.append(TextUtils.join(",", properties));            insertTableStringBuilder.append(" FROM ").append(tableName).append(";");            db.execSQL(insertTableStringBuilder.toString());        }    }    /**     * 存储新的数据库表 以及数据     * @param db     * @param daoClasses     */    private void restoreData(SQLiteDatabase 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");            ArrayList<String> properties = new ArrayList();            for (int j = 0; j < daoConfig.properties.length; j++) {                String columnName = daoConfig.properties[j].columnName;                if(getColumns(db, tempTableName).contains(columnName)) {                    properties.add(columnName);                }            }            StringBuilder insertTableStringBuilder = new StringBuilder();            insertTableStringBuilder.append("INSERT INTO ").append(tableName).append(" (");            insertTableStringBuilder.append(TextUtils.join(",", properties));            insertTableStringBuilder.append(") SELECT ");            insertTableStringBuilder.append(TextUtils.join(",", properties));            insertTableStringBuilder.append(" FROM ").append(tempTableName).append(";");            StringBuilder dropTableStringBuilder = new StringBuilder();            dropTableStringBuilder.append("DROP TABLE ").append(tempTableName);            db.execSQL(insertTableStringBuilder.toString());            db.execSQL(dropTableStringBuilder.toString());        }    }    private String getTypeByClass(Class<?> type) throws Exception {        if(type.equals(String.class)) {            return "TEXT";        }        if(type.equals(Long.class) || type.equals(Integer.class) || type.equals(long.class)) {            return "INTEGER";        }        if(type.equals(Boolean.class)) {            return "BOOLEAN";        }        Exception exception = new Exception(CONVERSION_CLASS_NOT_FOUND_EXCEPTION.concat(" - Class: ").concat(type.toString()));        exception.printStackTrace();        throw exception;    }    private static List<String> getColumns(SQLiteDatabase db, String tableName) {        List<String> columns = new ArrayList<>();        Cursor cursor = null;        try {            cursor = db.rawQuery("SELECT * FROM " + tableName + " limit 1", null);            if (cursor != null) {                columns = new ArrayList<>(Arrays.asList(cursor.getColumnNames()));            }        } catch (Exception e) {            Log.v(tableName, e.getMessage(), e);            e.printStackTrace();        } finally {            if (cursor != null)                cursor.close();        }        return columns;    }}

然后在DaoMaster中进行调用:

@Override    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {        Log.i("greenDAO", "Upgrading schema from version " + oldVersion + " to " + newVersion + " by migrating all tables data");        MigrationHelper.getInstance().migrate(db,                UserDao.class,                ItemDao.class);    }
这样就完成了数据库的升级以及数据的迁移。这种方式是不是很简单粗暴? 但在做数据库升级的时候有几点需要注意,第一点也是上文中提到的需要修改数据库的版本号,然后重新运行gernerator这个java类,重新构建实体、构造方法等。第二因为每次运行gernerator方法,都会删除之前实体和dao文件,因此为了不影响app的使用,还需要自行手动的添加一个之前表格式的构造(如果你之前是按照构造方法创建实体的话)。第三,即使数据库表结构没有变动的表最好也在migrate中传进去,因为有人说会出现一些你想不到的错误,博主也没测试过!虽然理论上不会产生错误,但还是为了保险起见,都做一个变化吧!

最后最重要的一点是,修改onUpgrade方法中的代码,否则之前所做的都会前功尽弃,如果这样上线的话,还会导致数据的丢失。这点切记!!!

忘了说了,此处代码都是写在activity中,但博主建议写在application中!

Demo下载地址:

greendao测试代码

0 0
原创粉丝点击