GreenDao詳解一

GreenDao是一個對象關(guān)系型框架,也就是我們常說的ORM框架,使用它可以通過操作對象的方式去操作數(shù)據(jù)庫绰沥。從而不用手寫sql代碼,簡化開發(fā)贺待。
本文針對GreenDao3.2.2版本進(jìn)行操作徽曲,為什么需要強(qiáng)調(diào)版本號? 因?yàn)镚reenDao2和GreenDao3有比較大的差別,3.0以后相比較之前的版本操作更加的方便麸塞。

添加依賴

1秃臣,在bulid.gradle文件下的dependencies下添加所需依賴

compile 'org.greenrobot:greendao-generator:3.2.2' 
compile 'org.greenrobot:greendao:3.2.2' // add library

2,在bulid.gradle下進(jìn)行配置

apply plugin: 'org.greenrobot.greendao'
greendao{
    schemaVersion 2 //升級greenDao版本號
    daoPackage '生成文件包名' // 一般為app包名+生成文件的文件夾名
    targetGenDir 'src/main/java' //生成的路徑
}

3,在project下的build.gradle配置

 dependencies {
        //其他省略
        classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2'
    }

上面的配置也就算完了奥此。

我們先明確一下需求:創(chuàng)建一個名為test.db的數(shù)據(jù)庫弧哎,而數(shù)據(jù)庫中有一張表user,需要使用greenDao對user表進(jìn)行CRUD操作稚虎,數(shù)據(jù)庫升級等常用的操作撤嫩。

創(chuàng)建實(shí)體類

先需要定義個User類并標(biāo)明@Entity注解和數(shù)據(jù)庫中的表進(jìn)行對應(yīng)

@Entity
public class User {
    @Id(autoincrement = true)
    private Long id;
    private String name;
    private int grade;
    @Transient//Transient表示該字段不參與序列化操作,不作為表中列名
    private int tempUsageCount;

   //…set(),get()方法省略

    public Integer age;
    @Generated(hash = 1097688115)
    public User(Long id, String name, int grade, Integer age) {
        this.id = id;
        this.name = name;
        this.grade = grade;
        this.age = age;
    }
    @Generated(hash = 586692638)
    public User() {
    }
}

需要注意的是@Id:通過這個注解標(biāo)記的字段必須是包裝類型Long類型蠢终,表示該字段為主鍵序攘,并且它默認(rèn)就是自增。否則在升級/降級數(shù)據(jù)庫的時候蜕径,會報錯两踏。

生成數(shù)據(jù)庫操作類

成功構(gòu)建工程之后,GreenDao會生成UserDao,DaoSession,DaoMaster三個文件兜喻。自動生成的Dao梦染,Session,Master官方是不建議直接修改這些文件的代碼朴皆,因?yàn)楫?dāng)數(shù)據(jù)庫需要升級/降級的時候使用生成器重新生成將會覆蓋之前修改過的代碼
為方便操作帕识,我們先封裝一個DBManager類:

public class DBManager {
    private static final String DB_NAME = "test.db";
    private static DBManager mDbManager;
    private static DaoMaster.DevOpenHelper mDevOpenHelper;
    private static volatile DaoMaster mDaoMaster;
    private static volatile DaoSession mDaoSession;
    private Context mContext;
    private DBManager(Context context) {
        this.mContext = context;
        // 初始化數(shù)據(jù)庫信息
        mDevOpenHelper = new DaoMaster.DevOpenHelper(context, DB_NAME);
        getDaoMaster(context);
        getDaoSession(context);
    }

    public static DBManager getInstance(Context context) {
        if (null == mDbManager) {
            synchronized (DBManager.class) {
                if (null == mDbManager) {
                    mDbManager = new DBManager(context);
                }
            }
        }
        return mDbManager;
    }

    /**
     * 獲取可讀數(shù)據(jù)庫
     *
     * @param context
     * @return
     */
    public static SQLiteDatabase getReadableDatabase(Context context) {
        if (null == mDevOpenHelper) {
            getInstance(context);
        }
        return mDevOpenHelper.getReadableDatabase();
    }

    /**
     * 獲取可寫數(shù)據(jù)庫
     *
     * @param context
     * @return
     */
    public static SQLiteDatabase getWritableDatabase(Context context) {
        if (null == mDevOpenHelper) {
            getInstance(context);
        }
        return mDevOpenHelper.getWritableDatabase();
    }

