GreenDao使用

簡(jiǎn)介

GreenDao是一個(gè)簡(jiǎn)化數(shù)據(jù)庫開發(fā)的ORM(Object Ralativational Mapping)來(對(duì)象關(guān)系映射)源框架垃喊,就是通過操作對(duì)象間接操作數(shù)據(jù)庫方式。

GreenDao的使用說明
  • gradle 配置中參數(shù)說明

    1. schemaVersion:指定數(shù)據(jù)庫schema版本號(hào)初家,遷移等操作會(huì)用到陌知。
    2. targetGenDir:生成數(shù)據(jù)庫文件的目錄仆葡。
    3. daoPackage:dao的包名志笼,包名默認(rèn)是entity所在的包纫溃。
  • 實(shí)體類中注解說明

    1. @Entity:表明這個(gè)實(shí)體類會(huì)在數(shù)據(jù)庫中生成一個(gè)與之相對(duì)應(yīng)的表紊浩,其中
      nameInDb:可以自定義表名坊谁,表明該實(shí)體對(duì)應(yīng)數(shù)據(jù)庫中的那張表呜袁,默認(rèn)為實(shí)體類名阶界。
      indexes:定義索引膘融,這里可跨越多個(gè)列氧映。
      createInDb:如果是有多個(gè)實(shí)體都關(guān)聯(lián)這個(gè)表岛都,可以把多余的實(shí)體里面設(shè)置為false避免重復(fù)創(chuàng)建(默認(rèn)是true)臼疫。
      schema:一個(gè)項(xiàng)目中有多個(gè)schema時(shí),表明要讓這個(gè)dao屬于哪個(gè)schema荣赶。
      active:是否應(yīng)該生成更新/刪除/刷新方法。如果Entity定義了 @ToOne 或 @ToMany關(guān)系利诺,那么獨(dú)立于該值是有效的立轧。意為是否支持實(shí)體類之間update氛改,refresh胜卤,delete等操作葛躏。

    2. @Id:對(duì)應(yīng)數(shù)據(jù)表中的主鍵舰攒,是一條數(shù)據(jù)的唯一標(biāo)識(shí)摩窃。如果實(shí)體沒有聲明主鍵猾愿,默認(rèn)創(chuàng)建Long類型主鍵"_id"自增蒂秘。使用Long類型主鍵時(shí)可以通過@Id(autoincrement = true)設(shè)置為自增姻僧。

    3. @Property(nameInDb = "USER_NAME" ):可以自定義字段名撇贺,注意外鍵不能使用該屬性显熏。表明這個(gè)屬性對(duì)應(yīng)數(shù)據(jù)表中的 USER_NAME 字段晒屎。

    4. @NotNull:該屬性值不能為空鼓鲁。

    5. @Transient:該屬性不會(huì)被存入數(shù)據(jù)庫中骇吭。

    6. @Unique:表明該屬性在數(shù)據(jù)庫中只能有唯一值燥狰。

    7. @Index:創(chuàng)建一個(gè)索引龙致。通過name設(shè)置索引別名目代,也可以通過unique給索引添加約束榛了。

    8. @Convert:指定一個(gè)PropertyConverter用于支持自定義類型(沒用過)霜大。

    9. @ToOne:定義自己與一個(gè)實(shí)體對(duì)象的關(guān)系战坤。

    10. @ToMany:定義自己與多個(gè)實(shí)體對(duì)象的關(guān)系(可不與@ToOne聯(lián)合使用)湖笨。@ToMany的屬性referencedJoinProperty慈省,類似于外鍵約束边败。

    11. @JoinProperty:對(duì)于更復(fù)雜的關(guān)系笑窜,可以使用這個(gè)注解標(biāo)明目標(biāo)屬性的源屬性排截,起關(guān)聯(lián)作用。

    12. @JoinEntity:如果你在做多對(duì)多的關(guān)系脱吱,有其他的表或?qū)嶓w參與箱蝠,可以給目標(biāo)屬性添加這個(gè)額外的注解宦搬。

    13. @OrderBy:指定{@ToMany}關(guān)系的相關(guān)集合的排序间校,(propertyA, propertyB)默認(rèn)為按主鍵ASC排序撇簿。

    14. @Generated:這個(gè)是build后greendao自動(dòng)生成的四瘫,這個(gè)注解理解為防止重復(fù)找蜜,每一塊代碼生成后會(huì)加個(gè)hash作為標(biāo)記洗做。

