GreenDao3 使用說明

GreenDao 3

一個將對象映射到 SQLite 數據庫中的輕量且快速的ORM解決方案

資料

Github
官網

android-database-sqlcipher Github
數據庫加密
GreenDaoUpgradeHelper Github
數據庫升級輔助

配置

導入

  • 配置項目的 build.gradle
buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2'
    }
}

// 使用數據庫升級輔助GreenDaoUpgradeHelper時添加
allprojects {
    repositories {
        maven { url "https://jitpack.io" }
    }
}
  • 配置模組的 build.gradle
apply plugin: 'org.greenrobot.greendao'

dependencies {
    compile 'org.greenrobot:greendao:3.2.2'

    // 使用數據庫加密時添加
    compile 'net.zetetic:android-database-sqlcipher:3.5.6'

    // 使用數據庫升級輔助GreenDaoUpgradeHelper時添加
    compile 'com.github.yuweiguocn:GreenDaoUpgradeHelper:v2.0.1'
}

參數

  • 設置 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
greendao {
    schemaVersion 1
    daoPackage 'com.example.greendaodemo.dao'
    targetGenDir 'src/main/java'
}

混淆

  • 配置模組的 proguard-rules.pro
### 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.**

基本用法

實體

@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設置自增,只能為 long/ Long 類型
    @Id(autoincrement = true)
    private Long id;

    // 唯一,默認索引。可另定義屬性唯一索引設為主鍵
    @Unique
    private String userId;

    // 列名,默認使用變量名。默認變化:userName --> USER_NAME
    @Property(nameInDb = "USERNAME")
    private String userName;

    // 索引,unique設置唯一,name設置索引別名
    @Index(unique = true)
    private long fk_dogId;

    // 非空
    @NotNull
    private String horseName;

    // 忽略,不持久化,可用關鍵字transient替代
    @Transient
    private int tempUsageCount;

    // 對一,實體屬性 joinProperty 對應外聯實體ID
    @ToOne(joinProperty = "fk_dogId")
    private Dog dog;

    // 對多。實體ID對應外聯實體屬性 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 對應實體ID;中間表屬性 targetProperty 對應外聯實體ID
    @ToMany
    @JoinEntity(
            entity = JoinUserWithSheep.class,
            sourceProperty = "uId",
            targetProperty = "sId"
    )
    private List<Sheep> sheep;
}
  • @Generated:greenDao生產代碼注解,手動修改報錯
  • @Keep:替換@Generated,greenDao不再生成,也不報錯。@Generated(無hash)也有相同的效果
  • @ToOne:joinProperty 和對象聯動,同時改變。對象懶加載,第一次請求后緩存
  • @ToMany:集合懶加載并緩存,之后獲取集合不查找數據庫,即集合數據不變。須手動修改集合,或調用reset方法清理集合

關系

  • User 和 Cat 配合產生一對多關系
@Entity
public class Cat {
    @Id
    private Long id;

    private String name;

    private long fk_userId;

    @ToOne(joinProperty = "fk_userId")
    private User user;
}
  • User、Sheep 和中間類 JoinUserWithSheep 配合產生多對多關系
@Entity
public class Sheep {
    @Id
    private Long id;

    private String name;

    @ToMany
    @JoinEntity(
            entity = JoinUserWithSheep.class,
            sourceProperty = "sId",
            targetProperty = "uId"
    )
    private List<User> users;
}

@Entity
public class JoinUserWithSheep {
    @Id
    private Long id;

    private Long uId;

    private Long sId;
}
  • 多個 TreeNode 配合產生樹狀關系
@Entity
public class TreeNode {
    @Id
    private Long id;

    private Long parentId;

    @ToOne(joinProperty = "parentId")
    private TreeNode parent;

    @ToMany(referencedJoinProperty = "parentId")
    private List<TreeNode> children;
}

初始化

// 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();

查詢日志

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)
void clear()  // 清理緩存域
Database getDatabase()
AbstractDao<?, ?> getDao(Class<? extends Object> entityClass)
Collection<AbstractDao<?, ?>> getAllDaos()
void runInTx(Runnable runnable)
<V> V callInTx(Callable<V> callable)
<V> V callInTxNoException(Callable<V> callable)

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()

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();
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 連接

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();
// 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

獲取查詢結果

// 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()  // 獲取結果數量

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");
// 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)

DeleteQuery 刪除查詢

DeleteQuery<T>  buildDelete()  // QueryBuilder

進階用法

聯合查詢

// 芝麻街住戶
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();
// 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[]StringDate
booleanintshortlongfloatdoublebyte
BooleanIntegerShortLongFloatDoubleByte

// 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;
        }
    }
}

升級

使用 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();

另有升級輔助庫 GreenDaoUpgradeHelper,通過 MigrationHelper 在刪表重建的過程中,使用臨時表保存數據并還原。

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());

高階用法

SQLCipher 加密

  • 使用getEncryptedReadableDb()getEncryptedWritableDb()獲取加密的數據庫
  • 256位AES加密,會提升APK的大小
  • Robolectric 測試時,須使用非加密數據庫
public static final boolean ENCRYPTED = true;

MySQLiteOpenHelper helper = new MySQLiteOpenHelper(this, ENCRYPTED ? "notes-encrypted.db" : "notes.db", null);
Database db = ENCRYPTED ? helper.getEncryptedWritableDb("<your-secret-password>") : helper.getWritableDb();
daoSession = new DaoMaster(db).newSession();

RxJava 支持

// DaoSession
RxTransaction   rxTx()
RxTransaction   rxTxPlain()

// Dao
RxDao<T,K>  rx()
RxDao<T,K>  rxPlain()

// QueryBuilder
RxQuery<T>  rx()
RxQuery<T>  rxPlain()
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容