1. 程式人生 > >Android ORM資料庫之GreenDao使用教程及原始碼分析

Android ORM資料庫之GreenDao使用教程及原始碼分析

一、簡介
1.Android ORM介紹
 在平時的開發過程中,大家一定會或多或少地接觸到 SQLite。然而在使用它時,我們往往需要做許多額外的工作,像編寫 SQL 語句與解析查詢結果等。所以,適用於 Android 的ORM 框架也就孕育而生了,現在市面上主流的框架有 OrmLite、SugarORM、Active Android、Realm 與 GreenDAO。下面先介紹下當前流行的5種ORM資料庫框架:
1)OrmLite
 OrmLite不是Android 平臺專用的ORM框架,它是Java ORM。支援JDBC連線,Spring以及Android平臺。語法中廣泛使用了註解(Annotation)。

2)SugarORM
 SugarORM是Android 平臺專用ORM。提供簡單易學的APIs。可以很容易的處理1對1和1對多的關係型資料,並通過3個函式save(), delete() 和 find() (或者 findById()) 來簡化CRUD基本操作。

3)Active Android
 Active Record(活動目錄)是Yii、Rails等框架中對ORM實現的典型命名方式。Active Android 幫助你以面向物件的方式來操作SQLite。

4)Realm
 Realm 是一個將可以使用的Android ORM,基於C++編寫,直接執行在你的裝置硬體上(不需要被解釋),因此執行很快。它同時是開源跨平臺的,iOS的程式碼可以在GitHub找到。

5)GreenDAO
 GreenDAO 是一個將物件對映到 SQLite 資料庫中的輕量且快速的 ORM 解決方案。

2.GreenDao介紹及優點

介紹:
 GreenDAO就是實現Java物件和SQLite Datebase的一個媒介人,簡化了SQLite的操作,而且他的資料庫操作效率非常高。可以幫助Android開發者快速將Java物件對映到SQLite資料庫的表單中的ORM解決方案,通過使用一個簡單的面向物件API,開發者可以對Java物件進行儲存、更新、刪除和查詢。
官網網址:http://greenrobot.org/greendao/features//

優點:
1)最大效能(最快的Android ORM),GreenDAO 效能遠遠高於同類的 ORMLite(可見下圖);
2)簡易API,通過Java工程生成的類中方法全都自動寫好了,可以直接呼叫;
3)高度優化,GreenDAO 支援 protocol buffer(protobuf) 協議資料的直接儲存,如果你通過 protobuf 協議與伺服器互動,將不需要任何的對映;與ORMLite等使用註解方式的ORM框架不同,greenDAO使用Code generation的方式,這也是其效能能大幅提升的原因。
4)最小記憶體消耗。
GreenDAO與ORMLite比較

二、配置

配置Java工程
1)在Android Studio中選擇File -> New -> New Module -> Java Library建立GreenDAO Generate工程;並且在該工程build.gradlew裡新增以下依賴:

compile 'org.greenrobot:greendao-generator:2.2.0'

2)在新建的Java工程中新建一個Java類,該Java類用於生成專案所需的Bean、DAO等檔案,以下是該類需要寫的程式碼:

public class ExampleDaoGenerator {

    public static void main(String[] args) throws Exception {
        // 建立了一個用於新增實體(Bean)的模式(Schema)物件。
        // 兩個引數分別代表:資料庫版本號與自動生成程式碼的包路徑。
        Schema schema = new Schema(1, "com.example.jianglei.greendaotestdemo.db.bean");
        // 也可以分別指定生成的 Bean 與 DAO 類所在的目錄
        schema.setDefaultJavaPackageDao("com.example.jianglei.greendaotestdemo.db.dao");
        //通過次Schema物件新增的所有實體都不會覆蓋自定義的程式碼
        // schema.enableKeepSectionsByDefault();
        // 建立Schema物件後,就可以使用它新增實體(Bean)了。
        addUser(schema);

        // 最後使用 DAOGenerator 類的 generateAll()方法自動生成程式碼,根據自己的情況更改輸出目錄
        new DaoGenerator().generateAll(schema,                   
                         "D:\\WorkSpace_01\\GreenDaoTestDemo\\app\\src\\main\\java");
    }

