1. 程式人生 > >使用GreenDao建立表、關聯表(一對一,一對多,多對多)、CURD、升級資料庫等操作

使用GreenDao建立表、關聯表(一對一,一對多,多對多)、CURD、升級資料庫等操作

        應用場景:從照片中找出包含有使用者人臉的照片,並儲存該照片中的人臉特徵、使用該特徵和使用者人臉特徵對比,滿足條件,照片就儲存到該使用者表裡

一、建立表

GreenDao託管地址:https://github.com/greenrobot/greenDAO

官方文件:http://greenrobot.org/greendao/documentation/updating-to-greendao-3-and-annotations

新增GreenDao外掛到專案中 

classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2'
buildscript {
    
    repositories {
    ..............................................................
    }
    dependencies {
     .............................................................
        classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2'
    
    }
}

新增依賴到模組中

apply plugin: 'org.greenrobot.greendao' // apply plugin

dependencies {
............
    implementation 'org.greenrobot:greendao:3.2.2' // add library
}

實體類

@Entity
public class PortraitPhoto {

  @Id(autoincrement = true)
//id引數型別要使用Long,不然儲存後id一直為0
  private Long id;
  private String name;
  private String path;
  private int faceCount;
  private String sdCardId;
//一對多的關係,一張照片可能有多個人,所以會有多個人臉特徵(一張臉一個特徵)
  @ToMany(referencedJoinProperty = "portraitPhotoId")
  List<Feature> featureList;
}

 

@Entity
public class Feature {
  @Id(autoincrement = true)
  private Long id;
  private Long portraitPhotoId;
  private byte[] features;
}

用於儲存使用者id ,因為使用者id是唯一的,所以我用它來做UserPhoto表的主鍵,也就是userId,下面儲存多對多關係時會用到


@Entity public class UserPhoto {
  @Id private Long id;
//照片和使用者是多對多的關係
  @ToMany
  @JoinEntity(entity = PhotoJoinUser.class, sourceProperty = "userId", targetProperty = "portraitPhotoId")
  private List<PortraitPhoto> portraitphotoList;

多對多的關係,需要建立中間表,一張照片裡面可能有多個使用者,一個使用者可以出現多張照片裡面 

@Entity
public class PhotoJoinUser {

  @Id
  private Long id;
  private Long portraitPhotoId;
  private Long userId;
}

 

實體類建立好之後,點選AndroidStudio工具欄 Build - Make project

初始化

//UpgradeHelper 是升級資料庫相關的一個類,繼承DaoMaster.OpenHelper
UpgradeHelper openHelper = new UpgradeHelper(context, NAME,null);
    final Database db = openHelper.getWritableDb();
    mDaoSession = new DaoMaster(db).newSession();

執行一下,就能建立表了,如下圖(這是新增資料後,可以看看錶的結構)

UserPhoto

二、儲存

  mPortraitPhotoDao = mDaoSession.getPortraitPhotoDao();
  mFeatureDao = mDaoSession.getFeatureDao();
  
   
    for (PortraitPhoto portraitPhoto : portraitPhotoList) {
    ......................

      if (portraitPhoto.getFaceCount() == NOBODY) {
        continue;
      }
      Bitmap bitmap = BitmapFactory.decodeFile(portraitPhoto.getPath());
      if (mPrtekIIS == null || bitmap == null) {
        continue;
      }

      final int width = bitmap.getWidth() & ~1;
      final int height = bitmap.getHeight() & ~1;
      Image image = new AshmemImage(ImageFormat.NV21, width, height);
      image.setBitmap(bitmap, new Rect(0, 0, width, height));
//查詢圖片中是否有人
      List<Person> persons = mPrtekIIS.findPersons(image);

      if (persons == null || persons.isEmpty()) {
        portraitPhoto.setFaceCount(NOBODY);

//儲存沒有人臉的照片
        mPortraitPhotoDao.insert(portraitPhoto);
        continue;
      }
      portraitPhoto.setFaceCount(persons.size());
//儲存有人臉的照片
      mPortraitPhotoDao.insert(portraitPhoto);
//儲存特徵
      for (Person person : persons) {
        FaceFeature facefeature = person.getFace().getFeature();

        if (facefeature == null) {
          continue;
        }

        Feature feature = new Feature();
        feature.setFeatures(facefeature.serialize());
//一對多關係儲存方式
        feature.setPortraitPhotoId(portraitPhoto.getId());
        mFeatureDao.insert(feature);

      }
      portraitPhotos.add(portraitPhoto);
    }

多對多關係儲存

     //照片和使用者已經儲存過了,所以只要在第三張繫結一下Id就可以了
     PhotoJoinUser photoJoinUser = new PhotoJoinUser();
//照片的id
     photoJoinUser.setPortraitPhotoId(portraitPhoto.getId());
//使用者的id
     photoJoinUser.setUserId(user.getUserId());
//儲存
     mDaoSession.insert(photoJoinUser);

GreenDao CURD方法支援多種引數型別

insertInTx()//儲存 集合或陣列、可變引數

insertOrReplace()//如果資料庫中不存在這一條資訊就儲存,否則替換

三、更新

update()//更新很簡單,專案也沒有使用,所以就不舉例子說明了

四、刪除

GreenDao不支援刪除級聯,有關聯表的資料需要查詢後再逐一刪除,例如,刪除了一張照片,照片裡有使用者(包含特徵)。但是關聯特徵表裡的資料不會刪掉

  public void deletePhoto(List<PortraitPhoto> photos) {
    if (photos == null || photos.isEmpty()) {
      return;
    }
    final PhotoJoinUserDao photoJoinUserDao = mDaoSession.getPhotoJoinUserDao();
    final FeatureDao featureDao = mDaoSession.getFeatureDao();
    final PortraitPhotoDao portraitPhotoDao = mDaoSession.getPortraitPhotoDao();

    for (PortraitPhoto photo : photos) {
//通過條件(照片id)從中間表刪除
      photoJoinUserDao.queryBuilder()
          .where(PhotoJoinUserDao.Properties.PortraitPhotoId.eq(photo.getId()))
          .buildDelete()
          .executeDeleteWithoutDetachingEntities();
//獲取該照片所有特徵
      final List<Feature> featureList = photo.getFeatureList();

      if (featureList != null) {
//刪除
        featureDao.deleteInTx(featureList);
      }
    }
//最後刪除該照片集合(其實裡邊就一張,只是通過條件查詢時返回List<PortraitPhoto>)
    portraitPhotoDao.deleteInTx(photos);
  }

五、查詢

  final List<PortraitPhoto> dbPhotoList = mPortraitPhotoDao.queryBuilder()
        .where(PortraitPhotoDao.Properties.SdCardId.eq(mSdcardId))
        .list();

六、升級

//在模組的Build.grade檔案新增、
greendao{
  schemaVersion 2//修改版本號,即可升級
}

 GreenDao資料庫升級預設是不會遷移資料的,之前的資料將會丟失。下面是在stackoverflow網站找到了解決方案,也寫了相關的作者和編輯人員

import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.greenrobot.greendao.AbstractDao;
import org.greenrobot.greendao.database.Database;
import org.greenrobot.greendao.database.StandardDatabase;
import org.greenrobot.greendao.internal.DaoConfig;



/**
 * Createdby PedroOkawa and modified by MBH on 16/08/16.
 */
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;
  }

}

前面初始化時,用到的 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);
  }
}