表關(guān)聯(lián)(一對(duì)一诚纸,一對(duì)多)舉例

現(xiàn)有兩張表:作者表和文章表畦徘,文章表中有一列authorId對(duì)應(yīng)作者表的id井辆。希望可以通過文章的id查詢作者信息杯缺。

一對(duì)一:

@Entity(nameInDb = "CsYdd",createInDb = true,indexes = {@Index(value = "id",unique = true)}) //value值必須是主鍵
public class Article {

  @Id(autoincrement = true) 
  public long id; //id是主鍵

  @SerializedName("is_FORCEEEE") //json解析換名
  @Property(nameInDb = "is_FORCEEEE2") //db中換名
  public boolean isFirst;

  private long authId; //該authId就是Author的主鍵id萍肆,注意類型一定要相同,這里都為long類型
  @ToOne(joinProperty = "authId")  
  private Author bean;
}

一對(duì)多:

@Entity(nameInDb = "CsYEE",createInDb = true,indexes = {@Index(value = "id",unique = true)})
public class Author {

  @Id
  public long id;

  @ToMany(referencedJoinProperty = "authId") //authId是Article中與Author關(guān)聯(lián)的參數(shù)名
  private List<Article> list;

}

代碼調(diào)用:
    ArticleDao dao = daoSession.getArticleDao();
    Article load = dao.load(id);
    Author bean = load.getBean();


    AuthorDao dao = daoSession.getArticleDao();
    Author load = dao.load(id);
    List<Article> list = load.getList();

多對(duì)多:(例如:老師與學(xué)生關(guān)系)

@Entity public class Teacher {

  @Id(autoincrement = true) private Long id;
  private String name;
  // 對(duì)多碉纳,@JoinEntity注解:entity 中間表;sourceProperty 實(shí)體屬性奴愉;targetProperty 外鏈實(shí)體屬性
  @ToMany
  @JoinEntity(entity = JoinStudentToTeacher.class, sourceProperty = "tid", targetProperty = "sid")
  private List<Student> students;
}

@Entity public class Student {

  @Id private Long id;
  private String name;

  //對(duì)多锭硼,@JoinEntity注解:entity 中間表檀头;sourceProperty 實(shí)體屬性暑始;targetProperty 外鏈實(shí)體屬性
  @ToMany
  @JoinEntity(entity = JoinStudentToTeacher.class, sourceProperty = "sid", targetProperty = "tid")
  private List<Teacher> teachers;
}

//借助的中間關(guān)系網(wǎng)
@Entity public class JoinStudentToTeacher {
  @Id(autoincrement = true) private Long id;
  //和teacher關(guān)聯(lián)的id
  private Long tid;
  //和student關(guān)聯(lián)的id
  private Long sid;
}