    /**
     * @param schema
     */
    private static void addUser(Schema schema) {
        // 一個實體(類)就關聯到資料庫中的一張表,此處表名為「User」
        Entity note = schema.addEntity("User");
        // 也可以重新給表命名
        // note.setTableName("NODE");
        //單獨讓某個實體不覆蓋自定義的程式碼
        // note.setHasKeepSections(true);
        // greenDAO 會自動根據實體類的屬性值來建立表字段,並賦予預設值
        note.addIdProperty().notNull().primaryKey();
        note.addStringProperty("name").notNull();
        // 與在 Java 中使用駝峰命名法不同,預設資料庫中的命名是使用大寫和下劃線來分割單詞的。
        note.addIntProperty("age").notNull();
    }
}

完成該類的編寫後,點選Run來執行這個類可以看到:
自動生成所需要的類
生成目錄

三、工作原理

四個核心類的功能體系及工作原理如下圖所示:
這裡寫圖片描述  這裡寫圖片描述

DaoMaster:
 一看名字就知道它是Dao中的最大的官了。它儲存了sqlitedatebase物件以及操作DAO classes(注意:不是物件)。其提供了一些建立和刪除table的靜態方法,其內部類OpenHelper和DevOpenHelper實現了SQLiteOpenHelper並建立資料庫的框架。

public class DaoMaster extends AbstractDaoMaster {
    public static final int SCHEMA_VERSION = 1;

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

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

    public static abstract class OpenHelper extends SQLiteOpenHelper {

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

        @Override
        public void onCreate(SQLiteDatabase 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, 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);
        }
    }

    public DaoMaster(SQLiteDatabase db) {
        super(db, SCHEMA_VERSION);
        registerDaoClass(UserDao.class);
    }

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

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

}

 從DaoMaster中我們可以發現,DaoMaster除了具有建立表和刪除表的兩個功能外,還有兩個內部類,分別為OpenHelper和DevOpenHelper,而DevOpenHelper繼承自OpenHelper,而OpenHelper繼承自SQLiteOpenHelper,而重寫的onCreate()方法中呼叫了createAllTables(db,false);方法來建立資料表,而createAllTables()方法中是通過呼叫UserDao靜態方法來建立表的UserDao.createTable(db, ifNotExists);我們點進這個方法中去看個究竟:

/** Creates the underlying database table. */
    public static void createTable(SQLiteDatabase db, boolean ifNotExists) {
        String constraint = ifNotExists? "IF NOT EXISTS ": "";
        db.execSQL("CREATE TABLE " + constraint + "\"USER\" (" + //
                "\"_id\" INTEGER PRIMARY KEY NOT NULL ," + // 0: id
                "\"NAME\" TEXT NOT NULL ," + // 1: name
                "\"AGE\" INTEGER NOT NULL );"); // 2: age
    }

 發現它內部就是通過sql語句來建立表的,只不過GreenDAO幫我們封裝好了,而且你會發現刪除表其實也一樣:

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

 在DevOpenHelper類中實現了onUpgrade()方法,就是更新資料庫的方法,它在更新資料表的時候會把以前的資料表刪除後再重新建立,所以這個你必須注意,當我們在利用GreenDAO更新資料表的時候,如果你想以前表中的資料儲存下來的話,我們必須自己封裝一個方法。接下來就是newSession()方法了,這個當然就是得到DaoSession例項了,關於DaoSession例項,GreenDAO官方建議不要重新建立新的例項,保持一個單例的引用即可。
 接下來就是看DaoMaster的父類AbstractDaoMaster的原始碼了,它的原始碼如下:

public abstract class AbstractDaoMaster {
    protected final SQLiteDatabase db;
    protected final int schemaVersion;
    protected final Map<Class<? extends AbstractDao<?, ?>>, DaoConfig> daoConfigMap;

    public AbstractDaoMaster(SQLiteDatabase db, int schemaVersion) {
        this.db = db;
        this.schemaVersion = schemaVersion;

        daoConfigMap = new HashMap<Class<? extends AbstractDao<?, ?>>, DaoConfig>();
    }

    protected void registerDaoClass(Class<? extends AbstractDao<?, ?>> daoClass) {
        DaoConfig daoConfig = new DaoConfig(db, daoClass);
        daoConfigMap.put(daoClass, daoConfig);
    }

    public int getSchemaVersion() {
        return schemaVersion;
    }

    /** Gets the SQLiteDatabase for custom database access. Not needed for greenDAO entities. */
    public SQLiteDatabase getDatabase() {
        return db;
    }

    public abstract AbstractDaoSession newSession();

    public abstract AbstractDaoSession newSession(IdentityScopeType type);
}

 看這個類的程式碼,最重要的就是這一行了:

protected final Map<Class<? extends AbstractDao<?, ?>>, DaoConfig> daoConfigMap;

 這裡定義了一個Map集合,Key是繼承自AbstractDao類的位元組碼物件,Value則為DaoConfig物件,而往這個Map集合中put資料是通過這個方法registerDaoClass(),程式碼在上面可以看到。Map的功能就是為每一個EntityDao位元組碼物件建立與之對應的db資料庫的對映關係,從而管理所有的EntityDao類。
再回到DaoMaster,可以看到DaoMaster類的構造方法中呼叫了registerDaoClass(),把EntityDao類和相應的資料庫db建立關聯。

DaoSession:
 操作具體的DAO物件(注意:是物件)。
 從上面可知DaoSession物件是通過master.newSession();建立的。我們看看DaoSession原始碼,發現它也有一個抽象的父類AbstractDaoSession,我們來看看DaoSession的原始碼:

public class DaoSession extends AbstractDaoSession {

    private final DaoConfig userDaoConfig;

    private final UserDao userDao;

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

        userDaoConfig = daoConfigMap.get(UserDao.class).clone();
        userDaoConfig.initIdentityScope(type);

        userDao = new UserDao(userDaoConfig, this);

        registerDao(User.class, userDao);
    }

    public void clear() {
        userDaoConfig.getIdentityScope().clear();
    }

    public UserDao getUserDao() {
        return userDao;
    }

}

 最主要的一個方法就是通過getUserDao()來得到UserDao例項,而建立一個UserDao物件正是在DaoSession的構造方法中:

userDao = new UserDao(userDaoConfig, this);

 這個正是從在DaoMaster建立的Map集合中取出key為UserDao.class的DaoConfig物件,剛剛就說了Map集合中儲存了UserDao類對應的資料庫db的關係對映,而這個DaoConfig物件正是管理了對應的db物件。然後把這個DaoConfig傳給UserDao(userDaoConfig, this),所以這就說明了我們使用UserDao物件來進行資料庫上的CRUD操作而對應的資料庫也會變化的原因,這個過程實際上就是在間接操作資料庫。
 下面來就是看看它的父類AbstractDaoSession:

public class AbstractDaoSession {
    private final SQLiteDatabase db;
    private final Map<Class<?>, AbstractDao<?, ?>> entityToDao;

    public AbstractDaoSession(SQLiteDatabase db) {
        this.db = db;
        this.entityToDao = new HashMap<Class<?>, AbstractDao<?, ?>>();
    }

    protected <T> void registerDao(Class<T> entityClass, AbstractDao<T, ?> dao) {
        entityToDao.put(entityClass, dao);
    }

    /** Convenient call for {@link AbstractDao#insert(Object)}. */
    public <T> long insert(T entity) {
        @SuppressWarnings("unchecked")
        AbstractDao<T, ?> dao = (AbstractDao<T, ?>) getDao(entity.getClass());
        return dao.insert(entity);
    }

    /** Convenient call for {@link AbstractDao#insertOrReplace(Object)}. */
    public <T> long insertOrReplace(T entity) {
        @SuppressWarnings("unchecked")
        AbstractDao<T, ?> dao = (AbstractDao<T, ?>) getDao(entity.getClass());
        return dao.insertOrReplace(entity);
    }

    /** Convenient call for {@link AbstractDao#refresh(Object)}. */
    public <T> void refresh(T entity) {
        @SuppressWarnings("unchecked")
        AbstractDao<T, ?> dao = (AbstractDao<T, ?>) getDao(entity.getClass());
        dao.refresh(entity);
    }

    /** Convenient call for {@link AbstractDao#update(Object)}. */
    public <T> void update(T entity) {
        @SuppressWarnings("unchecked")
        AbstractDao<T, ?> dao = (AbstractDao<T, ?>) getDao(entity.getClass());
        dao.update(entity);
    }

    /** Convenient call for {@link AbstractDao#delete(Object)}. */
    public <T> void delete(T entity) {
        @SuppressWarnings("unchecked")
        AbstractDao<T, ?> dao = (AbstractDao<T, ?>) getDao(entity.getClass());
        dao.delete(entity);
    }

    /** Convenient call for {@link AbstractDao#deleteAll()}. */
    public <T> void deleteAll(Class<T> entityClass) {
        @SuppressWarnings("unchecked")
        AbstractDao<T, ?> dao = (AbstractDao<T, ?>) getDao(entityClass);
        dao.deleteAll();
    }

    /** Convenient call for {@link AbstractDao#load(Object)}. */
    public <T, K> T load(Class<T> entityClass, K key) {
        @SuppressWarnings("unchecked")
        AbstractDao<T, K> dao = (AbstractDao<T, K>) getDao(entityClass);
        return dao.load(key);
    }