    /**
     * 獲取DaoMaster
     * <p>
     * 判斷是否存在數(shù)據(jù)庫,如果沒有則創(chuàng)建數(shù)據(jù)庫
     *
     * @param context
     * @return
     */
  public static DaoMaster getDaoMaster(Context context) {
        if (null == mDaoMaster) {
            synchronized (DbManager.class) {
                if (null == mDaoMaster) {
                    mDevOpenHelper = new DaoMaster.DevOpenHelper(context, DB_NAME);
                    mDaoMaster = new DaoMaster(mDevOpenHelper.getWritableDb());
                }
            }
        }
        return mDaoMaster;
    }

    /**
     * 獲取DaoSession
     *
     * @param context
     * @return
     */
    public static DaoSession getDaoSession(Context context) {
        if (null == mDaoSession) {
            synchronized (DBManager.class) {
                mDaoSession = getDaoMaster(context).newSession();
            }
        }
        return mDaoSession;
    }
}

對數(shù)據(jù)表的進(jìn)行操作

添加User表數(shù)據(jù)

/**
 * 向User表中添加一條記錄
 */
private void addData() {
    DaoMaster daoMaster = DBManager.getDaoMaster(mContext);
    DaoSession daoSession = daoMaster.newSession();
    UserDao userDao = daoSession.getUserDao();
    mUser = new User();
    mUser.setName("Hubery" + System.currentTimeMillis());
    mUser.setAge(15);
    userDao.insert(mUser);
}

/**
 * 更新一條記錄
 * @param user
 */
public void updateUser(User user) {
    DaoMaster daoMaster = DBManager.getDaoMaster(mContext);
    DaoSession daoSession = daoMaster.newSession();
    UserDao userDao = daoSession.getUserDao();
    userDao.update(user);
}

/**
 * 刪除一條記錄
 *
 * @param user
 */
public void deleteUser(User user) {
    DaoMaster daoMaster = DBManager.getDaoMaster(mContext);
    DaoSession daoSession = daoMaster.newSession();
    UserDao userDao = daoSession.getUserDao();
    userDao.delete(user);
}

/**
 * 查詢用戶列表
 */
public User queryUserModel(String curName) {
    DaoMaster daoMaster = DBManager.getDaoMaster(mContext);
    DaoSession daoSession = daoMaster.newSession();
    UserDao userDao = daoSession.getUserDao();
    QueryBuilder<User> qb = userDao.queryBuilder();
    qb.where(UserDao.Properties.Name.like("%" + curName + "%")).build();
    List<User> list = qb.list();
    if (list != null && list.size() > 0) {
        Toast.makeText(mContext, list.get(0).getName(), Toast.LENGTH_LONG).show();
        return list.get(0);
    } else {
        return null;
    }
}

/**
 * 查詢用戶列表
 */
public List<User> queryUserList() {
    DaoMaster daoMaster = DBManager.getDaoMaster(mContext);
    DaoSession daoSession = daoMaster.newSession();
    UserDao userDao = daoSession.getUserDao();
    QueryBuilder<User> qb = userDao.queryBuilder();
    List<User> list = qb.list();
    return list;
}

當(dāng)然GreenDao為我們封裝的對表的操作還不僅僅是這些遂铡,包括表一對多等復(fù)雜操作肮疗,感興趣的可以自行去查閱相關(guān)文檔。

數(shù)據(jù)庫中表升級/降級

說完GreenDao對數(shù)據(jù)表的基本操作之后扒接,再來看看數(shù)據(jù)庫的升級伪货,數(shù)據(jù)庫的升級需要繼承android.database.sqlite包中的SQLiteOpenHelper類,升級/降級的操作需要在onUpgrade()方法中執(zhí)行钾怔。

/**
 * 數(shù)據(jù)庫升級
 * Created by hubery on 2018/2/9.
 */
public class OpenHelper extends DaoMaster.OpenHelper {
    public OpenHelper(Context context, String name) {
        super(context, name);
    }

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

    @Override
    public void onUpgrade(Database db, int oldVersion, int newVersion) {
//判斷條件
        //操作數(shù)據(jù)庫的更新 有幾個表升級都可以傳入到下面
        MigrationHelper.getInstance().migrate(db,UserDao.class);
    }
}

當(dāng)然在這里我們只是做一個功能的演示碱呼,并沒有拿oldVersion和newVersion版本號去控制升級/降級。現(xiàn)在我們把升級/降級的操作都放到MigrationHelper中去宗侦,先來看看這個類:

