GreenDAO系列之(1)入門

寒翎發表於2017-02-20

關於greenDao

簡介

greenDAO 是一個開源的ORM資料庫框架。它幫助開發者從日常的資料庫的讀寫sql語句中解放出來,開發者只需要關注具體的Java物件,就能夠進行資料庫的訪問操作。

image

greenDao features

1.強大的效能,可能是ORM資料庫中效能最好的。官方把greenDao和OrmLite、ActiveAndroid做的效能對比,資料如下:
image

  1. 易於使用的API
  2. 低記憶體消耗
  3. 包size比較小,如果不包含加密,大概proguard後的包size為8k。
  4. 支援資料庫加密: 支援SQLCipher。SQLCipher對個人開發者來說倒不錯,不過對於公司來說吸引力不大。

greenDao 核心類說明

image

如上圖所示,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


相關文章