    /** Convenient call for {@link AbstractDao#loadAll()}. */
    public <T, K> List<T> loadAll(Class<T> entityClass) {
        @SuppressWarnings("unchecked")
        AbstractDao<T, K> dao = (AbstractDao<T, K>) getDao(entityClass);
        return dao.loadAll();
    }

    /** Convenient call for {@link AbstractDao#queryRaw(String, String...)}. */
    public <T, K> List<T> queryRaw(Class<T> entityClass, String where, String... selectionArgs) {
        @SuppressWarnings("unchecked")
        AbstractDao<T, K> dao = (AbstractDao<T, K>) getDao(entityClass);
        return dao.queryRaw(where, selectionArgs);
    }

    /** Convenient call for {@link AbstractDao#queryBuilder()}. */
    public <T> QueryBuilder<T> queryBuilder(Class<T> entityClass) {
        @SuppressWarnings("unchecked")
        AbstractDao<T, ?> dao = (AbstractDao<T, ?>) getDao(entityClass);
        return dao.queryBuilder();
    }

    public AbstractDao<?, ?> getDao(Class<? extends Object> entityClass) {
        AbstractDao<?, ?> dao = entityToDao.get(entityClass);
        if (dao == null) {
            throw new DaoException("No DAO registered for " + entityClass);
        }
        return dao;
    }

    /**
     * Run the given Runnable inside a database transaction. If you except a result, consider callInTx.
     */
    public void runInTx(Runnable runnable) {
        db.beginTransaction();
        try {
            runnable.run();
            db.setTransactionSuccessful();
        } finally {
            db.endTransaction();
        }
    }

    /**
     * Calls the given Callable inside a database transaction and returns the result of the Callable. If you don't
     * except a result, consider runInTx.
     */
    public <V> V callInTx(Callable<V> callable) throws Exception {
        db.beginTransaction();
        try {
            V result = callable.call();
            db.setTransactionSuccessful();
            return result;
        } finally {
            db.endTransaction();
        }
    }

    /**
     * Like {@link #callInTx(Callable)} but does not require Exception handling (rethrows an Exception as a runtime
     * DaoException).
     */
    public <V> V callInTxNoException(Callable<V> callable) {
        db.beginTransaction();
        try {
            V result;
            try {
                result = callable.call();
            } catch (Exception e) {
                throw new DaoException("Callable failed", e);
            }
            db.setTransactionSuccessful();
            return result;
        } finally {
            db.endTransaction();
        }
    }

    /** Gets the SQLiteDatabase for custom database access. Not needed for greenDAO entities. */
    public SQLiteDatabase getDatabase() {
        return db;
    }

    /** Allows to inspect the meta model using DAOs (e.g. querying table names or properties). */
    public Collection<AbstractDao<?, ?>> getAllDaos() {
        return Collections.unmodifiableCollection(entityToDao.values());
    }

    /**
     * Creates a new {@link AsyncSession} to issue asynchronous entity operations. See {@link AsyncSession} for details.
     */
    public AsyncSession startAsyncSession() {
        return new AsyncSession(this);
    }

}

 可以看到它的父類中,大部分方法都是進行CRUD操作的,而事實上我們在進行CRUD操作都是通過UserDao物件來進行的,實際上這兩種做法沒有區別,因為它內部本身就是通過dao物件來進行CRUD操作的,大家看看這些方法的返回值就知道了。
 在DaoSession和UserDao呼叫CRUD操作中,查詢操作比較特殊,原因是GreenDao在查詢這塊加了快取 ,GreenDao在查詢時使用了弱引用WeakReference,對已經查詢過的資料,先查詢這個引用而不是查詢資料庫(前提是沒有GC),速度更快。
 這個快取的程式碼是在AbstractQueryData類中,如下:

Q forCurrentThread() {
        int threadId = Process.myTid();
        if (threadId == 0) {
            // Workaround for Robolectric, always returns 0
            long id = Thread.currentThread().getId();
            if (id < 0 || id > Integer.MAX_VALUE) {
                throw new RuntimeException("Cannot handle thread ID: " + id);
            }
            threadId = (int) id;
        }
        synchronized (queriesForThreads) {
            WeakReference<Q> queryRef = queriesForThreads.get(threadId);
            Q query = queryRef != null ? queryRef.get() : null;
            if (query == null) {
                gc();
                query = createQuery();
                queriesForThreads.put(threadId, new WeakReference<Q>(query));
            } else {
                System.arraycopy(initialValues, 0, query.parameters, 0, initialValues.length);
            }
            return query;
        }
    }