greenDao方式測(cè)試

  private void cs_toMany_toMany() {
    TeacherDao TeacherDao = MyApplication.getDaoSession().getTeacherDao();
    StudentDao studentDao = MyApplication.getDaoSession().getStudentDao();
    JoinStudentToTeacherDao spDao = MyApplication.getDaoSession().getJoinStudentToTeacherDao();

    Teacher p1 = new Teacher();
    p1.setId(1L);
    p1.setName("teacherOne");
    Teacher p2 = new Teacher();
    p2.setId(2L);
    p2.setName("teacherTwo");
    Teacher p3 = new Teacher();
    p3.setId(3L);
    p3.setName("teacherThree");

    Student stu1 = new Student();
    stu1.setId(1L);
    stu1.setName("stu1");
    Student stu2 = new Student();
    stu2.setId(2L);
    stu2.setName("stu2");
    Student stu3 = new Student();
    stu3.setId(3L);
    stu3.setName("stu3");
    
    // 模擬 多對(duì)多關(guān)系牙肝,建立關(guān)系網(wǎng)
    //p1有stu1 stu2 stu3 配椭,那么反過來stu123都有p1
    JoinStudentToTeacher sp1 = new JoinStudentToTeacher();
    sp1.setTid(1L);
    sp1.setSid(1l);
    JoinStudentToTeacher sp2 = new JoinStudentToTeacher();
    sp2.setTid(1L);
    sp2.setSid(2L);
    JoinStudentToTeacher sp3 = new JoinStudentToTeacher();
    sp3.setTid(1L);
    sp3.setSid(3L);

    //p2有stu1 stu2 stu3 股缸,那么反過來stu123都有p2
    JoinStudentToTeacher sp4 = new JoinStudentToTeacher();
    sp4.setTid(2L);
    sp4.setSid(1L);
    JoinStudentToTeacher sp5 = new JoinStudentToTeacher();
    sp5.setTid(2L);
    sp5.setSid(2L);
    JoinStudentToTeacher sp6 = new JoinStudentToTeacher();
    sp6.setTid(2L);
    sp6.setSid(3L);

    TeacherDao.insertOrReplace(p1); //也可用insertOrReplaceInTx()方式直接插入List
    TeacherDao.insertOrReplace(p2);
    TeacherDao.insertOrReplace(p3);

    studentDao.insertOrReplace(stu1);
    studentDao.insertOrReplace(stu2);
    studentDao.insertOrReplace(stu3);

    spDao.insertOrReplace(sp1);
    spDao.insertOrReplace(sp2);
    spDao.insertOrReplace(sp3);
    spDao.insertOrReplace(sp4);
    spDao.insertOrReplace(sp5);
    spDao.insertOrReplace(sp6);

    List<Teacher> Teachers = TeacherDao.queryBuilder().build().list();

    for (Teacher Teacher : Teachers) {
      Log.d("GreenDao","Teacher:"+Teacher.toString());
    }
  }

容易忽略的:
1.如果是list或者數(shù)組類型的屬性XX,只有g(shù)etXX方法坎背,沒有setXX方法得滤,但可用GreenDao的insertOrReplaceInTx()方法設(shè)置懂更。
2.如果你要打印 @ToOne對(duì)應(yīng)的bean類時(shí),要先調(diào)用getXXX方法龄捡,而不是直接打印對(duì)象(因?yàn)闆]有賦值聘殖,而是在getXX的時(shí)候才賦值的)
3.以上三種方法奸腺,刪除時(shí)候都不會(huì)關(guān)聯(lián)刪除突照。

GreenDao的其他方法