**
 * Created by hubery on 2018/2/8.
 */
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(Database db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
        generateTempTables(db, daoClasses); 
        DaoMaster.dropAllTables(db, true);// 重新創(chuàng)建新的數(shù)據(jù)庫表
        DaoMaster.createAllTables(db, false);
        restoreData(db, daoClasses);
    }

    /**
     * 生成臨時列表
     *
     * @param db
     * @param daoClasses
     */
    private void generateTempTables(Database 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());

        }
    }

    /**
     * 存儲新的數(shù)據(jù)庫表 以及數(shù)據(jù)
     *
     * @param db
     * @param daoClasses
     */
    private void restoreData(Database 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 List<String> getColumns(Database 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;
    }
}
public void migrate(Database db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
generateTempTables(db, daoClasses); 
restoreData(db, daoClasses);
}

這里升級的步驟:先創(chuàng)建一張臨時表愚臀,把表中數(shù)據(jù)插入到臨時表中,然后新建一個新的表矾利,把臨時表的數(shù)據(jù)拷貝到新表里面去姑裂,最后刪除臨時表。

性能效率對比

因?yàn)镚reenDao框架對表進(jìn)行CRU操作的時候男旗,由于其本身底層使用的不是反射舶斧,而是編譯時期生成的代碼,而不是反射的機(jī)制察皇,所以相對比其他的ORM關(guān)系型數(shù)據(jù)庫而言捧毛,效率會高很多。
這是官網(wǎng)的一張對比截圖以及官網(wǎng)的一段描述:


GreenDao.png

所有的ORM關(guān)系型數(shù)據(jù)庫中,greendao是最快的呀忧。greendao不作任何妥協(xié)的性能师痕。數(shù)據(jù)庫非常適合存儲大量數(shù)據(jù),因此速度很快而账。使用greendao胰坟,大多數(shù)實(shí)體可以插入,更新泞辐,以每秒幾千單位率加載笔横。

另外提一下ObjectBox:ObjectBox是移動端數(shù)據(jù)庫框架,靈感來自于NoSql咐吼,速度非炒档蓿快,號稱是市面上最快的移動端數(shù)據(jù)庫框架锯茄。目前非關(guān)系行數(shù)據(jù)庫也就只有Realm 能與之相比厢塘。

本文主要講解了一下greendao的基本使用,包括引入greendao,greendao對于單表的CRUD操作肌幽,以及數(shù)據(jù)庫升級晚碾,性能對比等。

GreenDao詳解二

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末喂急,一起剝皮案震驚了整個濱河市格嘁,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌廊移,老刑警劉巖糕簿,帶你破解...
    沈念sama閱讀 211,639評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異狡孔,居然都是意外死亡冶伞,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評論 3 385
  • 文/潘曉璐 我一進(jìn)店門步氏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人徒爹,你說我怎么就攤上這事荚醒。” “怎么了隆嗅?”我有些...
    開封第一講書人閱讀 157,221評論 0 348
  • 文/不壞的土叔 我叫張陵界阁,是天一觀的道長。 經(jīng)常有香客問我胖喳,道長泡躯,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,474評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮较剃,結(jié)果婚禮上咕别,老公的妹妹穿的比我還像新娘。我一直安慰自己写穴,他們只是感情好惰拱,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,570評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著啊送,像睡著了一般偿短。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上馋没,一...
    開封第一講書人閱讀 49,816評論 1 290
  • 那天昔逗,我揣著相機(jī)與錄音,去河邊找鬼篷朵。 笑死勾怒,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的款票。 我是一名探鬼主播控硼,決...
    沈念sama閱讀 38,957評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼艾少!你這毒婦竟也來了卡乾?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,718評論 0 266
  • 序言:老撾萬榮一對情侶失蹤缚够,失蹤者是張志新(化名)和其女友劉穎幔妨,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體谍椅,經(jīng)...
    沈念sama閱讀 44,176評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡误堡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,511評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了雏吭。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片锁施。...
    茶點(diǎn)故事閱讀 38,646評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖杖们,靈堂內(nèi)的尸體忽然破棺而出悉抵,到底是詐尸還是另有隱情,我是刑警寧澤摘完,帶...
    沈念sama閱讀 34,322評論 4 330
  • 正文 年R本政府宣布姥饰,位于F島的核電站,受9級特大地震影響孝治,放射性物質(zhì)發(fā)生泄漏列粪。R本人自食惡果不足惜审磁,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,934評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望岂座。 院中可真熱鬧态蒂,春花似錦、人聲如沸掺逼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽吕喘。三九已至赘那,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間氯质,已是汗流浹背募舟。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留闻察,地道東北人拱礁。 一個月前我還...
    沈念sama閱讀 46,358評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像辕漂,于是被迫代替她去往敵國和親呢灶。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,514評論 2 348

推薦閱讀更多精彩內(nèi)容