GreenDao —— 簡單快速操作 Android SQLite 數(shù)據(jù)庫

GreenDao —— 簡單快速操作 Android SQLite 數(shù)據(jù)庫

GreenDao 是輕量快速的 SQLite 數(shù)據(jù)庫 ORM 解決方案。(greenDAO is a light & fast ORM solution that maps objects to SQLite databases.

GreenDao 層次

ORM(Object-Relationl Mapping)用于在關(guān)系型數(shù)據(jù)庫與對象之間做一個映射??梢允箶?shù)據(jù)庫操作想對象一樣使用,而避開使用復(fù)雜的SQL語句交互。

GreenDao 特點:

  • 性能強大。(可能是 Android 平臺最快的 ORM 框架)
  • 簡易便捷的 API
  • 開銷小
  • 依賴體積小
  • 支持?jǐn)?shù)據(jù)庫加密
  • 強大的社區(qū)支持

此前接觸 Android 的 SQLite 數(shù)據(jù)庫操作,有感于直接使用 SQLite 繁瑣且低效,使用 Android 官方的 Room 也感覺效果不佳。最后選擇 GreenDao 總算滿足預(yù)期。

GreenDao 環(huán)境配置

1. Project 下的 build.gradle 增加插件支持


buildscript {
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.2.1'
        classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2' // 版本建議最新
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

2. app 下的 build.gradle 增加插件依賴


apply plugin: 'com.android.application'
apply plugin: 'org.greenrobot.greendao'

android {
    ...

    // greendao 配置
    greendao {
        schemaVersion 1                                         // 數(shù)據(jù)庫版本號
        daoPackage      'org.cvte.research.faceapi.greendao'    // greenDao 自動生成的代碼保存的包名
        targetGenDir    'src/main/java'                         // 自動生成的代碼存儲的路徑,默認(rèn)是 build/generated/source/greendao.
    }

    ...
}

dependencies {
    ...

    // GreenDao  數(shù)據(jù)庫ORM
    implementation 'org.greenrobot:greendao:3.2.2'
    // GreenDao 生成dao和model的generator的項目 發(fā)布時可以去掉
    implementation 'org.greenrobot:greendao-generator:3.2.2'
}

接下來可以使用 GreenDao 對數(shù)據(jù)庫對象進(jìn)行操作了。

創(chuàng)建 GreenDao 數(shù)據(jù)庫對象實體

1. 創(chuàng)建數(shù)據(jù)庫實體

創(chuàng)建實體需要了解 GreenDao 的注解:

注解 描述 其它參數(shù)
Entity 對應(yīng)數(shù)據(jù)庫中的表 nameInDb:表使用別名。默認(rèn)為類名
active:標(biāo)記一個實體是否處于活動狀態(tài),活動實體有 update、delete、refresh 方法。默認(rèn)為 false
indexes:定義多列索引
Id 該數(shù)據(jù)庫表的主鍵,只能是 Long 或 long 類型 autoincrement:設(shè)置是否自增,可以通過傳入 null 自動分配
Unique 唯一??梢酝ㄟ^設(shè)置唯一的屬性設(shè)為主鍵
Property 列名 nameInDb:列使用別名。默認(rèn)為變量名
Index 索引 unique:設(shè)置唯一
name:設(shè)置索引的別名
NotNull 非空。該字段值不能為空
Transient 忽略。greendao 將不會創(chuàng)建對應(yīng)的項
ToOne 表格映射關(guān)系一對一 joinProperty:外聯(lián)實體與該實體主鍵的匹配成員
ToMany 表格映射關(guān)系一對多或多對多
Generated greendao 產(chǎn)生的部分,手動修改會報錯
Keep 替換 Generated,greendao不再生成和報錯
Convert 數(shù)據(jù)類型轉(zhuǎn)換。實體類型與數(shù)據(jù)庫類型轉(zhuǎn)換,實現(xiàn)存儲和修改的便捷 converter:轉(zhuǎn)換方法
columnType:數(shù)據(jù)庫使用的數(shù)據(jù)類型

文件名為:UserBean.java


@Entity(nameInDb = "user_table")
public class UserBean {
    @Id(autoincrement = true)
    @Unique
    @Property(nameInDb = "user_id")
    private Long userId;

    @NotNull
    @Property(nameInDb = "group_id")
    private Long groupId;

    @NotNull
    @Property(nameInDb = "user_name")
    private String userName;

    @Unique
    @NotNull
    @Property(nameInDb = "user_number")
    private String userNumber;
}

另外增加一個 GroupBean.java 實體,內(nèi)容如下:


@Entity(nameInDb = "group_table")
public class GroupBean {
    @Id(autoincrement = true)
    @Unique
    @Property(nameInDb = "group_id")
    private Long groupId;

    @Unique
    @NotNull
    @Property(nameInDb = "group_name")
    private String groupName;
}

2. 編譯后,生成完善的數(shù)據(jù)庫實體方法

編譯后可以看到 UserBean.java 文件增加了不少接口。


@Entity(nameInDb = "user_table")
public class UserBean {
    @Id(autoincrement = true)
    @Unique
    @Property(nameInDb = "user_id")
    private Long userId;

    @NotNull
    @Property(nameInDb = "group_id")
    private Long groupId;

    @NotNull
    @Property(nameInDb = "user_name")
    private String userName;

    @Unique
    @NotNull
    @Property(nameInDb = "user_number")
    private String userNumber;

    @Generated(hash = 1853997691)
    public UserBean(Long userId, @NotNull Long groupId, @NotNull String userName,
            @NotNull String userNumber) {
        this.userId = userId;
        this.groupId = groupId;
        this.userName = userName;
        this.userNumber = userNumber;
    }

    @Generated(hash = 1203313951)
    public UserBean() {
    }

    public Long getUserId() {
        return this.userId;
    }

    public void setUserId(Long userId) {
        this.userId = userId;
    }

    public Long getGroupId() {
        return this.groupId;
    }

    public void setGroupId(Long groupId) {
        this.groupId = groupId;
    }

    public String getUserName() {
        return this.userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getUserNumber() {
        return this.userNumber;
    }

    public void setUserNumber(String userNumber) {
        this.userNumber = userNumber;
    }
}

自動生成的方法:

  1. 無參構(gòu)造函數(shù)
  2. 有參構(gòu)造函數(shù)
  3. getter / setter 方法

除此之外,還生成了 DaoMaster,DaoSession,UserBeanDao (GroupBean 對應(yīng)生成 GroupBeanDao ) 等文件。
下面來介紹各自的功能。

3. DaoMaster,DaoSession,Dao 文件

文件 描述 相應(yīng)文件
DaoMaster 保存數(shù)據(jù)庫對象(SQLiteDatabase) DaoMaster
DaoSession 管理所有的 Dao 對象 DaoSession
Dao 數(shù)據(jù)訪問對象(Data Access Object),可以通過 Dao 操作數(shù)據(jù)實體 UserBeanDao、GroupBeanDao
Entity 數(shù)據(jù)實體(每個實體對應(yīng)數(shù)據(jù)庫內(nèi)的一個表) UserBean、GroupBean

通過 GreenDao 操作數(shù)據(jù)庫。

1. 初始化數(shù)據(jù)庫


private static DaoSession mDaoSession;

public initDatabase(Context context, String databaseFileName) {
    DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(context, databaseFileName, null);
    SQLiteDatabase db = helper.getWritableDatabase();
    DaoMaster daoMaster = new DaoMaster(db);
    mDaoSession = daoMaster.newSession();

    // 打開查詢的LOG
    // QueryBuilder.LOG_SQL = true;
    // QueryBuilder.LOG_VALUES = true;
}

public static DaoSession getDaoSession() {
    return mDaoSession;
}

執(zhí)行 initDatabase 初始化數(shù)據(jù)庫后,可以通過 getDaoSession 拿到 DaoSession 對每個數(shù)據(jù)庫實體進(jìn)行處理。

2. 插入數(shù)據(jù)

以以下數(shù)據(jù)為例:

組織(GroupName) 名字(UserName) 編號(UserNumber)
CHN LiTianYu 1-0090
USA LiBill 10-8082
XYZ MiXue 2-720

public void insertData(String groupName, String userName, String userNumber) {
    GroupBean groupBean = new GroupBean(null, groupName);                       // Group 主鍵(groupId)是自動增加的,使用 null 就可以自增了。
    long keyGroupBean = getDaoSession().getGroupBeanDao().insert(groupBean);    // 返回值是插入庫后的 Group 實體的 key
    groupBean = getDaoSession().getGroupBeanDao().load(keyGroupBean);           // 通過 key 可以獲取到插入的 Group 數(shù)據(jù)
    
    UserBean userBean = new UserBean(null, groupBean.getGroupId(), userName, userNumber);  // User 主鍵(userId)也是自動增加的,但是 groupId 需要通過關(guān)聯(lián)的 Group 獲取。
    getDaoSession().getUserBeanDao().insert(userBean);
}

3. 刪除數(shù)據(jù)


// 刪除單個數(shù)據(jù)
getDaoSession().getUserBeanDao().delete(userBean);

// 刪除多個數(shù)據(jù) (userBeanList 類型為 List<UserBean>)
getDaoSession().getUserBeanDao().deleteInTx(userBeanList);

// 刪除所有數(shù)據(jù)
getDaoSession().getUserBeanDao().deleteAll();

4. 查找數(shù)據(jù)


// 查詢 userNumber 為 "1-0090" 的 user
getDaoSession().getUserBeanDao().queryBuilder().where(UserBeanDao.Properties.UserNumber.eq("1-0090")).unique();

// 查詢 userName 形如 "Lixxx" 的 user ( 類似于 SQLite 的 like 模糊查詢語法 )
getDaoSession().getUserBeanDao().queryBuilder().where(UserBeanDao.Properties.UserNumber.eq("Li%")).unique();

// 查詢 groupName 為 "CHN" 的 user ( 關(guān)聯(lián)表查詢 )
QueryBuilder<UserBean> qb = getDaoSession().getUserBeanDao().queryBuilder();    // 需要獲取 User 數(shù)據(jù),所以 qb 為 User
Join join_UserBean = qb.join(UserBean.class, UserBeanDao.Properties.UserId);    // 設(shè)置 User 的關(guān)聯(lián)規(guī)則(根據(jù) UserBean.userId == UserBean.userId)
Join join_GroupBean = qb.join(join_UserBean, UserBeanDao.Properties.GroupId, GroupBean.class, GroupBeanDao.Properties.GroupId);     // 設(shè)置 Group 的關(guān)聯(lián)規(guī)則(根據(jù) UserBean.groupId == GroupBean.groupId)
join_GroupBean.where(GroupBeanDao.Properties.GroupName.eq("CHN"));      // 其它查找條件(User和Group已經(jīng)關(guān)聯(lián)起來了)
qb.list();      // 返回查找結(jié)果

使用 .unique() 為獲取查詢滿足要求的第一個數(shù)據(jù)。
使用 .list() 為獲取所有滿足要求的數(shù)據(jù)(返回結(jié)果為 List<> 類型)

多表關(guān)聯(lián)查詢稍顯復(fù)雜,可以通過 ToOne、ToMany 設(shè)置表與表之間的關(guān)系進(jìn)行直接訪問的查詢。
但是大數(shù)據(jù)量時效率沒有使用以上方法快。

5. 更改數(shù)據(jù)


// 更改單個數(shù)據(jù)
getDaoSession().getUserBeanDao().update(userBean);

// 更改多個數(shù)據(jù) (userBeanList 類型為 List<UserBean>)
getDaoSession().getUserBeanDao().updateInTx(userBeanList);

其它

1. 類型轉(zhuǎn)換

SQLite 數(shù)據(jù)庫的數(shù)據(jù)類型有限(甚至不支持float),而作為對象則允許所有java的類型(數(shù)組、各種類等)。
因此在 GreenDao 中支持類型轉(zhuǎn)換(從數(shù)據(jù)庫數(shù)據(jù)類型轉(zhuǎn)換為實體的數(shù)據(jù)類型),方便對實體進(jìn)行修改查詢。

請在對應(yīng)的成員中加入 @Convert ,如:


// 以實體的 float[] 與 數(shù)據(jù)庫的 TEXT 類型轉(zhuǎn)換為例
@Convert(converter = ConvertFloatArrayToString.class, columnType = String.class)
private float[] featureData;

轉(zhuǎn)換方法:


public class ConvertFloatArrayToString implements PropertyConverter<float[], String> {
    // 數(shù)據(jù)庫類型 -> 實體類型
    public float[] convertToEntityProperty(String databaseValue) {
        String[] strList = databaseValue.split(",");
        float[] floatList = new float[strList.length];
        for (int i = 0, len = strList.length; i < len; ++ i) {
            floatList[i] = Float.parseFloat(strList[i]);
        }
        return floatList;
    }

    // 實體類型 -> 數(shù)據(jù)庫類型
    public String convertToDatabaseValue(float[] entityProperty) {
        String str = "" + entityProperty[0];
        for (int i = 1, len = entityProperty.length; i < len; ++ i) {
            str += "," + entityProperty[i];
        }
        return str;
    }
}

這樣就可以讀取直接操作 float[] 進(jìn)行讀寫,不需要每次都手動轉(zhuǎn)為 TEXT(對應(yīng) Java 中的 String)或者解析 TEXT 了。

2. 其它的其它

比如加密,比如緩存,比如懶加載。。。有空再補吧

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容