1 inser() 插入一條數(shù)據(jù)
2 insertInTx() 批量插入數(shù)據(jù)讹蘑,比如List
3 insertOrReplace() 插入數(shù)據(jù)座慰,傳入的對(duì)象主鍵如果存在于數(shù)據(jù)庫中角骤,有則更新,否則插入
4 insertOrReplaceInTx() 批量插入數(shù)據(jù)背桐,同上
5 save()比insertOrReplace()更有效率链峭,但其會(huì)先判斷對(duì)象是否有Key值弊仪,有則采用update方式更新(若在數(shù)據(jù)庫中無任何數(shù)據(jù)励饵,則此數(shù)據(jù)不會(huì)保存到數(shù)據(jù)庫)役听,無則采用insert方式插入典予。
6 insertWithoutSettingPk() 不需要設(shè)置主鍵屬性瘤袖,因?yàn)閮H對(duì)主鍵為L(zhǎng)ong類型自增有效捂敌,String類型主鍵會(huì)Error黍匾。
7 deleteAll() 刪除相應(yīng)表的全部數(shù)據(jù)(清空相應(yīng)表)呛梆。
8 deleteByKey() 刪除給定PK的實(shí)體填物。
9 deleteByKeyInTx(K... keys) 使用事務(wù)刪除數(shù)據(jù)庫中給定鍵的所有實(shí)體滞磺。批量刪除击困。
10 delete(T entity) entity的id屬性必須有值,否則報(bào)錯(cuò)阅茶。就是刪除與entity主鍵值相同的數(shù)據(jù)脸哀,其他數(shù)據(jù)值與表中數(shù)據(jù)值不一致時(shí)撞蜂,會(huì)刪除蝌诡。
11 deleteInTx(T... entities) 使用事務(wù)刪除數(shù)據(jù)庫中給定的實(shí)體浦旱。批量刪除。每個(gè)entity的id屬性必須有值尼酿,否則報(bào)錯(cuò)裳擎。
12 update(T entity) entity的id屬性必須有值思币,否則報(bào)錯(cuò)谷饿。若entity的id值表中不存在時(shí)博投,不會(huì)報(bào)錯(cuò)毅哗。
13 updateInTx(T... entities) 使用事務(wù)更新數(shù)據(jù)庫中給定的實(shí)體虑绵。批量更新翅睛。
14 load(K key) 加載給定PK的實(shí)體捕发。傳入:key or null。返回:該實(shí)體 或 null充石。
15 loadByRowId(long rowId) 加載給定RowId的實(shí)體骤铃。傳入:key or null惰爬。返回:該實(shí)體 或 null撕瞧。
16 loadAll() 從數(shù)據(jù)庫加載所有可用的實(shí)體。
17 unique() 返回非空的結(jié)果巩掺,否則拋出異常胖替。
18 uniqueOrThrow() 返回非空的結(jié)果独令,否則拋出異常燃箭。
19 queryBuilder()用法示例如下:

    mCustomerBeanDao.queryBuilder()
        .limit(3) //取前3條數(shù)據(jù)
        .offset(2) //偏移量(必須與limit()配合使用)
        .distinct() //去重
        .orderDesc(CustomerBeanDao.Properties.Adcode) //降序---descending
        .orderAsc(CustomerBeanDao.Properties.District) //升序---ascending
        .orderCustom(CustomerBeanDao.Properties.Adcode,"") //偏好排序---custom---定制
        .orderRaw("") //使用原始sql排序
        .preferLocalizedStringOrder() //使用本地化排序
        .stringOrderCollation("COLLATE NOCASE") //orderAsc和orderAsc自定義排序
        .where(CustomerBeanDao.Properties.CustName.like("dfsfs%")) //查詢條件招狸,參數(shù)間關(guān)系為 and
        .whereOr(CustomerBeanDao.Properties.AreaName.like("dsfsf%") //查詢條件瓢颅,參數(shù)間關(guān)系為 or
            ,CustomerBeanDao.Properties.Address.eq("")) //n個(gè)查詢條件
        .build()
        .list();

查詢條件有:

    between(Object value1, Object value2): BETWEEN … AND …
    eq(Object value): equal (‘=’)
    notEq(Object value): not equal (‘<>’)
    gt(Object value): than (‘>’)  ---- greater than
    lt(Object value): less than (‘<’) ---less than
    ge(Object value): greater or equal (‘>=’)
    le(Object value): less or equal (‘<=’)
    like(String value): LIKE
    isNotNull(): IS NOT NULL
    isNull(): IS NULL
    in(Object… inValues): IN (…, …, …)
    notIn(Object… notInValues): NOT IN (…, …, …)
    in(Collection< ?> inValues): IN (…, …, …)
    notIn(Collection< ?> notInValues): NOT IN (…, …, …)

queryBuilder.or()用法,借用一個(gè)網(wǎng)上的例子說明:
獲取1970年10月或之后出生的名為“Tom”的用戶信息(對(duì)應(yīng)的sql:where name = "Tom" and ((year = 1970 and month >=10) or (year >1970)))

QueryBuilder<User> qb = userDao.queryBuilder();
//要特別注意qb.or(,qb.and())中嵌套的層級(jí)結(jié)構(gòu)
qb.where(Properties.FirstName.eq("Tom"),qb.or(Properties.YearOfBirth.gt(1970),qb.and(Properties.YearOfBirth.eq(1970),Properties.MonthOfBirth.ge(10)))); 
List<User> youngJoes = qb.list();

