GreenDAO系列之(1)入门

关于greenDao

简介

greenDAO 是一个开源的ORM数据库框架。它帮助开发者从日常的数据库的读写sql语句中解放出来,开发者只需要关注具体的Java对象,就能够进行数据库的访问操作。

GreenDAO系列之(1)入门

greenDao features

1.强大的性能,可能是ORM数据库中性能最好的。官方把greenDao和OrmLite、ActiveAndroid做的性能对比,数据如下:
GreenDAO系列之(1)入门

  1. 易于使用的API
  2. 低内存消耗
  3. 包size比较小,如果不包含加密,大概proguard后的包size为8k。
  4. 支持数据库加密: 支持SQLCipher。SQLCipher对个人开发者来说倒不错,不过对于公司来说吸引力不大。

greenDao 核心类说明

GreenDAO系列之(1)入门

如上图所示,greenDao有几个核心类:

  1. DaoMaster : db的管理类,是db的总入口,总管的角色,管理着database创建、更新以及数据库中有哪些表之类。每一个db对应一个DaoMaster。
  2. DaoSession : 会话, 管理指定的schema的所有dao,另外DaoSession也支持数据缓存,可以指定dao是否使用缓存。
  3. xxxDao : 某个table的操作对象
  4. Entity : 通俗易懂,不解释了

greenDao工程说明

greenDao 的github包含两个公共组件 :

  1. DaoCore :greenDao的核心框架,我们一般说的greenDao就是指这个
  2. DaoGenerator :通俗易懂,就是xxxDao的生成器,帮助开发者生成xxxDAO对象,开发者只需要定义自己的Entity,通过DaoGenrator就能够生成对应的xxxDao对象。

新手接入

这里演示一个学生的例子


1.创建自己的Android Project,前戏就不贴了

2.导入greenDao

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'org.greenrobot:greendao-gradle-plugin:3.2.1'
    }
}

apply plugin: 'org.greenrobot.greendao'

dependencies {
    compile 'org.greenrobot:greendao:3.2.0'
}

3.编写Entity
我们定义一个Student class:

@Entity
public class Student {
    @Id
    private String mId;
    private String mName;
    private int mGender;

}

@Entity和@Id是greenDao的标签,分别用来表示这是个Entity以及Entity的主键,greenDao Generator会根据这些标签自动生成DaoMaster、DaoSession、StudentDao。 具体的标签的使用方法可以查看官方文档。

4、点击Run, 最后生成的代码如下(部分加了注释):

DaoMaster.java

// THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT.
/**
 * Master of DAO (schema version 1): knows all DAOs.
 */
public class DaoMaster extends AbstractDaoMaster {
    public static final int SCHEMA_VERSION = 1;

    /** Creates underlying database table using DAOs. */
    public static void createAllTables(Database db, boolean ifNotExists) {
        StudentDao.createTable(db, ifNotExists);
    }

    /** Drops underlying database table using DAOs. */
    public static void dropAllTables(Database db, boolean ifExists) {
        StudentDao.dropTable(db, ifExists);
    }

    /**
     * WARNING: Drops all table on Upgrade! Use only during development.
     * Convenience method using a {@link DevOpenHelper}.
     */
    public static DaoSession newDevSession(Context context, String name) {
        Database db = new DevOpenHelper(context, name).getWritableDb();
        DaoMaster daoMaster = new DaoMaster(db);
        return daoMaster.newSession();
    }

    public DaoMaster(SQLiteDatabase db) {
        this(new StandardDatabase(db));
    }

    public DaoMaster(Database db) {
        super(db, SCHEMA_VERSION);
        registerDaoClass(StudentDao.class);
    }

    public DaoSession newSession() {
        return new DaoSession(db, IdentityScopeType.Session, daoConfigMap);
    }

    public DaoSession newSession(IdentityScopeType type) {
        return new DaoSession(db, type, daoConfigMap);
    }

    /**
     * Calls {@link #createAllTables(Database, boolean)} in {@link #onCreate(Database)} -
     */
    public static abstract class OpenHelper extends DatabaseOpenHelper {
        public OpenHelper(Context context, String name) {
            super(context, name, SCHEMA_VERSION);
        }

        public OpenHelper(Context context, String name, CursorFactory factory) {
            super(context, name, factory, SCHEMA_VERSION);
        }

        @Override
        public void onCreate(Database db) {
            Log.i("greenDAO", "Creating tables for schema version " + SCHEMA_VERSION);
            //创建数据库
            createAllTables(db, false);
        }
    }

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

}

DaoSession.java :

// THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT.

/**
 * {@inheritDoc}
 * 
 * @see org.greenrobot.greendao.AbstractDaoSession
 */
public class DaoSession extends AbstractDaoSession {

    private final DaoConfig studentDaoConfig;

    private final StudentDao studentDao;

    public DaoSession(Database db, IdentityScopeType type, Map<Class<? extends AbstractDao<?, ?>>, DaoConfig>
            daoConfigMap) {
        super(db);

        studentDaoConfig = daoConfigMap.get(StudentDao.class).clone();
        //设置缓存类型
        studentDaoConfig.initIdentityScope(type);

        studentDao = new StudentDao(studentDaoConfig, this);

        registerDao(Student.class, studentDao);
    }
    
    //清除缓存
    public void clear() {
        studentDaoConfig.clearIdentityScope();
    }

    public StudentDao getStudentDao() {
        return studentDao;
    }

}

StudentDao.java

// THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT.
/** 
 * DAO for table "STUDENT".
*/
public class StudentDao extends AbstractDao<Student, String> {

