GreenDao3 使用说明

izdv7003 7年前
   <h2><strong>GreenDao 3</strong></h2>    <p>一个将对象映射到 SQLite 数据库中的轻量且快速的ORM解决方案</p>    <h2><strong>导入</strong></h2>    <ul>     <li>在项目的 build.gradle 添加:</li>    </ul>    <pre>  <code class="language-java">buildscript {      repositories {          mavenCentral()      }      dependencies {          classpath 'org.greenrobot:greendao-gradle-plugin:3.2.0'      }  }    // 使用数据库升级辅助GreenDaoUpgradeHelper时添加  allprojects {          repositories {                  ...                  maven { url "https://jitpack.io" }          }  }</code></pre>    <ul>     <li>在模组的 build.gradle 中添加:</li>    </ul>    <pre>  <code class="language-java">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>    <ul>     <li>设置 Schema,在模组的 build.gradle 中添加:      <ul>       <li>schemaVersion:数据库schema版本号,通过 *OpenHelpers 迁移数据,schema改变值增加。默认为1</li>       <li>daoPackage:生成DAOs、DaoMaster、DaoSession的包名。默认为entities所在包名。</li>       <li>targetGenDir:生成DAOs、DaoMaster、DaoSession的目录。默认为 build/generated/source/greendao</li>       <li>generateTests: 设置 true 自动生成单元测试。</li>       <li>targetGenDirTests: 设置生成单元测试目录。默认为 src/androidTest/java</li>      </ul> </li>    </ul>    <pre>  <code class="language-java">greendao {      schemaVersion 1      daoPackage 'com.example.greendaodemo.dao'      targetGenDir 'src/main/java'  }</code></pre>    <ul>     <li>混淆</li>    </ul>    <pre>  <code class="language-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>    <h2><strong>基本用法</strong></h2>    <h3><strong>实体</strong></h3>    <pre>  <code class="language-java">@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>    <ul>     <li>@Generated :greenDao生产代码注解,手动修改报错</li>     <li>@Keep :替换@Generated,greenDao不再生成,也不报错。@Generated(无hash)也有相同的效果</li>     <li>@ToOne :fk 和对象联动,同时改变。对象懒加载,第一次请求后立即获取对象</li>     <li>@ToMany :集合懒加载并缓存,之后获取集合不查找数据库,即集合数据不变。须手动修改集合,或调用reset方法清理集合</li>    </ul>    <h3><strong>初始化</strong></h3>    <pre>  <code class="language-java">// 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>    <h3><strong>Dao 增加</strong></h3>    <pre>  <code class="language-java">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)</code></pre>    <h3><strong>Dao 删除</strong></h3>    <pre>  <code class="language-java">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)</code></pre>    <h3><strong>Dao 修改</strong></h3>    <pre>  <code class="language-java">void     update(T entity)  void     updateInTx(T... entities)  void     updateInTx(java.lang.Iterable<T> entities)</code></pre>    <h3><strong>Dao 其它</strong></h3>    <pre>  <code class="language-java">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>    <h3><strong>Dao 查询</strong></h3>    <pre>  <code class="language-java">java.util.List<T>     loadAll()  T     load(K key)  T     loadByRowId(long rowId)</code></pre>    <h3><strong>QueryBuilder 查询</strong></h3>    <pre>  <code class="language-java">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>    <pre>  <code class="language-java">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>    <h3><strong>Query 重复查询</strong></h3>    <pre>  <code class="language-java">// 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>    <pre>  <code class="language-java">// 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>    <h3><strong>获取查询结果</strong></h3>    <pre>  <code class="language-java">// 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>    <h3><strong>SQL 查询</strong></h3>    <pre>  <code class="language-java">// 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>    <pre>  <code class="language-java">// 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>    <h3><strong>DeleteQuery 删除查询</strong></h3>    <pre>  <code class="language-java">DeleteQuery<T>     buildDelete()  // QueryBuilder</code></pre>    <h3><strong>查询日志</strong></h3>    <pre>  <code class="language-java">QueryBuilder.LOG_SQL = true;  QueryBuilder.LOG_VALUES = true;</code></pre>    <h3><strong>DaoSession 增删改查</strong></h3>    <pre>  <code class="language-java">// 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)</code></pre>    <h2><strong>进阶用法</strong></h2>    <h3><strong>联合查询</strong></h3>    <pre>  <code class="language-java">// 芝麻街住户  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>    <pre>  <code class="language-java">// 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)</code></pre>    <h3><strong>自定义类型</strong></h3>    <p>默认支持类型: byte[] 、 String 、 Date 、</p>    <p>boolean 、 int 、 short 、 long 、 float 、 double 、 byte 、</p>    <p>Boolean 、 Integer 、 Short 、 Long 、 Float 、 Double 、 Byte</p>    <pre>  <code class="language-java">// 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>    <h3><strong>升级</strong></h3>    <p>使用 DevOpenHelper 每次升级数据库,表会删除重建,推荐开发使用。实际使用中建立类继承 DaoMaster.OpenHelper,实现 onUpgrade()</p>    <pre>  <code class="language-java">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>    <p>另有升级辅助库 GreenDaoUpgradeHelper,通过 MigrationHelper 在删表重建的过程中,使用临时表保存数据并还原。</p>    <p>注意: MigrationHelper.migrate() ,暂时只接收 SQLiteDatabase ,不接收 Database,影响加密的使用。可修改源码支持。</p>    <pre>  <code class="language-java">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>    <h2><strong>高阶用法</strong></h2>    <h3><strong>SQLCipher 加密</strong></h3>    <ul>     <li>使用 getEncryptedReadableDb() 和 getEncryptedWritableDb() 获取加密的数据库</li>     <li>256位AES加密,会提升APK的大小</li>     <li>Robolectric 测试时,须使用非加密数据库</li>    </ul>    <pre>  <code class="language-java">DevOpenHelper helper = new DevOpenHelper(this, "notes-db-encrypted.db");  Database db = helper.getEncryptedWritableDb("<your-secret-password>");  DaoSession daoSession = new DaoMaster(db).newSession();</code></pre>    <h3><strong>RxJava</strong></h3>    <pre>  <code class="language-java">// 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>    <p> </p>    <p>来自:http://www.jianshu.com/p/4e6d72e7f57a</p>    <p> </p>