20 or\and(WhereCondition... condMore) 在where或whereOr中使用木人,condMore參數(shù)間關(guān)系為or\and
21 list() 執(zhí)行查詢并將返回一個(gè)包含所有entity的list為結(jié)果醒第,直接加載在內(nèi)存中(存在緩存問題)稠曼。
22 listLazy() 當(dāng)真正用到數(shù)據(jù)時(shí)(訪問entity的屬性時(shí))霞幅,才會(huì)查詢數(shù)據(jù)庫司恳。
23 listLazyUncached() 也實(shí)現(xiàn)了懶加載技術(shù)扔傅,但是返回結(jié)果list沒有保存在內(nèi)存中,沒法復(fù)用试读,每次都會(huì)向數(shù)據(jù)庫查詢真正的數(shù)據(jù)鹏往。
24 listIterator() 執(zhí)行查詢并將結(jié)果作為列表迭代器返回伊履;確保關(guān)閉它以關(guān)閉底層游標(biāo)唐瀑。一旦迭代器完全迭代哄辣,游標(biāo)就會(huì)關(guān)閉力穗。
25 queryRawCreate() 基于給定的原始SQL創(chuàng)建一個(gè)可重復(fù)的{查詢}對(duì)象气嫁,可以在這里傳遞任何WHERE子句和參數(shù)寸宵。
26 queryRaw() 一個(gè)原始樣式查詢梯影,可以在這里傳遞任何WHERE子句和參數(shù)甲棍。
27 queryRawCreateListArgs() 基于給定的原始SQL創(chuàng)建一個(gè)可重復(fù)的{查詢}對(duì)象感猛,您可以在這里傳遞任何WHERE子句和參數(shù)唱遭。
28 多線程查詢:
29 rx():返回在IO線程發(fā)射事件的 Observables
30 rxPlain():返回沒有設(shè)置無線程調(diào)度的 Observables
31 detach() 將一個(gè)實(shí)體從會(huì)話中拆分拷泽。后續(xù)查詢結(jié)果不會(huì)返回此對(duì)象袖瞻。
32 detachAll() 所有的 同上
33 refresh() 通過從數(shù)據(jù)庫重新加載重置該實(shí)體的所有本地更改屬性聋迎。
34 daoSession.clear() 清除daoSession的緩存
35 dao.detachAll() 清除指定dao類的緩存
36 QueryBuilder.LOG_SQL = true; //打印SQL標(biāo)志
37 QueryBuilder.LOG_VALUES = true; //打印參數(shù)標(biāo)志

執(zhí)行原始SQL查詢舉例:(目的:根據(jù)一個(gè)表的數(shù)據(jù)查另一個(gè)表中數(shù)據(jù))
方法一霉晕,被拼接在where之后添加查詢條件:

Query<User> query = userDao.queryBuilder().where(new StringCondition("_ID in " +"(SELECT USER_ID FROM USER_MESSAGE WHERE READ_FLAG = 0)")).build();

方法二牺堰,被拼接在查詢語句的SELECT和實(shí)體之后:

Query<User> query = userDao.queryRawCreate(", Table2 G WHERE G.NAME=? AND T.GROUP_ID=G._ID", "admin"); //注意:實(shí)體User默認(rèn)別名為 T伟葫,Table2前邊的","是表間的分隔符

方法三筏养,Database方式:

Cursor cursor = daoSession().getDatabase().rawQuery(sql,selectionArgs);

數(shù)據(jù)庫升級(jí)

Android數(shù)據(jù)庫升級(jí)涉及到的問題:
1. 若不升級(jí)渐溶,是不能增加新表弄抬,也不能修改或增加舊表字段的眉睹,除非卸載APP重裝(即刪除數(shù)據(jù)庫并重建,但丟數(shù)據(jù)),否則會(huì)crash斋配;
2. 升級(jí)版本號(hào)艰争,觸發(fā)onUpgrade()方法桂对,可手動(dòng)進(jìn)行新舊表的增蕉斜、刪、改等操作机错;
3. 實(shí)現(xiàn)數(shù)據(jù)庫升級(jí)方案:a.采用增加新表,同時(shí)修改或增減舊表字段青瀑;b.舊表改名為臨時(shí)表斥难,新建表蘸炸,導(dǎo)入數(shù)據(jù)搭儒,如上方式;

public final class MigrationHelper {
 
  public static void migrate(SQLiteDatabase sqliteDatabase, Class<? extends AbstractDao<?, ?>>... daoClasses) {
    StandardDatabase db = new StandardDatabase(sqliteDatabase);
    generateNewTablesIfNotExists(db, daoClasses);
    generateTempTables(db, daoClasses);
    dropAllTables(db, true, daoClasses);
    createAllTables(db, false, daoClasses);
    restoreData(db, daoClasses);
  }
 
  public static void migrate(StandardDatabase db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
    generateNewTablesIfNotExists(db, daoClasses);
    generateTempTables(db, daoClasses);
    dropAllTables(db, true, daoClasses);
    createAllTables(db, false, daoClasses);
    restoreData(db, daoClasses);
  }
 
