GreenDao 3.0 基本使用 & 自定義數據庫路徑 & 數據庫升級

greenDAO是一款輕鬆快速的Android ORM解決方案,可將對象映射到SQLite數據庫.
這裏只是介紹其中的一部分功能, 想了解更多還是得去官網看看.
github地址: https://github.com/greenrobot/greenDAO

提示:使用前需要申請讀寫權限

一.導入greenDAO

1. 項目根目錄下, build.gradle,如下圖:
mavenCentral() 
classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2' //greendao
  1. 在 根目錄/app/build.gradle文件,添加
apply plugin: 'org.greenrobot.greendao'

android 節點下:

android{
    greendao{
            schemaVersion 1  //數據庫版本號
            daoPackage'greendao' //編譯時生成文件 所在的包名
            targetGenDir'src/main/java'  //編譯時生成文件 所在的目錄
      }
}
implementation 'org.greenrobot:greendao:3.2.2' //數據庫

2. 建立數據庫實體模型

@Entity
public class User{
    @Id
    String name= ""; 
    String age= "0.00";
    String lunch = "0.00";
}

@Entity 標記該類爲數據庫實體模型,指示GreendDao生成代碼
@Id 主鍵

三. 使用

1.默認方式
DbOpenHelper helper = new DaoMaster.DevOpenHelper(context, "數據庫文件名", null);
DaoMaster daoMaster = new DaoMaster(helper.getWritableDatabase());
DaoSession daoSession = daoMaster.newSession();
//然後就可以用userDao 操作數據庫了.
2. 自定義保存路徑

發現問題: 數據庫文件保存在哪兒? 可以自定義路徑嗎?
網上搜到, 默認保存的目錄是 /data/data/Package Name/database. 可是卻不知道爲什麼?
最終還是得跑去看看源碼. 這裏簡單說一下:
①. 我們一開始就new DaoMaster.DevOpenHelper這個對象, 我們對着它的構造函數一層一層進去看, 發現最終調用的是android系統自帶的 SQLiteOpenHelper.java這個類. 如下圖:

從上圖我們可以看到: 獲取路徑的方法是mContext.getDatabasePath(name), 這個時候我就想: 新建一個Context, 然後重寫getDatabasePath就可以了, 結果發現崩掉了.

②. 再試嘗試, 重寫Context中的openOrCreateDatabase, 結果成功了.

③. 看過源碼的應該知道,我們默認傳進去的Context實際上就是ContextWrapper, 於是有了以下寫法:

最終解決方法:

public class DatabaseContext  extends ContextWrapper {

    public static String dbPath = "";

    public DatabaseContext(Context base, String dbPath) {
        super(base);
        if(!TextUtils.isEmpty(dbPath)){
            this.dbPath = dbPath;
        }
    }

    @Override
    public File getDatabasePath(String name){
        File dbDir = new File(dbPath);
        if(!dbDir.exists()){
            dbDir.mkdir();
        }

        File dbFile = new File(dbPath, name);
        if(!dbFile.exists()){
            try {
                dbFile.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return dbFile;
    }

    @Override
    public SQLiteDatabase openOrCreateDatabase(String name, int mode, SQLiteDatabase.CursorFactory factory) {
        SQLiteDatabase result = SQLiteDatabase.openOrCreateDatabase(getDatabasePath(name), null);
        return result;
    }

    @Override
    public SQLiteDatabase openOrCreateDatabase(String name, int mode, SQLiteDatabase.CursorFactory factory, DatabaseErrorHandler errorHandler) {
        SQLiteDatabase result = SQLiteDatabase.openOrCreateDatabase(getDatabasePath(name), null);
        return result;
    }
}

使用

DbOpenHelper helper = new DaoMaster.DevOpenHelper(new DatabaseContext(getApplication(), "數據庫目錄"), "數據庫文件名", null);
DaoMaster daoMaster = new DaoMaster(helper.getWritableDatabase());
DaoSession daoSession = daoMaster.newSession();
UserDao userDao = daoSession.getUserDao()
3. 數據升級出現的問題: 源數據會被覆蓋

解決辦法: 複製一份數據備份, 被覆蓋後重新保存
具體做法: 新增兩個輔助類: MigrationHelper.java 和 DbOpenHelper.java

/**
 * @Description: 數據庫升級輔助
 * @Author: liys
 * @CreateDate: 2019/6/17 14:15
 * @UpdateUser: 更新者
 * @UpdateDate: 2019/6/17 14:15
 * @UpdateRemark: 更新說明
 * @Version: 1.0
 */
public 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;
    }
}
/**
 * @Description: 數據庫升級輔助
 * @Author: liys
 * @CreateDate: 2019/6/17 14:11
 * @UpdateUser: 更新者
 * @UpdateDate: 2019/6/17 14:11
 * @UpdateRemark: 更新說明
 * @Version: 1.0
 */
public class DbOpenHelper extends DaoMaster.DevOpenHelper {

    public DbOpenHelper(Context context, String name) {
        super(context, name);
    }

    @Override
    public void onUpgrade(Database db, int oldVersion, int newVersion) {
        //切記不要調用super.onUpgrade(db,oldVersion,newVersion)
        if (oldVersion < newVersion) {
            MigrationHelper.migrate((StandardDatabase) db,
                    SpendMoneyBeanDao.class //具體操作數據庫的dao類, 如果需要多個,用逗號隔開
                    //TestDao1.class,
                    //TestDao2.class
            );
        }
    }
}
使用:
DbOpenHelper helper = new DbOpenHelper(new DatabaseContext(context, "數據庫目錄"), "數據庫文件名");
DaoMaster daoMaster = new DaoMaster(helper.getWritableDatabase());
DaoSession daoSession = daoMaster.newSession();
UserDao userDao = daoSession.getUserDao()
4. 增刪改查
UserDao userDao = daoSession.getUserDao()
//增
userDao.insert(userBean);
//刪
userDao.delete(userBean);
//改
userDao.update(userBean)
//查
//1. 查詢所有
userDao.queryBuilder().list();
//2. 單條件查詢(名字=liys)
userDao.queryBuilder()
      .where(UserDaoDao.Properties.Name.eq("liys"))
      .list();
//3. 多條件查詢, 同時滿足(name=liys  && age = 18)
userDao.queryBuilder()
      .where(UserDaoDao.Properties.Name.eq("liys"),
            UserDaoDao.Properties.Age.eq("18"))
      .list();
//4. 多條件查詢(或)
userDao.queryBuilder()
      .whereOr(UserDaoDao.Properties.Name.eq("liys"),
            UserDaoDao.Properties.Age.eq("18"))
//5. 查出來排序
serDao.queryBuilder()
      //.orderAsc(UserDaoDao.Properties.Age) //升序
      .orderAsc(UserDaoDao.Properties.Age) //升序
      .list();
5. 查詢條件總結

eq : 等於

notEq : 不等於

like: 模糊查詢 記住模糊查詢,string要用夾在%key%中間。

//查詢Name包含liys的人。
xxDao.queryBuilder().where(Properties.Name.like("%liys%")).list();

IN(..., ..., ...) 在給出的value的範圍內的符合項

gt: 大於

ge 大於等於

lt 小於

le 小於等於

isNull 不是空的

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章