GreenDao3 使用說明
GreenDao 3
一個將對象映射到 SQLite 數據庫中的輕量且快速的ORM解決方案
導入
- 在項目的 build.gradle 添加:
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'org.greenrobot:greendao-gradle-plugin:3.2.0'
}
}
// 使用數據庫升級輔助GreenDaoUpgradeHelper時添加
allprojects {
repositories {
...
maven { url "
在模組的 build.gradle 中添加:
apply plugin: 'org.greenrobot.greendao'
dependencies {
compile 'org.greenrobot:greendao:3.2.0'
// 數據庫加密時添加
compile 'net.zetetic:android-database-sqlcipher:3.5.1'
// 使用數據庫升級輔助GreenDaoUpgradeHelper時添加
compile 'com.github.yuweiguocn:GreenDaoUpgradeHelper:v1.2.0'
}</code></pre>
- 設置 Schema,在模組的 build.gradle 中添加:
- schemaVersion:數據庫schema版本號,通過 *OpenHelpers 遷移數據,schema改變值增加。默認為1
- daoPackage:生成DAOs、DaoMaster、DaoSession的包名。默認為entities所在包名。
- targetGenDir:生成DAOs、DaoMaster、DaoSession的目錄。默認為 build/generated/source/greendao
- generateTests: 設置 true 自動生成單元測試。
- targetGenDirTests: 設置生成單元測試目錄。默認為 src/androidTest/java
</li>
</ul>
greendao {
schemaVersion 1
daoPackage 'com.example.greendaodemo.dao'
targetGenDir 'src/main/java'
}
- 混淆
### greenDAO 3
-keepclassmembers class * extends org.greenrobot.greendao.AbstractDao {
public static java.lang.String TABLENAME;
}
-keep class **$Properties
If you do not use SQLCipher:
-dontwarn org.greenrobot.greendao.database.**
If you do not use RxJava:
-dontwarn rx.**</code></pre>
基本用法
實體
@Entity(
// schema 名,多個 schema 時設置關聯實體。插件產生不支持,需使用產生器
// schema = "myschema",
// 標記一個實體是否處于活動狀態,活動實體有 update、delete、refresh 方法。默認為 false
active = false,
// 表名,默認為類名
nameInDb = "AWESOME_USERS",
// 定義多列索引
indexes = {
@Index(value = "name DESC", unique = true)
},
// 標記是否創建表,默認 true。多實體對應一個表或者表已創建,不需要 greenDAO 創建時設置 false
createInDb = true,
// 是否產生所有參數構造器。默認為 true。無參構造器必定產生
generateConstructors = true,
// 如果沒有 get/set 方法,是否生成。默認為 true
generateGettersSetters = true
)
public class User {
// 主鍵,autoincrement設置自增
@Id(autoincrement = true)
private Long id;
// 唯一,默認索引
@Unique
private Long userId;
// 列名,默認使用變量名。變化:customName --> CUSTOM_NAME
@Property(nameInDb = "USERNAME")
private String name;
// 索引,unique設置唯一,name設置索引別名
@Index(unique = true)
private long fk_dogId;
// 非空
@NotNull
private String horseName;
// 忽略,不持久化,可用關鍵字transient替代
@Transient
private int tempUsageCount;
// 對一,實體屬性與外聯實體中的ID相對應。默認自動自動生成。fk和對象聯動,同時改變。對象懶加載
@ToOne(joinProperty = "fk_dogId")
private Dog dog;
// 對多,referencedJoinProperty 指定實體中與外聯實體屬性相對應的外鍵
@ToMany(referencedJoinProperty = "fk_userId")
private List<Cat> cats;
// 對多,@JoinProperty注解:name 實體中的屬性;referencedName 外聯實體中的屬性。
@ToMany(joinProperties = {
@JoinProperty(name = "horseName", referencedName = "name")
})
private List<Horse> horses;
// 對多,@JoinEntity注解:entity 中間表;sourceProperty 實體屬性;targetProperty 外鏈實體屬性
@ToMany
@JoinEntity(
entity = JoinSheepToUser.class,
sourceProperty = "uId",
targetProperty = "sId"
)
private List<Sheep> sheep;
}
@Entity
public class JoinSheepToUser {
@Id
private Long id;
private Long uId;
private Long sId;
}
@Entity
public class Sheep {
@Id
private Long id;
private String name;
}</code></pre>
- @Generated :greenDao生產代碼注解,手動修改報錯
- @Keep :替換@Generated,greenDao不再生成,也不報錯。@Generated(無hash)也有相同的效果
- @ToOne :fk 和對象聯動,同時改變。對象懶加載,第一次請求后立即獲取對象
- @ToMany :集合懶加載并緩存,之后獲取集合不查找數據庫,即集合數據不變。須手動修改集合,或調用reset方法清理集合
初始化
// Application 中執行
// DevOpenHelper 每次數據庫升級會清空數據,一般用于開發
DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(this, "notes-db", null);
Database db = helper.getWritableDb();
DaoSession daoSession = new DaoMaster(db).newSession();
// 在使用的地方獲取 DAO
NoteDao noteDao = daoSession.getNoteDao();</code></pre>
Dao 增加
long insert(T entity) // 插入指定實體
void insertInTx(T... entities)
void insertInTx(java.lang.Iterable<T> entities)
void insertInTx(java.lang.Iterable<T> entities, boolean setPrimaryKey)
long insertWithoutSettingPk(T entity) // 插入指定實體,無主鍵
long insertOrReplace(T entity) // 插入或替換指定實體
void insertOrReplaceInTx(T... entities)
void insertOrReplaceInTx(java.lang.Iterable<T> entities)
void insertOrReplaceInTx(java.lang.Iterable<T> entities, boolean setPrimaryKey)
void save(T entity) // 依賴指定的主鍵插入或修改實體
void saveInTx(T... entities)
void saveInTx(java.lang.Iterable<T> entities)
Dao 刪除
void deleteAll() // 刪除所有
void delete(T entity) // 刪除指定的實體
void deleteInTx(T... entities)
void deleteInTx(java.lang.Iterable<T> entities)
void deleteByKey(K key) // 刪除指定主鍵對應的實體
void deleteByKeyInTx(K... keys)
void deleteByKeyInTx(java.lang.Iterable<K> keys)
Dao 修改
void update(T entity)
void updateInTx(T... entities)
void updateInTx(java.lang.Iterable<T> entities)
Dao 其它
void refresh(T entity) // 從數據庫獲取值刷新本地實體
long count() // 數量
boolean detach(T entity) // 從域中分離實體
void detachAll() // 從域中分離所有實體
AbstractDaoSession getSession()
Database getDatabase()
java.lang.String getTablename()
java.lang.String[] getAllColumns()
java.lang.String[] getPkColumns()
java.lang.String[] getNonPkColumns()
Property getPkProperty()
Property[] getProperties()</code></pre>
Dao 查詢
java.util.List<T> loadAll()
T load(K key)
T loadByRowId(long rowId)
QueryBuilder 查詢
List joes = userDao.queryBuilder() // 查詢 User
.where(Properties.FirstName.eq("Joe")) // 首名為 Joe
.orderAsc(Properties.LastName) // 末名升序排列
.list(); // 返回集合
// Joe,>= 1970.10
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();</code></pre>
QueryBuilder<T> queryBuilder() // Dao
// QueryBuilder
QueryBuilder<T> where(WhereCondition cond, WhereCondition... condMore) // 條件,AND 連接
QueryBuilder<T> whereOr(WhereCondition cond1, WhereCondition cond2, WhereCondition... condMore) // 條件,OR 連接
QueryBuilder<T> distinct() // 去重,例如使用聯合查詢時
QueryBuilder<T> limit(int limit) // 限制返回數
QueryBuilder<T> offset(int offset) // 偏移結果起始位,配合limit(int)使用
QueryBuilder<T> orderAsc(Property... properties) // 排序,升序
QueryBuilder<T> orderDesc(Property... properties) // 排序,降序
QueryBuilder<T> orderCustom(Property property, java.lang.String customOrderForProperty) // 排序,自定義
QueryBuilder<T> orderRaw(java.lang.String rawOrder) // 排序,SQL 語句
QueryBuilder<T> preferLocalizedStringOrder() // 本地化字符串排序,用于加密數據庫無效
QueryBuilder<T> stringOrderCollation(java.lang.String stringOrderCollation) // 自定義字符串排序,默認不區分大小寫
WhereCondition and(WhereCondition cond1, WhereCondition cond2, WhereCondition... condMore) // 條件,AND 連接
WhereCondition or(WhereCondition cond1, WhereCondition cond2, WhereCondition... condMore) // 條件,OR 連接</code></pre>
Query 重復查詢
// Joe,1970
Query query = userDao.queryBuilder().where(
Properties.FirstName.eq("Joe"), Properties.YearOfBirth.eq(1970)
).build();
List joesOf1970 = query.list();
// Maria,1977
query.setParameter(0, "Maria");
query.setParameter(1, 1977);
List mariasOf1977 = query.list();</code></pre>
// QueryBuilder
Query<T> build()
CursorQuery buildCursor()
CountQuery<T> buildCount()
DeleteQuery<T> buildDelete()
// Query
// 設置查詢參數,從 0 開始
Query<T> setParameter(int index, java.lang.Object parameter)
Query<T> setParameter(int index, java.lang.Boolean parameter)
Query<T> setParameter(int index, java.util.Date parameter)
void setLimit(int limit) // 限制返回數
void setOffset(int offset) // 偏移結果起始位,配合limit(int)使用
// Query 綁定線程,執行非本線程的 Query 拋異常,調用獲取本線程 Query
Query<T> forCurrentThread() // 獲取本線程 Query</code></pre>
獲取查詢結果
// QueryBuilder、Query
T unique() // 返回唯一結果或者 null
T uniqueOrThrow() // 返回唯一非空結果,如果 null 則拋異常
java.util.List<T> list() // 返回結果集進內存
// 懶加載,須在 try/finally 代碼中關閉。
LazyList<T> listLazy() // 第一次使用返回結果集,所有數據使用后會自動關閉
LazyList<T> listLazyUncached() // 返回虛擬結果集,數據庫讀取不緩存
CloseableListIterator<T> listIterator() // 懶加載數據迭代器,不緩存,所有數據使用后會自動關閉
// QueryBuilder、CountQuery
long count() // 獲取結果數量</code></pre>
SQL 查詢
// QueryBuilder.where() 配合 WhereCondition.StringCondition() 實現SQL查詢
Query query = userDao.queryBuilder()
.where(new WhereCondition.StringCondition("_ID IN (SELECT USER_ID FROM USER_MESSAGE WHERE READ_FLAG = 0)"))
.build();
// Dao.queryRawCreate() 實現SQL查詢
Query query = userDao.queryRawCreate( ", GROUP G WHERE G.NAME=? AND T.GROUP_ID=G._ID", "admin");</code></pre>
// Dao
java.util.List<T> queryRaw(java.lang.String where, java.lang.String... selectionArg)
Query<T> queryRawCreate(java.lang.String where, java.lang.Object... selectionArg)
Query<T> queryRawCreateListArgs(java.lang.String where, java.util.Collection<java.lang.Object> selectionArg)
// WhereCondition.PropertyCondition
PropertyCondition(Property property, java.lang.String op)
PropertyCondition(Property property, java.lang.String op, java.lang.Object value)
PropertyCondition(Property property, java.lang.String op, java.lang.Object[] values)
// WhereCondition.StringCondition
StringCondition(java.lang.String string)
StringCondition(java.lang.String string, java.lang.Object value)
StringCondition(java.lang.String string, java.lang.Object... values)</code></pre>
DeleteQuery 刪除查詢
DeleteQuery<T> buildDelete() // QueryBuilder
查詢日志
QueryBuilder.LOG_SQL = true;
QueryBuilder.LOG_VALUES = true;
DaoSession 增刪改查
// DaoSession 的方法轉換成 Dao 的對應方法執行
<T> long insert(T entity)
<T> long insertOrReplace(T entity)
<T> void delete(T entity)
<T> void deleteAll(java.lang.Class<T> entityClass)
<T> void update(T entity)
<T,K> T load(java.lang.Class<T> entityClass, K key)
<T,K> java.util.List<T> loadAll(java.lang.Class<T> entityClass)
<T> QueryBuilder<T> queryBuilder(java.lang.Class<T> entityClass)
<T,K> java.util.List<T> queryRaw(java.lang.Class<T> entityClass, java.lang.String where, java.lang.String... selectionArgs)
<T> void refresh(T entity)
進階用法
聯合查詢
// 芝麻街住戶
QueryBuilder<User> queryBuilder = userDao.queryBuilder();
queryBuilder.join(Address.class, AddressDao.Properties.userId)
.where(AddressDao.Properties.Street.eq("Sesame Street"));
List<User> users = queryBuilder.list();?
// 歐洲超過百萬人口的城市
QueryBuilder qb = cityDao.queryBuilder().where(Properties.Population.ge(1000000));
Join country = qb.join(Properties.CountryId, Country.class);
Join continent = qb.join(country, CountryDao.Properties.ContinentId,
Continent.class, ContinentDao.Properties.Id);
continent.where(ContinentDao.Properties.Name.eq("Europe"));
List<City> bigEuropeanCities = qb.list();
// 爺爺叫林肯的人
QueryBuilder qb = personDao.queryBuilder();
Join father = qb.join(Person.class, Properties.FatherId);
Join grandfather = qb.join(father, Properties.FatherId, Person.class, Properties.Id);
grandfather.where(Properties.Name.eq("Lincoln"));
List<Person> lincolnDescendants = qb.list();</code></pre>
// QueryBuilder,聯合查詢
<J> Join<T,J> join(java.lang.Class<J> destinationEntityClass, Property destinationProperty)
<J> Join<T,J> join(Property sourceProperty, java.lang.Class<J> destinationEntityClass)
<J> Join<T,J> join(Property sourceProperty, java.lang.Class<J> destinationEntityClass, Property destinationProperty)
<J> Join<T,J> join(Join<?,T> sourceJoin, Property sourceProperty, java.lang.Class<J> destinationEntityClass, Property destinationProperty)
自定義類型
默認支持類型: byte[] 、 String 、 Date 、
boolean 、 int 、 short 、 long 、 float 、 double 、 byte 、
Boolean 、 Integer 、 Short 、 Long 、 Float 、 Double 、 Byte
// enum 轉換為 Integer
@Entity
public class User {
@Id
private Long id;
@Convert(converter = RoleConverter.class, columnType = Integer.class)
private Role role;
public enum Role {
DEFAULT(0), AUTHOR(1), ADMIN(2);
final int id; // 使用穩定的 id 來轉換,不要使用不穩定的名字和順序
Role(int id) {
this.id = id;
}
}
public static class RoleConverter implements PropertyConverter<Role, Integer> {
@Override
public Role convertToEntityProperty(Integer databaseValue) {
if (databaseValue == null) {
return null;
}
for (Role role : Role.values()) {
if (role.id == databaseValue) {
return role;
}
}
return Role.DEFAULT; // 準備一個默認值,防止數據移除時崩潰
}
@Override
public Integer convertToDatabaseValue(Role entityProperty) {
// 判斷返回 null
return entityProperty == null ? null : entityProperty.id;
}
}
}</code></pre>
升級
使用 DevOpenHelper 每次升級數據庫,表會刪除重建,推薦開發使用。實際使用中建立類繼承 DaoMaster.OpenHelper,實現 onUpgrade()
public class MySQLiteOpenHelper extends DaoMaster.OpenHelper {
public MySQLiteOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory) {
super(context, name, factory);
}
@Override
public void onUpgrade(Database db, int oldVersion, int newVersion) {
if (oldVersion == newVersion) {
Log.d("onUpgrade", "數據庫是最新版本" + oldVersion + ",不需要升級");
return;
}
Log.d("onUpgrade", "數據庫從版本" + oldVersion + "升級到版本" + newVersion);
switch (oldVersion) {
case 1:
String sql = "";
db.execSQL(sql);
case 2:
default:
break;
}
}
}
// 初始化使用 MySQLiteOpenHelper
MySQLiteOpenHelper helper = new MySQLiteOpenHelper(this, "notes-db", null);
Database db = helper.getWritableDb();
DaoSession daoSession = new DaoMaster(db).newSession();</code></pre>
另有升級輔助庫 GreenDaoUpgradeHelper,通過 MigrationHelper 在刪表重建的過程中,使用臨時表保存數據并還原。
注意: MigrationHelper.migrate() ,暫時只接收 SQLiteDatabase ,不接收 Database,影響加密的使用。可修改源碼支持。
public class MySQLiteOpenHelper extends DaoMaster.OpenHelper {
public MySQLiteOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory) {
super(context, name, factory);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
MigrationHelper.migrate(db,TestDataDao.class,TestData2Dao.class,TestData3Dao.class);
}
}
// 初始化
MigrationHelper.DEBUG = true; //如果你想查看日志信息,請將DEBUG設置為true
MySQLiteOpenHelper helper = new MySQLiteOpenHelper(this, "test.db", null);
DaoMaster daoMaster = new DaoMaster(helper.getWritableDatabase());</code></pre>
高階用法
SQLCipher 加密
- 使用 getEncryptedReadableDb() 和 getEncryptedWritableDb() 獲取加密的數據庫
- 256位AES加密,會提升APK的大小
- Robolectric 測試時,須使用非加密數據庫
DevOpenHelper helper = new DevOpenHelper(this, "notes-db-encrypted.db");
Database db = helper.getEncryptedWritableDb("<your-secret-password>");
DaoSession daoSession = new DaoMaster(db).newSession();
RxJava
// DaoSession
RxTransaction rxTx() // 返回 IO 線程下的 Observables
RxTransaction rxTxPlain() // 返回無線程調度的 Observables
// Dao
RxDao<T,K> rx()
RxDao<T,K> rxPlain()
// QueryBuilder
RxQuery<T> rx()
RxQuery<T> rxPlain()</code></pre>
來自:http://www.jianshu.com/p/4e6d72e7f57a