  private static void generateNewTablesIfNotExists(StandardDatabase db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
    reflectMethod(db, "createTable", true, daoClasses);
  }
 
  private static void generateTempTables(StandardDatabase db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
    for (int i = 0; i < daoClasses.length; i++) {
      DaoConfig daoConfig = new DaoConfig(db, daoClasses[i]);
      String tableName = daoConfig.tablename;
      String tempTableName = daoConfig.tablename.concat("_TEMP");
      StringBuilder insertTableStringBuilder = new StringBuilder();
      insertTableStringBuilder.append("CREATE TEMP TABLE ").append(tempTableName);
      insertTableStringBuilder.append(" AS SELECT * FROM ").append(tableName).append(";");
      db.execSQL(insertTableStringBuilder.toString());
    }
  }
 
  private static void dropAllTables(StandardDatabase db, boolean ifExists, @NonNull Class<? extends AbstractDao<?, ?>>... daoClasses) {
    reflectMethod(db, "dropTable", ifExists, daoClasses);
  }
 
  private static void createAllTables(StandardDatabase db, boolean ifNotExists, @NonNull Class<? extends AbstractDao<?, ?>>... daoClasses) {
    reflectMethod(db, "createTable", ifNotExists, daoClasses);
  }
 
  /**
   * dao class already define the sql exec method, so just invoke it
   */
  private static void reflectMethod(StandardDatabase db, String methodName, boolean isExists, @NonNull Class<? extends AbstractDao<?, ?>>... daoClasses) {
    if (daoClasses.length < 1) {
      return;
    }
    try {
      for (Class cls : daoClasses) {
        Method method = cls.getDeclaredMethod(methodName, Database.class, boolean.class);
        method.invoke(null, db, isExists);
      }
    } catch (NoSuchMethodException e) {
      e.printStackTrace();
    } catch (InvocationTargetException e) {
      e.printStackTrace();
    } catch (IllegalAccessException e) {
      e.printStackTrace();
    }
  }
 
  private static void restoreData(StandardDatabase db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
    for (int i = 0; i < daoClasses.length; i++) {
      DaoConfig daoConfig = new DaoConfig(db, daoClasses[i]);
      String tableName = daoConfig.tablename;
      String tempTableName = daoConfig.tablename.concat("_TEMP");
      // get all columns from tempTable, take careful to use the columns list
      List<String> columns = getColumns(db, tempTableName);
      ArrayList<String> properties = new ArrayList<>(columns.size());
      for (int j = 0; j < daoConfig.properties.length; j++) {
        String columnName = daoConfig.properties[j].columnName;
        if (columns.contains(columnName)) {
          properties.add(columnName);
        }
      }
      if (properties.size() > 0) {
        final String columnSQL = TextUtils.join(",", properties);
 
        StringBuilder insertTableStringBuilder = new StringBuilder();
        insertTableStringBuilder.append("INSERT INTO ").append(tableName).append(" (");
        insertTableStringBuilder.append(columnSQL);
        insertTableStringBuilder.append(") SELECT ");
        insertTableStringBuilder.append(columnSQL);
        insertTableStringBuilder.append(" FROM ").append(tempTableName).append(";");
        db.execSQL(insertTableStringBuilder.toString());
      }
      StringBuilder dropTableStringBuilder = new StringBuilder();
      dropTableStringBuilder.append("DROP TABLE ").append(tempTableName);
      db.execSQL(dropTableStringBuilder.toString());
    }
  }
 
  private static List<String> getColumns(StandardDatabase db, String tableName) {
    List<String> columns = null;
    Cursor cursor = null;
    try {
      cursor = db.rawQuery("SELECT * FROM " + tableName + " limit 0", null);
      if (null != cursor && cursor.getColumnCount() > 0) {
        columns = Arrays.asList(cursor.getColumnNames());
      }
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      if (cursor != null)
        cursor.close();
      if (null == columns)
        columns = new ArrayList<>();
    }
    return columns;
  }
}

然后在初始化時(shí)用到的 UpgradeHelper 類的onUpgrade方法中添加:

public class UpgradeHelper extends DaoMaster.OpenHelper {
  public UpgradeHelper(Context context, String name, SQLiteDatabase.CursorFactory factory) {
    super(context, name, factory);
  }
 