UserDao:
 實際生成的UserDao類,通常對應具體的java類,其有更多的許可權和方法來操作資料庫元素。
先看看UserDao類的原始碼:

public class UserDao extends AbstractDao<User, Long> {

    public static final String TABLENAME = "USER";

    /**
     * Properties of entity User.<br/>
     * Can be used for QueryBuilder and for referencing column names.
    */
    public static class Properties {
        public final static Property Id = new Property(0, Long.class, "id", true, "_id");
        public final static Property Name = new Property(1, String.class, "name", false, "NAME");
        public final static Property Age = new Property(2, int.class, "age", false, "AGE");
    }


    public UserDao(DaoConfig config) {
        super(config);
    }

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

    /** Creates the underlying database table. */
    public static void createTable(SQLiteDatabase db, boolean ifNotExists) {
        String constraint = ifNotExists? "IF NOT EXISTS ": "";
        db.execSQL("CREATE TABLE " + constraint + "\"USER\" (" + //
                "\"_id\" INTEGER PRIMARY KEY ," + // 0: id
                "\"NAME\" TEXT NOT NULL ," + // 1: name
                "\"AGE\" INTEGER NOT NULL );"); // 2: age
    }

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

    /** @inheritdoc */
    @Override
    protected void bindValues(SQLiteStatement stmt, User entity) {
        stmt.clearBindings();

        Long id = entity.getId();
        if (id != null) {
            stmt.bindLong(1, id);
        }
        stmt.bindString(2, entity.getName());
        stmt.bindLong(3, entity.getAge());
    }

    /** @inheritdoc */
    @Override
    public Long readKey(Cursor cursor, int offset) {
        return cursor.isNull(offset + 0) ? null : cursor.getLong(offset + 0);
    }    

    /** @inheritdoc */
    @Override
    public User readEntity(Cursor cursor, int offset) {
        User entity = new User( //
            cursor.isNull(offset + 0) ? null : cursor.getLong(offset + 0), // id
            cursor.getString(offset + 1), // name
            cursor.getInt(offset + 2) // age
        );
        return entity;
    }

    /** @inheritdoc */
    @Override
    public void readEntity(Cursor cursor, User entity, int offset) {
        entity.setId(cursor.isNull(offset + 0) ? null : cursor.getLong(offset + 0));
        entity.setName(cursor.getString(offset + 1));
        entity.setAge(cursor.getInt(offset + 2));
     }

    /** @inheritdoc */
    @Override
    protected Long updateKeyAfterInsert(User entity, long rowId) {
        entity.setId(rowId);
        return rowId;
    }

    /** @inheritdoc */
    @Override
    public Long getKey(User entity) {
        if(entity != null) {
            return entity.getId();
        } else {
            return null;
        }
    }

    /** @inheritdoc */
    @Override    
    protected boolean isEntityUpdateable() {
        return true;
    }

}

 除了之前講的createTable和dropTable方法以外,比較重要的就是bindValues()這個方法,它是用來繫結實體的屬性名和表中的欄位名的;Property是用來得到這個屬性對應表中的列名、是否為主鍵等值,這個為查詢、更新等提供了條件。UserDao的父類AbstractDao原始碼中主要是一些CRUD方法和其它的一些方法,這裡就不再多作介紹了。

User:
 對於實體類,這沒什麼可講的,就是一個Bean,一個實體類對應一張表,實體類裡面有對應各個欄位的getter和setter方法。通常代表了一個數據庫row的標準java properties。

四、專案使用

 前面很大篇幅講了些工作原理的分析,下面就到了專案使用階段,相信只要理解上面的工作原理,大家使用起來就非常順手了,而且GreenDao封裝了很多簡易的CRDU方法。

基本使用
 我們首先來看看使用GreenDAO的基本步驟:

// 生成資料庫檔案,名為user-db
DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(this, "students-db", null);
SQLiteDatabase db = helper.getWritableDatabase();
// 建立特定模式下的所有的DAO物件和資料庫db物件的對映
DaoMaster master = new DaoMaster(db);
// 管理特定模式下的所有DAO物件,並提供一些通用的CRUD持久化方法
DaoSession session = master.newSession();
// 得到指定的UserDao物件
UserDao dao = session.getUserDao();
dao.insert(student);
//...

 GreenDao不僅資料庫建立幫我們寫好,也提供了很多CRDU方法,大家可以獲取dao物件後通過”.”這個看到有很多方法供使用,這些都是基本用法,下面介紹一些常用的:

/*
增加:
dao.insert(Student entity);//新增一個
dao.insertInTx(Student...entity);//批量新增

刪除:
dao.deleteByKey(Long key);//根據主鍵刪除
dao.deleteByKeyInTx(Long...keys);//批量刪除
dao.delete(Student entity);//根據實體刪除
dao.deleteInTx(Student... entities);//

批量刪除
dao.deleteAll();//全部刪除

修改:
dao.update(Student entity);//根據實體更新
dao.updateInTx(Student...entities);//批量更新

查詢:
Query query = dao.queryBuilder().where(StudentDao.Properties.Name.eq(content)).build();
List list = query.list();//或者利用sql語言查詢
Query query = dao.queryBuilder().where( new StringCondition("_ID IN "+"(SELECT _ID FROM USER WHERE AGE = 20)").build()
*/

封裝使用
 好了,上面講了基本用法,但是想想,我們專案裡,不可能用到資料庫的地方就初始化這麼一大堆吧,而且咱封裝的資料庫方法也不想暴露給使用者,那就要要自己封裝了,下面是我自己研究封裝了一個Helper類,大家可以看看:

/**
 * Created by jianglei on 2016/6/30.
 */
public class GreenDaoHelper<T> {

    private static final String NB_NAME = "demo";

    private static GreenDaoHelper mGreenDaoInstance;

    private static Context mApplicationContext;

    private DaoMaster.DevOpenHelper mDaoHelper;

    private DaoMaster mDaoMaster;

    private DaoSession mDaoSession;

    private GreenDaoHelper(Context context) {
        mDaoHelper = new DaoMaster.DevOpenHelper(context, NB_NAME, null);
        mDaoMaster = new DaoMaster(mDaoHelper.getWritableDatabase());
        mDaoSession = mDaoMaster.newSession();
    }

    public static void initGreenDao(Context context) {
        mApplicationContext = context;
        getInstance();
    }

    public static GreenDaoHelper getInstance() {
        if (mGreenDaoInstance == null) {
            synInit(mApplicationContext);
        }
        return mGreenDaoInstance;
    }

    private synchronized static void synInit(Context context) {
        if (mGreenDaoInstance == null) {
            mGreenDaoInstance = new GreenDaoHelper(context);
        }
    }

