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

今天要说的是众所周知的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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值