  @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    MigrationHelper.migrate(db, PortraitPhotoDao.class, FeatureDao.class, PhotoJoinUserDao.class,
        UserPhotoDao.class);
  }
}

升級(jí)方案參考于:https://blog.csdn.net/zhanlv/article/details/82425709

SQL中常用命令

對(duì)表結(jié)構(gòu)操作命令:

  • 修改表名:
    rename table 舊表名 to 新表名

  • 修改表選項(xiàng):
    alter table 表名 表選項(xiàng) [=] 新值

  • 新增字段:
    alter table 表名 add [column] 新字段名 列類型[列屬性] [位置first/after 字段名](默認(rèn)加到表的最后面)字段位置:字段想要存放的位置铃岔,first:在某某之間(最前面)毁习,第一個(gè)字段纺且;after 字段名:放在某個(gè)具體的字段后(默認(rèn)的)

  • 刪除字段
    alter table 表名 drop 字段名

  • 修改字段名
    alter table 表名 change 舊字段名 新字段名 字段類型[列屬性] [位置屬性] (修改名字需要修改字段類型)

  • 修改字段類型(屬性):
    alter table 表名 modify 字段名 新類型 [新屬性] [新位置]

  • 刪除表結(jié)構(gòu)
    drop table 表名[,表名2....]; 可同時(shí)刪除多個(gè)表

  • 小結(jié):對(duì)表結(jié)構(gòu)操作時(shí)载碌,都需要用特定字段如alter,并且需要特指table及名稱嫁艇,但下邊對(duì)數(shù)據(jù)操作步咪,則不需要特指table益楼。

對(duì)表中數(shù)據(jù)操作命令:

  • 增:insert into 表名(字段列表,當(dāng)插入所有字段時(shí)可省略) values(對(duì)應(yīng)字段列表)
  • 刪:delete from 表名 [where條件] 如果沒有where條件静袖,意味著系統(tǒng)會(huì)自動(dòng)刪除該表所有數(shù)據(jù)(慎用)
  • 改:update 表名 set 字段名 = 新值 [where條件];
  • 查:select 字段列表 from 表名 from 表名 where 字段名=值 and 字段名=值 order by name DESC, age ASC //字段列表使用‘ 坠陈, ’隔開

需要注意:

寫的順序:select ... from... where.... group by... having... order by..
執(zhí)行順序:from... where...group by... having.... select ... order by...

  • group by數(shù)據(jù)分組舉例:

方法一:
SELECT * FROM (select a.* from new_table a inner join new_table b on a.name = b.name group by name order by time desc) c GROUP BY c.kefuid

方法二:
SELECT * FROM (select * from new_table group by id order by time desc) c GROUP BY c.kefuid

  • having條件語句:
    having與where有類似作用仇矾,當(dāng)WHERE 關(guān)鍵字無法與合計(jì)函數(shù)一起使用時(shí)使用:
    例如:我們希望查找訂單總金額少于 2000 的客戶:

SELECT Customer,SUM(OrderPrice) FROM Orders GROUP BY Customer HAVING SUM(OrderPrice)<2000

然而贮匕,現(xiàn)在我們希望查找客戶 "Bush" 或 "Adams" 擁有超過 1500 的訂單總金額刻盐,則需要這樣:

SELECT Customer,SUM(OrderPrice) FROM Orders WHERE Customer='Bush' OR Customer='Adams'
GROUP BY Customer HAVING SUM(OrderPrice)>1500

  • SUM () 函數(shù)返回?cái)?shù)值列的總數(shù):

SELECT COUNT(column_name) FROM table_name //函數(shù)返回指定列的值的數(shù)目值(NULL 不計(jì)入)
SELECT COUNT(*) FROM table_name //函數(shù)返回表中的記錄數(shù)目值
SELECT COUNT(DISTINCT column_name) FROM table_name //函數(shù)返回指定列的不同值的數(shù)目值

  • AVG()取平局值:如我們希望找到 OrderPrice 值高于 OrderPrice 平均值的客戶

SELECT Customer FROM Orders WHERE OrderPrice>(SELECT AVG(OrderPrice) FROM Orders)

  • inner join