    public static final String TABLENAME = "STUDENT";

    /**
     * 定义Studnet表的Column
     */
    public static class Properties {
        public final static Property MId = new Property(0, String.class, "mId", true, "M_ID");
        public final static Property MName = new Property(1, String.class, "mName", false, "M_NAME");
        public final static Property MGender = new Property(2, int.class, "mGender", false, "M_GENDER");
    }


    public StudentDao(DaoConfig config) {
        super(config);
    }
    
    public StudentDao(DaoConfig config, DaoSession daoSession) {
        super(config, daoSession);
    }

    /** 创建Student表,这种创建方法有点太low了居然i无法做到自动创建 */
    public static void createTable(Database db, boolean ifNotExists) {
        String constraint = ifNotExists? "IF NOT EXISTS ": "";
        db.execSQL("CREATE TABLE " + constraint + "\"STUDENT\" (" + //
                "\"M_ID\" TEXT PRIMARY KEY NOT NULL ," + // 0: mId
                "\"M_NAME\" TEXT," + // 1: mName
                "\"M_GENDER\" INTEGER NOT NULL );"); // 2: mGender
    }

    /** Drops the underlying database table. */
    public static void dropTable(Database db, boolean ifExists) {
        String sql = "DROP TABLE " + (ifExists ? "IF EXISTS " : "") + "\"STUDENT\"";
        db.execSQL(sql);
    }

    /**
     * 插入语句时绑定的数据,这种写法有点坑,万一MId为空时,生成的语句就有w
     */
    @Override
    protected final void bindValues(DatabaseStatement stmt, Student entity) {
        stmt.clearBindings();
 
        String mId = entity.getMId();
        if (mId != null) {
            stmt.bindString(1, mId);
        }
 
        String mName = entity.getMName();
        if (mName != null) {
            stmt.bindString(2, mName);
        }
        stmt.bindLong(3, entity.getMGender());
    }

    @Override
    protected final void bindValues(SQLiteStatement stmt, Student entity) {
        stmt.clearBindings();
 
        String mId = entity.getMId();
        if (mId != null) {
            stmt.bindString(1, mId);
        }
 
        String mName = entity.getMName();
        if (mName != null) {
            stmt.bindString(2, mName);
        }
        stmt.bindLong(3, entity.getMGender());
    }

    @Override
    public String readKey(Cursor cursor, int offset) {
        return cursor.isNull(offset + 0) ? null : cursor.getString(offset + 0);
    }    

    /**
     * 从sql读取数据,生成student对象
     */
    @Override
    public Student readEntity(Cursor cursor, int offset) {
        Student entity = new Student( //
            cursor.isNull(offset + 0) ? null : cursor.getString(offset + 0), // mId
            cursor.isNull(offset + 1) ? null : cursor.getString(offset + 1), // mName
            cursor.getInt(offset + 2) // mGender
        );
        return entity;
    }
     
    @Override
    public void readEntity(Cursor cursor, Student entity, int offset) {
        entity.setMId(cursor.isNull(offset + 0) ? null : cursor.getString(offset + 0));
        entity.setMName(cursor.isNull(offset + 1) ? null : cursor.getString(offset + 1));
        entity.setMGender(cursor.getInt(offset + 2));
     }
    
    @Override
    protected final String updateKeyAfterInsert(Student entity, long rowId) {
        return entity.getMId();
    }
    
    @Override
    public String getKey(Student entity) {
        if(entity != null) {
            return entity.getMId();
        } else {
            return null;
        }
    }

    @Override
    public boolean hasKey(Student entity) {
        return entity.getMId() != null;
    }

    @Override
    protected final boolean isEntityUpdateable() {
        return true;
    }
    
}

5.使用

  • 初始化数据库
DevOpenHelper helper = new DevOpenHelper(this, "students");
Database db = helper.getWritableDb();
mDaoSession = new DaoMaster(db).newSession();
mStudentDao = mDaoSession.getStudentDao();
  • 添加
Student student = new Student();
student.setMId(String.valueOf(System.currentTimeMillis()));
student.setMName("name"+System.currentTimeMillis());
student.setMGender((int) (System.currentTimeMillis()%2));
mStudentDao.insert(student);
  • 删除
final Student deleteStudent = mStudentDao.queryBuilder().orderDesc(StudentDao.Properties.MId).limit(1).unique();

mStudentDao.delete(deleteStudent);

小结

从上面的demo,可以看到访问数据库的过程变得非常的简单。开发者只需要定义自己的entity,greenDao Generator就能自动生成对应的类文件。整个使用过程非常简单,但是,一般说到但是,就是来讲它的坏话了。。。我们也看到greenDao有如下几个问题:

  1. greenDao Generator仍然有点笨,生成的代码真心是笨(如:要是我们的变量是以m开头,则会生成 getM,setM方法。),还需要我们进一步加工,才能变成比较美观的代码。
  2. greenDao的DaoMaster在数据库的创建以及更新上都使用了指定sql语句的方法,比较笨拙,虽然网上有一个叫做MigrationHelper的解决方案,但仍不够友好。后续再给大家介绍一下我们使用的方法。
  3. xxxDao的Property支持的属性有限

如:Property.java

public final int ordinal;
public final java.lang.Class<?> type;
public final java.lang.String name;
public final boolean primaryKey;
public final java.lang.String columnName;

不支持default、is null、unique 等属性


Reference: https://github.com/greenrobot/greenDAO

上一篇:《第一行代码》百分比布局出现的问题


下一篇:关于config.gradle的使用