GreenDao 介紹:
greenDAO是一個(gè)對象關(guān)系映射(ORM)的框架,能夠提供一個(gè)接口通過操作對象的方式去操作關(guān)系型數(shù)據(jù)庫,它能夠讓你操作數(shù)據(jù)庫時(shí)更簡單、更方便。如下圖所示:
官網(wǎng)地址:http://greenrobot.org/greendao/
github:https://github.com/greenrobot/greenDAO
GreenDao 優(yōu)點(diǎn):
性能高,號(hào)稱Android最快的關(guān)系型數(shù)據(jù)庫
內(nèi)存占用小
庫文件比較小,小于100K,編譯時(shí)間低,而且可以避免65K方法限制
支持?jǐn)?shù)據(jù)庫加密 greendao支持SQLCipher進(jìn)行數(shù)據(jù)庫加密 有關(guān)SQLCipher可以參考這篇博客 Android數(shù)據(jù)存儲(chǔ)之Sqlite采用SQLCipher數(shù)據(jù)庫加密實(shí)戰(zhàn)
簡潔易用的API
GreenDao 3.0改動(dòng):
使用過GreenDao的同學(xué)都知道,3.0之前需要通過新建GreenDaoGenerator工程生成Java數(shù)據(jù)對象(實(shí)體)和DAO對象,非常的繁瑣而且也加大了使用成本。
GreenDao 3.0最大的變化就是采用注解的方式通過編譯方式生成Java數(shù)據(jù)對象和DAO對象。
GreenDao 3.0使用方式:
1.)在build.gradle添加如下配置
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'org.greenrobot:greendao-gradle-plugin:3.0.0'
}
}
apply plugin: 'org.greenrobot.greendao'
dependencies {
compile 'org.greenrobot:greendao:3.0.1'
}
2.)新建實(shí)體
@Entity
public class User {
@Id
private long id;
private String name;
private int age;
//下面省去了 setter/getter
}
此時(shí)編譯一下自動(dòng)生成DaoMaster 、DaoSession、Dao,如圖所示 默認(rèn)位置:
3.) Gradle 插件配置
比如上面想指定生成DaoMaster 、DaoSession、Dao位置
greendao {
targetGenDir 'src/main/java'
}
schemaVersion : 數(shù)據(jù)庫schema版本,也可以理解為數(shù)據(jù)庫版本號(hào)
daoPackage :設(shè)置DaoMaster 、DaoSession、Dao包名
targetGenDir :設(shè)置DaoMaster 、DaoSession、Dao目錄
targetGenDirTest :設(shè)置生成單元測試目錄
generateTests :設(shè)置自動(dòng)生成單元測試用例
4.)實(shí)體 @Entity注解
schema :告知GreenDao當(dāng)前實(shí)體屬于哪個(gè)schema
active: 標(biāo)記一個(gè)實(shí)體處于活動(dòng)狀態(tài),活動(dòng)實(shí)體有更新、刪除和刷新方法
nameInDb: 在數(shù)據(jù)中使用的別名,默認(rèn)使用的是實(shí)體的類名
indexes: 定義索引,可以跨越多個(gè)列
createInDb: 標(biāo)記創(chuàng)建數(shù)據(jù)庫表
5.)基礎(chǔ)屬性注解
@Id : 主鍵 long/Long型,可以通過@Id(autoincrement = true)設(shè)置自增長
@Property: 設(shè)置一個(gè)非默認(rèn)關(guān)系映射所對應(yīng)的列名,默認(rèn)是的使用字段名 舉例:@Property (nameInDb="name")
@NotNul: 設(shè)置數(shù)據(jù)庫表當(dāng)前列不能為空
@Transient :添加次標(biāo)記之后不會(huì)生成數(shù)據(jù)庫表的列
6.)索引注解
@Index: 使用@Index作為一個(gè)屬性來創(chuàng)建一個(gè)索引,通過name設(shè)置索引別名,也可以通過unique給索引添加約束
@Unique: 向數(shù)據(jù)庫列添加了一個(gè)唯一的約束
7.)關(guān)系注解
@ToOne: 定義與另一個(gè)實(shí)體(一個(gè)實(shí)體對象)的關(guān)系
@ToMany: 定義與多個(gè)實(shí)體對象的關(guān)系
GreenDao 3.0簡單實(shí)戰(zhàn):
1.)通過上面使用方式我們可以獲取DaoMaster 、DaoSession、Dao類
這里聲明一個(gè)數(shù)據(jù)庫管理者單例
public class DBManager {
private final static String dbName = "test_db";
private static DBManager mInstance;
private DaoMaster.DevOpenHelper openHelper;
private Context context;
public DBManager(Context context) {
this.context = context;
openHelper = new DaoMaster.DevOpenHelper(context, dbName, null);
}
/**
* 獲取單例引用
*
* @param context
* @return
/
public static DBManager getInstance(Context context) {
if (mInstance == null) {
synchronized (DBManager.class) {
if (mInstance == null) {
mInstance = new DBManager(context);
}
}
}
return mInstance;
}
}
2.)獲取可讀可寫數(shù)據(jù)庫
可讀數(shù)據(jù)庫
/*
* 獲取可讀數(shù)據(jù)庫
/
private SQLiteDatabase getReadableDatabase() {
if (openHelper == null) {
openHelper = new DaoMaster.DevOpenHelper(context, dbName, null);
}
SQLiteDatabase db = openHelper.getReadableDatabase();
return db;
}
可寫數(shù)據(jù)庫
/*
* 獲取可寫數(shù)據(jù)庫
/
private SQLiteDatabase getWritableDatabase() {
if (openHelper == null) {
openHelper = new DaoMaster.DevOpenHelper(context, dbName, null);
}
SQLiteDatabase db = openHelper.getWritableDatabase();
return db;
}
3.)插入數(shù)據(jù)
/*
* 插入一條記錄
*
* @param user
/
public void insertUser(User user) {
DaoMaster daoMaster = new DaoMaster(getWritableDatabase());
DaoSession daoSession = daoMaster.newSession();
UserDao userDao = daoSession.getUserDao();
userDao.insert(user);
}
/*
* 插入用戶集合
*
* @param users
/
public void insertUserList(List users) {
if (users == null || users.isEmpty()) {
return;
}
DaoMaster daoMaster = new DaoMaster(getWritableDatabase());
DaoSession daoSession = daoMaster.newSession();
UserDao userDao = daoSession.getUserDao();
userDao.insertInTx(users);
}
4.)刪除數(shù)據(jù)
/*
* 刪除一條記錄
*
* @param user
/
public void deleteUser(User user) {
DaoMaster daoMaster = new DaoMaster(getWritableDatabase());
DaoSession daoSession = daoMaster.newSession();
UserDao userDao = daoSession.getUserDao();
userDao.delete(user);
}
5.)更新數(shù)據(jù)
/*
* 更新一條記錄
*
* @param user
/
public void updateUser(User user) {
DaoMaster daoMaster = new DaoMaster(getWritableDatabase());
DaoSession daoSession = daoMaster.newSession();
UserDao userDao = daoSession.getUserDao();
userDao.update(user);
}
6.)查詢數(shù)據(jù)
/*
* 查詢用戶列表
/
public List queryUserList() {
DaoMaster daoMaster = new DaoMaster(getReadableDatabase());
DaoSession daoSession = daoMaster.newSession();
UserDao userDao = daoSession.getUserDao();
QueryBuilder qb = userDao.queryBuilder();
List list = qb.list();
return list;
}
/*
* 查詢用戶列表
*/
public List queryUserList(int age) {
DaoMaster daoMaster = new DaoMaster(getReadableDatabase());
DaoSession daoSession = daoMaster.newSession();
UserDao userDao = daoSession.getUserDao();
QueryBuilder qb = userDao.queryBuilder();
qb.where(UserDao.Properties.Age.gt(age)).orderAsc(UserDao.Properties.Age);
List list = qb.list();
return list;
}
7.)測試程序
DBManager dbManager = DBManager.getInstance(this);
for (int i = 0; i < 5; i++) {
User user = new User();
user.setId(i);
user.setAge(i * 3);
user.setName("第" + i + "人");
dbManager.insertUser(user);
}
List userList = dbManager.queryUserList();
for (User user : userList) {
Log.e("TAG", "queryUserList--before-->" + user.getId() + "--" + user.getName() +"--"+user.getAge());
if (user.getId() == 0) {
dbManager.deleteUser(user);
}
if (user.getId() == 3) {
user.setAge(10);
dbManager.updateUser(user);
}
}
userList = dbManager.queryUserList();
for (User user : userList) {
Log.e("TAG", "queryUserList--after--->" + user.getId() + "---" + user.getName()+"--"+user.getAge());
}
-----------------------------------3.0-----------------
在 GreenDao 3.X之注解已經(jīng)了解到GreenDao 3.0的改動(dòng)及注解。對于數(shù)據(jù)庫的操作,無異于增刪改查等四個(gè)操作。下面我們將了解GreenDao 3.X如何使用?
AbstractDao
所有的自動(dòng)生成的XXDao都是繼承于AbstractDao,此類中基本上封裝了所有的增刪改操作,包括數(shù)據(jù)庫的事務(wù)操作。常用的API如下:
void attachEntity(T entity):
long count():獲取數(shù)據(jù)庫中數(shù)據(jù)的數(shù)量
// 數(shù)據(jù)刪除相關(guān)
void delete(T entity):從數(shù)據(jù)庫中刪除給定的實(shí)體
void deleteAll() :刪除數(shù)據(jù)庫中全部數(shù)據(jù)
void deleteByKey(K key):從數(shù)據(jù)庫中刪除給定Key所對應(yīng)的實(shí)體
void deleteByKeyInTx(java.lang.Iterable<K> keys):使用事務(wù)操作刪除數(shù)據(jù)庫中給定的所有key所對應(yīng)的實(shí)體
void deleteByKeyInTx(K... keys):使用事務(wù)操作刪除數(shù)據(jù)庫中給定的所有key所對應(yīng)的實(shí)體
void deleteInTx(java.lang.Iterable<T> entities):使用事務(wù)操作刪除數(shù)據(jù)庫中給定實(shí)體集合中的實(shí)體
void deleteInTx(T... entities):使用事務(wù)操作刪除數(shù)據(jù)庫中給定的實(shí)體
// 數(shù)據(jù)插入相關(guān)
long insert(T entity):將給定的實(shí)體插入數(shù)據(jù)庫
void insertInTx(java.lang.Iterable<T> entities):使用事務(wù)操作,將給定的實(shí)體集合插入數(shù)據(jù)庫
void insertInTx(java.lang.Iterable<T> entities, boolean setPrimaryKey):使用事務(wù)操作,將給定的實(shí)體集合插入數(shù)據(jù)庫,
并設(shè)置是否設(shè)定主鍵
void insertInTx(T... entities):將給定的實(shí)體插入數(shù)據(jù)庫
long insertOrReplace(T entity):將給定的實(shí)體插入數(shù)據(jù)庫,若此實(shí)體類存在,則覆蓋
void insertOrReplaceInTx(java.lang.Iterable<T> entities):使用事務(wù)操作,將給定的實(shí)體插入數(shù)據(jù)庫,若此實(shí)體類存在,則覆蓋
void insertOrReplaceInTx(java.lang.Iterable<T> entities, boolean setPrimaryKey):使用事務(wù)操作,將給定的實(shí)體插入數(shù)據(jù)庫,若此實(shí)體類存在,則覆蓋
并設(shè)置是否設(shè)定主鍵
void insertOrReplaceInTx(T... entities):使用事務(wù)操作,將給定的實(shí)體插入數(shù)據(jù)庫,若此實(shí)體類存在,則覆蓋
long insertWithoutSettingPk(T entity):將給定的實(shí)體插入數(shù)據(jù)庫,但不設(shè)定主鍵
// 新增數(shù)據(jù)插入相關(guān)API
void save(T entity):將給定的實(shí)體插入數(shù)據(jù)庫,若此實(shí)體類存在,則更新
void saveInTx(java.lang.Iterable<T> entities):將給定的實(shí)體插入數(shù)據(jù)庫,若此實(shí)體類存在,則更新
void saveInTx(T... entities):使用事務(wù)操作,將給定的實(shí)體插入數(shù)據(jù)庫,若此實(shí)體類存在,則更新
// 加載相關(guān)
T load(K key):加載給定主鍵的實(shí)體
java.util.List<T> loadAll():加載數(shù)據(jù)庫中所有的實(shí)體
protected java.util.List<T> loadAllAndCloseCursor(android.database.Cursor cursor) :從cursor中讀取、返回實(shí)體的列表,并關(guān)閉該cursor
protected java.util.List<T> loadAllFromCursor(android.database.Cursor cursor):從cursor中讀取、返回實(shí)體的列表
T loadByRowId(long rowId) :加載某一行并返回該行的實(shí)體
protected T loadUnique(android.database.Cursor cursor) :從cursor中讀取、返回唯一實(shí)體
protected T loadUniqueAndCloseCursor(android.database.Cursor cursor) :從cursor中讀取、返回唯一實(shí)體,并關(guān)閉該cursor
//更新數(shù)據(jù)
void update(T entity) :更新給定的實(shí)體
protected void updateInsideSynchronized(T entity, DatabaseStatement stmt, boolean lock)
protected void updateInsideSynchronized(T entity, android.database.sqlite.SQLiteStatement stmt, boolean lock)
void updateInTx(java.lang.Iterable<T> entities) :使用事務(wù)操作,更新給定的實(shí)體
void updateInTx(T... entities):使用事務(wù)操作,更新給定的實(shí)體
QueryBuilder、Query
基本查詢
GreenDao中,使用QueryBuilder自定義查詢實(shí)體,而不是再寫繁瑣的SQL語句,避免了SQL語句的出錯(cuò)率。大家都知道寫SQL語句時(shí),非常容易出錯(cuò),出錯(cuò)后又十分的難查。QueryBuilder真是幫忙解決了一個(gè)大麻煩。具體該如何使用呢?
List joes = userDao.queryBuilder()
// 查詢的條件
.where(Properties.FirstName.eq("Joe"))
// 返回實(shí)體集合升序排列
.orderAsc(Properties.LastName)
.list();
QueryBuilder qb = userDao.queryBuilder();
// 查詢的條件
qb.where(Properties.FirstName.eq("Joe"),
qb.or(Properties.YearOfBirth.gt(1970),
qb.and(Properties.YearOfBirth.eq(1970), Properties.MonthOfBirth.ge(10))));
List youngJoes = qb.list(); </span>
上面是官方給出的兩個(gè)列子,不僅滿足了查詢語句的易寫,同時(shí)使用了流式寫法,提高了代碼的可閱讀性。
Limit、Offset、Pagination
在實(shí)際開發(fā)過程中,大家肯定碰到這樣的問題,當(dāng)數(shù)據(jù)過多在一頁顯示不出來的時(shí)候,要么選擇前面十條顯示,要么分頁顯示,但是數(shù)據(jù)總是獲取全部的。其實(shí),剛接觸GreenDao的時(shí)候,也是這么干,獲取全部的實(shí)體集合,然后再根據(jù)實(shí)際情況截取。看了API以后,豁然開朗,大神們已經(jīng)幫我們解決了這件事。此時(shí)不得不說,QueryBuilder中的Limit(限制)、Offset(偏移),limit(int)和offset(int)協(xié)同設(shè)置,可以完美解決分頁顯示。
limit(int):限制查詢返回結(jié)果的數(shù)目
offset(int):設(shè)置查詢結(jié)果的偏移量,此查詢需與limit(int)結(jié)合使用,而不能夠脫離limit(int)單獨(dú)使用
Query
當(dāng)執(zhí)行多次查詢時(shí),實(shí)際是QueryBuilder多次調(diào)用Query類。如果執(zhí)行多次相同的查詢,應(yīng)使用QueryBuilder的build()方法來創(chuàng)建Query,而不是直接使用Query類。如果查詢返回的結(jié)果是唯一性的,可以使用操作符方法,如果不希望此唯一性不返回 null,此時(shí)可調(diào)用uniqOrThrow()方法。如果查詢返回的結(jié)果是多個(gè),可以使返回的結(jié)果是一個(gè)集合,有如下方法:
list():所有實(shí)體加載至內(nèi)存,結(jié)果通常是一個(gè)ArrayList
listLazy():實(shí)體在需要時(shí),加載至內(nèi)存,表中的第一個(gè)元素被第一次訪問時(shí)會(huì)被緩存,下次訪問時(shí),使用緩存
listLazyUncached():任何對列表實(shí)體的訪問懂事從數(shù)據(jù)庫中加載
listIterator():以按需加載的方式來遍歷結(jié)果,數(shù)據(jù)沒有被緩存
一旦使用QueryBuilder創(chuàng)建了一個(gè)query,那么這個(gè)Query對象就可以就可以被復(fù)用來執(zhí)行查詢顯然這種方式逼重新創(chuàng)建一次Query效率要高。
如果Query的參數(shù)沒有變更,你只需要再次調(diào)用List/unuque方法即可
如果參數(shù)發(fā)生了變化,那么就需要通過setParameter方法來處理每一個(gè)發(fā)生改變的參數(shù)
Query query = userDao.queryBuilder().where(Properties.FirstName.eq("Joe"), Properties.YearOfBirth.eq(1970)).build();
List joesOf1970 = query.list();
query.setParameter(0, "Maria");
query.setParameter(1, 1977);
List mariasOf1977 = query.list();
由此可見,Query在執(zhí)行一次build之后會(huì)將查詢結(jié)果進(jìn)行緩存,方便下次繼續(xù)使用。
執(zhí)行原生SQL語句
兩種方法:
Query query = userDao.queryBuilder().where(
new StringCondition("_ID IN " +
"(SELECT USER_ID FROM USER_MESSAGE WHERE READ_FLAG = 0)").build();
如果這里的QueryBuilder沒有提供你想要的特性,可以使用原始的queryRaw或queryRawCreate方法。
Query query = userDao.queryRawCreate( ", GROUP G WHERE G.NAME=? AND T.GROUP_ID=G._ID", "admin");
注:寫SQL語句時(shí)推薦定義常量來表示表名或者表項(xiàng),這樣可以防止出錯(cuò),因?yàn)榫幾g器會(huì)檢查
基本使用
創(chuàng)建實(shí)體類
@Entity(generateConstructors = false)
public class Student {
@Id
private Long id;
private String name;
private int age;
public Student() {
}
@Keep
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public Student(Long id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
@Keep
public Long getId() {
return id;
}
@Keep
public void setId(Long id) {
this.id = id;
}
@Keep
public String getName() {
return name;
}
@Keep
public void setName(String name) {
this.name = name;
}
@Keep
public int getAge() {
return age;
}
@Keep
public void setAge(int age) {
this.age = age;
}
@Keep
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Student)) return false;
Student student = (Student) o;
return name.equals(student.name);
}
@Keep
@Override
public int hashCode() {
return (int) (id ^ (id >>> 32));
}
@Keep
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
創(chuàng)建完實(shí)體類后,Rebuild Project生成DaoMaster、DaoSession。DaoMaster、DaoSession位于在Gradle設(shè)置的目錄及文件夾里。
[圖片上傳中。。。(21)]
[圖片上傳中。。。(22)]
創(chuàng)建Database管理類
public class DbManager {
// 是否加密
public static final boolean ENCRYPTED = true;
private static final String DB_NAME = "tea.db";
private static DbManager mDbManager;
private static DaoMaster.DevOpenHelper mDevOpenHelper;
private static DaoMaster mDaoMaster;
private static 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;
}
/**
* @desc 獲取可讀數(shù)據(jù)庫
**/
public static SQLiteDatabase getReadableDatabase(Context context) {
if (null == mDevOpenHelper) {
getInstance(context);
}
return mDevOpenHelper.getReadableDatabase();
}
/**
* @desc 獲取可寫數(shù)據(jù)庫
**/
public static SQLiteDatabase getWritableDatabase(Context context) {
if (null == mDevOpenHelper) {
getInstance(context);
}
return mDevOpenHelper.getWritableDatabase();
}
/**
* @desc 獲取DaoMaster
**/
public static DaoMaster getDaoMaster(Context context) {
if (null == mDaoMaster) {
synchronized (DbManager.class) {
if (null == mDaoMaster) {
mDaoMaster = new DaoMaster(getWritableDatabase(context));
}
}
}
return mDaoMaster;
}
/**
* @desc 獲取DaoSession
**/
public static DaoSession getDaoSession(Context context) {
if (null == mDaoSession) {
synchronized (DbManager.class) {
mDaoSession = getDaoMaster(context).newSession();
}
}
return mDaoSession;
}
}
數(shù)據(jù)庫操作類
public class StudentDaoOpe {
/**
* @desc 添加數(shù)據(jù)至數(shù)據(jù)庫
**/
public static void insertData(Context context, Student stu) {
DbManager.getDaoSession(context).getStudentDao().insert(stu);
}
/**
* @desc 將數(shù)據(jù)實(shí)體通過事務(wù)添加至數(shù)據(jù)庫
**/
public static void insertData(Context context, List<Student> list) {
if (null == list || list.size() <= 0) {
return;
}
DbManager.getDaoSession(context).getStudentDao().insertInTx(list);
}
@desc 添加數(shù)據(jù)至數(shù)據(jù)庫,如果存在,將原來的數(shù)據(jù)覆蓋
public static void saveData(Context context, Student student) {
DbManager.getDaoSession(context).getStudentDao().save(student);
}
/**
* @desc 查詢所有數(shù)據(jù)
**/
public static List<Student> queryAll(Context context) {
QueryBuilder<Student> builder = DbManager.getDaoSession(context).getStudentDao().queryBuilder();
return builder.build().list();
}