nner join(等值聯(lián)接) 從兩表中抽取出聯(lián)結(jié)字段相等的行數(shù)據(jù)敦锌,整合到一起后返回。
left join(左聯(lián)接) 將左表中的所有數(shù)據(jù)和右表中聯(lián)結(jié)字段相等的數(shù)據(jù),整合到一起后返回。
right join(右聯(lián)接) 將右表中的所有記錄和左表中聯(lián)結(jié)字段相等的數(shù)據(jù),整合到一起后返回汉买。

inner join 連接兩個(gè)數(shù)據(jù)表的用法:

SELECT * FROM 表1 INNER JOIN 表2 ON 表1.字段號(hào)=表2.字段號(hào)

inner join 連接三個(gè)數(shù)據(jù)表的用法:

SELECT * FROM (表1 INNER JOIN 表2 ON 表1.字段號(hào)=表2.字段號(hào)) INNER JOIN 表3 ON 表1.字段號(hào)=表3.字段號(hào)

總結(jié):
只要兩個(gè)表的公共字段有匹配值录别,就將這兩個(gè)表中的記錄組合起來,類似數(shù)學(xué)中的取并集葫男,并集(合并相同部分)。

推薦幾個(gè)網(wǎng)址:
? ?? 查看 inner join on 用法示例
? ?? sql語法大全
? ?? sql舉例大全
? ?? Mysql教程(Windows)

網(wǎng)上相關(guān)資源很多旺遮,在此記錄只為方便查看耿眉。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末组底,一起剝皮案震驚了整個(gè)濱河市筐骇,隨后出現(xiàn)的幾起案子铛纬,更是在濱河造成了極大的恐慌,老刑警劉巖棺弊,帶你破解...
    沈念sama閱讀 222,378評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件模她,死亡現(xiàn)場(chǎng)離奇詭異缝驳,居然都是意外死亡用狱,警方通過查閱死者的電腦和手機(jī)拼弃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,970評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門吻氧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人鲁森,你說我怎么就攤上這事歌溉〔莼郏” “怎么了漫谷?”我有些...
    開封第一講書人閱讀 168,983評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵蹂析,是天一觀的道長(zhǎng)舔示。 經(jīng)常有香客問我,道長(zhǎng)识窿,這世上最難降的妖魔是什么斩郎? 我笑而不...
    開封第一講書人閱讀 59,938評(píng)論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮喻频,結(jié)果婚禮上缩宜,老公的妹妹穿的比我還像新娘。我一直安慰自己甥温,他們只是感情好锻煌,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,955評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著宋梧,像睡著了一般加叁。 火紅的嫁衣襯著肌膚如雪展融。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,549評(píng)論 1 312
  • 那天础嫡,我揣著相機(jī)與錄音涧尿,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的扔涧。 我是一名探鬼主播,決...
    沈念sama閱讀 41,063評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼曙搬,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起扳还,我...
    開封第一講書人閱讀 39,991評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤俏让,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后预鬓,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體竣蹦,經(jīng)...
    沈念sama閱讀 46,522評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡抄淑,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,604評(píng)論 3 342
  • 正文 我和宋清朗相戀三年郑原,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了女器。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,742評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡驳阎,死狀恐怖沫屡,靈堂內(nèi)的尸體忽然破棺而出劫瞳,到底是詐尸還是另有隱情,我是刑警寧澤废睦,帶...
    沈念sama閱讀 36,413評(píng)論 5 351
  • 正文 年R本政府宣布杖挣,位于F島的核電站,受9級(jí)特大地震影響歌殃,放射性物質(zhì)發(fā)生泄漏波材。R本人自食惡果不足惜躲因,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,094評(píng)論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望苍姜。 院中可真熱鬧垫释,春花似錦琅束、人聲如沸艾船。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,572評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽防楷。三九已至,卻和暖如春吝沫,著一層夾襖步出監(jiān)牢的瞬間栅受,已是汗流浹背而芥。 一陣腳步聲響...
    開封第一講書人閱讀 33,671評(píng)論 1 274
  • 我被黑心中介騙來泰國打工趋翻, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留辟癌,地道東北人厂置。 一個(gè)月前我還...
    沈念sama閱讀 49,159評(píng)論 3 378
  • 正文 我出身青樓海铆,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,747評(píng)論 2 361