    /**
     * 單條插入表
     *
     * @param value 傳入bean
     */
    @SuppressWarnings("unchecked")
    public void singleInsert(T value) {
        AbstractDao dao = null;
        try {
            mDaoSession.insert(value);
            dao = mDaoSession.getDao(value.getClass());
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (dao != null) {
            dao.insert(value);
        } else {
            throw new IllegalArgumentException("The argument you pass is incorrect!!!");
        }
    }

    /**
     * 多條插入表
     *
     * @param values 傳入bean的list
     */
    @SuppressWarnings("unchecked")
    public void multiInsert(T values) {
        List<Object> list = null;
        AbstractDao dao = null;
        try {
            list = (List<Object>) values;
            dao = mDaoSession.getDao(list.get(0).getClass());
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (dao != null && list.size() > 0) {
            dao.insertInTx(list);
        } else {
            throw new IllegalArgumentException("The argument you pass is incorrect!!!");
        }
    }

    /**
     * 單條更新
     *
     * @param value 傳入bean
     */
    @SuppressWarnings("unchecked")
    public void singleUpdate(T value) {
        AbstractDao dao = null;
        try {
            dao = mDaoSession.getDao(value.getClass());
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (dao != null) {
            dao.update(value);
        } else {
            throw new IllegalArgumentException("The argument you pass is incorrect!!!");
        }
    }

    /**
     * 多條更新
     *
     * @param values 傳入bean的list
     */
    @SuppressWarnings("unchecked")
    public void multiUpdate(T values) {
        List<Object> list = null;
        AbstractDao dao = null;
        try {
            list = (List<Object>) values;
            dao = mDaoSession.getDao(list.get(0).getClass());
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (dao != null && list.size() > 0) {
            dao.updateInTx(list);
        } else {
            throw new IllegalArgumentException("The argument you pass is incorrect!!!");
        }
    }

    /**
     * 查詢所有
     *
     * @param clazz 傳入所需查詢bean的class
     */
    @SuppressWarnings("unchecked")
    public List queryAll(Class clazz) {
        AbstractDao dao = null;
        try {
            dao = mDaoSession.getDao(clazz);
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (dao != null) {
            return dao.queryBuilder().list();
        } else {
            throw new IllegalArgumentException("The argument you pass is incorrect!!!");
        }
    }

    /**
     * 條件查詢
     * @param clazz 傳入所需查詢bean的class
     * @param condition 傳入查詢條件
     */
    @SuppressWarnings("unchecked")
    public List queryWithFilter(Class clazz, WhereCondition condition) {
        AbstractDao dao = null;
        try {
            dao = mDaoSession.getDao(clazz);
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (dao != null) {
            return dao.queryBuilder().where(condition).list();
        } else {
            throw new IllegalArgumentException("The argument you pass is incorrect!!!");
        }
    }

    /**
     * 刪除所有
     *
     * @param clazz 傳入所需刪除bean的class
     */
    @SuppressWarnings("unchecked")
    public void deleteAll(Class clazz) {
        AbstractDao dao = null;
        try {
            dao = mDaoSession.getDao(clazz);
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (dao != null) {
            dao.deleteAll();
        } else {
            throw new IllegalArgumentException("The argument you pass is incorrect!!!");
        }
    }

    /**
     * 釋放資料庫
     */
    public void closeGreenDao() {
        if (mDaoHelper != null) mDaoHelper.close();
        if (mDaoSession != null) mDaoSession.clear();
    }
}

 大家可以看到,我在封裝的時候,只需要傳入實體類就行了,那這裡又是怎麼知道它自己的Dao呢,其實這個就要回到前面講AbstractDaoSession的時候,大家可以看到裡面定義了一個Map和一個registerDao方法:

/*AbstractDaoSession*/
private final Map<Class<?>, AbstractDao<?, ?>> entityToDao;

protected <T> void registerDao(Class<T> entityClass, AbstractDao<T, ?> dao) {
        entityToDao.put(entityClass, dao);
}

/*DaoMaster*/
public DaoMaster(SQLiteDatabase db) {
        super(db, SCHEMA_VERSION);
        registerDaoClass(UserDao.class);
}

 而在DaoMaster的建構函式的時候註冊了User的Dao,我就可以通過bean.getClass()獲取到它是哪個實體類clazz,然後通過DaoSession的mDaoSession.getDao(clazz)獲取它的Dao,這樣獲取Dao之後就通過它去CRDU操作,其他的方法我都寫了註釋,大家可以自己看咯。
 在專案中使用:
 1)Application中初始化:

GreenDaoHelper.initGreenDao(this);

 2)需要使用資料庫的地方呼叫:

GreenDaoHelper.getInstance().(CRDU方法);

相關推薦

Android ORM資料庫GreenDao使用教程原始碼分析

一、簡介 1.Android ORM介紹  在平時的開發過程中,大家一定會或多或少地接觸到 SQLite。然而在使用它時,我們往往需要做許多額外的工作,像編寫 SQL 語句與解析查詢結果等。所以,適用於 Android 的ORM 框架也就孕育而生了,現在市面

Android ORM 框架 greenDAO 使用心得

前言 我相信,在平時的開發過程中,大家一定會或多或少地接觸到 SQLite。然而在使用它時,我們往往需要做許多額外的工作,像編寫 SQL 語句與解析查詢結果等。所以,適用於 Android 的ORM 框架也就孕育而生了,現在市面上主流的框架有 OrmLite、SugarOR

Android ORM系列GreenDao最佳實踐

GreenDAO是一個可以幫助Android開發者快速將Java物件對映到SQLite資料庫的表單中的ORM解決方案,通過使用一個簡單的面向物件API,開發者可以對Java物件進行儲存、更新、刪除和查詢。 GreenDao有兩個專案,一個是生成dao和

Android與JSJsBridge使用與原始碼分析

在Android開發中,由於Native開發的成本較高,H5頁面的開發更靈活,修改成本更低,因此前端網頁JavaScript(下面簡稱JS)與Java之間的互相呼叫越來越常見。 JsBridge就是一個簡化Android與JS通訊的框架,原始碼:https://github.com/lzyzsd

Spring DataRepository建立原始碼分析

背景 在上一篇文章Spring Data之EntityManager建立及原始碼分析介紹了EntityManager的建立過程,我們使用Spring Data JPA開發時一般是通過自定義一個例如UserRepository的介面,然後繼承JpaRepository或者CrudRepos

Spring DataEntityManager建立原始碼分析

背景 前一篇文章介紹了EntityManagerFactory的建立過程,有了EntityManagerFactory接下來就是要獲取EntityManager了,但EntityManager的建立不再是通過@Conditional註解,而是使用的@PersistenceContext註

Spring DataEntityManagerFactory建立原始碼分析

背景 在Spring Data之JPA開篇中可以看到Spring Boot的啟動日誌,先是建立了HikariDataSource,然後緊接著構建了EntityManagerFactory 2018-10-25 09:32:20.645 INFO 37469 --- [

Spring DataDataSource建立原始碼分析

背景 俗話說萬變不離其宗,程式碼中對資料庫的操作,首先是要獲取資料庫連線,而Java中最原生的連線方式就是通過DriverManager private static String driver = "org.h2.Driver"; private static String url

Java中執行緒區域性變數ThreadLocal使用教程原始碼分析

       在Java多執行緒程式設計中有時候會遇見執行緒本地區域性變數ThreadLocal這個類,下面就來講講ThreadLocal的使用及原始碼分析。        ThreadLocal 是Thread Local Varial(執行緒區域性變數)的意思,每個執行

Android 進階】ORM 框架 greenDAO學習筆記

前言 當初學習Hibernate的時候就非常驚歎這種ORM思想,後來才知道原來Android中也有這種基於ORM思想的開源框架greenDAO。 greenDAO簡介: 簡單的講,greenDAO 是一個將物件對映到 SQLite 資料庫

Android 三級快取(記憶體!!!、本地、網路)記憶體LruCache擴充套件 原始碼分析--- 學習和程式碼講解

一. 三級快取簡介 如上圖所示,目前App中UI介面經常會涉及到圖片,特別是像“今日關注”新聞這類app中,圖片運用的機率十分頻繁。當手機上需要顯示大量圖片類似listView、gridView控制元件並且使用者會上下滑動,即將瀏覽過的圖片又載入一遍,

Android ORM 框架:GreenDao 資料庫升級

前言 一,GreenDao 預設的升級方式 GreenDao 預設的升級方式是刪除所有舊版,在重新建新表,這樣一來使用者的本地歷史資料則會丟失,這點我們通過DaoMaster 的內部類 DevOpenHelper 原始碼可以瞭解到。

Mysql備份還原資料庫mysqldump例項引數詳細說明

Mysql備份還原資料庫之mysqldump例項及引數詳細說明 我們在運營專案的過程中肯定會遇到備份資料庫,還原資料庫的情況,我們一般用一下兩種方式來處理: 1.使用into outfile 和 load data infile匯入匯出備份資料 這種方法的好處是,匯出

資料庫Oracle安裝教程測試辦法

1.先到Oracle官網下載Oracle12c(博主下的此版本) http://www.oracle.com/technetwork/cn/database/enterprise-edition/downloads/index.html 2.下載完成後解壓zip檔案得到如圖資料夾,點選set

RxJava 2.x 教程原始碼揭祕(一)入門理解基本操作符

目錄 前言 Rxjava的介紹 Rxjava的優勢 Rxjava是觀察者模式 Rxjava是裝飾者模式 Observable Rxjava的操作符 subScribeOn與observeOn切換執行緒 其他操作符 補充 前言   &nbs

Android非同步訊息處理機制詳解原始碼分析

PS一句:最終還是選擇CSDN來整理髮表這幾年的知識點,該文章平行遷移到CSDN。因為CSDN也支援MarkDown語法了,牛逼啊! 【工匠若水 http://blog.csdn.net/yanbober 轉載煩請註明出處,尊重分享成果】 最近相對來說比較閒,加上養病,所

mysql資料庫主從不一致場景分析如何避免

master庫寫redo、binlog不實時丟資料的場景 redo的ib_logfile與binlog日誌如果被設定非實時flush,就有可能存在丟資料的情況: redo未寫入磁碟,但binlog寫入磁碟,造成從庫資料量比主庫多。 redo寫入了磁碟,但是binlog未寫入,造成從庫資料量

Android RabbitMQ使用RabbitMQ安裝配置

Rabbit安裝 準備 Erlang: http://www.erlang.org/downloads Rabbit: http://www.rabbitmq.com/download.html Er

Spring監聽器ApplicationListener原理原始碼解析例項

一、原理及原始碼解析 事件:ContextRefreshedEvent、IOCTest_Ext$1[source=我釋出的事件]、ContextClosedEvent;  *  1)、ContextRefreshedEvent事件:  *      1)、容器建立物件:re

RxJava 2.x 教程原始碼揭祕(三)Rxjava操作符原始碼解析

本文將探究: 知道執行緒排程是怎麼實現的 知道操作符是怎麼實現的 RxJava最強大的莫過於它的執行緒排程 和 花式操作符。 map操作符 map是一個高頻的操作符,我們首先拿他開刀。 例子如下,源頭Observable傳送的是String型別的數字,利用map轉換成