GreenDao 資料庫:使用Raw文件夾下的資料庫文件以及資料庫升級

来源:http://www.cnblogs.com/libertycode/archive/2017/01/06/6256000.html
-Advertisement-
Play Games

一、使用Raw文件夾下的資料庫文件 在使用GreenDao框架時,資料庫和數據表都是根據生成的框架代碼來自動創建的,從生成的DaoMaster中的OpenHelper類可以看出: 對應的createAllTables函數代碼: 再接著往下看: 從以上的代碼可以看出GreenDao在第一次使用的時候會 ...


一、使用Raw文件夾下的資料庫文件

在使用GreenDao框架時,資料庫和數據表都是根據生成的框架代碼來自動創建的,從生成的DaoMaster中的OpenHelper類可以看出:

public static abstract class OpenHelper extends SQLiteOpenHelper {

        public OpenHelper(Context context, String name, CursorFactory factory) {
            super(context, name, factory, SCHEMA_VERSION);
        }

        @Override
        public void onCreate(SQLiteDatabase db) {
            Log.i("greenDAO", "Creating tables for schema version " + SCHEMA_VERSION);
       //修改第二個參數為true
createAllTables(db,
false); } }

對應的createAllTables函數代碼:

/** Creates underlying database table using DAOs. */
    public static void createAllTables(SQLiteDatabase db, boolean ifNotExists) {
        xxxxxDao.createTable(db, ifNotExists);
    }

再接著往下看:

/** Creates the underlying database table. */
    public static void createTable(SQLiteDatabase db, boolean ifNotExists) {
        String constraint = ifNotExists? "IF NOT EXISTS ": "";
        db.execSQL("CREATE TABLE " + constraint + "'DOCTOR' (" + //
                "'_id' INTEGER PRIMARY KEY AUTOINCREMENT ," + // 0: id
                "'NAME' TEXT," + // 1: name
               
    }

從以上的代碼可以看出GreenDao在第一次使用的時候會強制創建數據表,如果這樣的話很可能就會導致程式崩潰。

public static abstract class OpenHelper extends SQLiteOpenHelper {

        public OpenHelper(Context context, String name, CursorFactory factory) {
            super(context, name, factory, SCHEMA_VERSION);
        }

        @Override
        public void onCreate(SQLiteDatabase db) {
            Log.i("greenDAO", "Creating tables for schema version " + SCHEMA_VERSION);
       //修改第二個參數為true

            createAllTables(db, true);
        }
    }

所以要使用Raw文件中的資料庫文件需要以下幾步:

  1)修改參數:

public static abstract class OpenHelper extends SQLiteOpenHelper {

        public OpenHelper(Context context, String name, CursorFactory factory) {
            super(context, name, factory, SCHEMA_VERSION);
        }

        @Override
        public void onCreate(SQLiteDatabase db) {
            Log.i("greenDAO", "Creating tables for schema version " + SCHEMA_VERSION);
       //修改第二個參數為true

            createAllTables(db, true);
        }
    }

  2)添加GreenDaoContextWrapper.java文件到項目中

public class GreenDaoContextWrapper extends ContextWrapper {

private Context mContext;

public GreenDaoContextWrapper(Context base) {
super(base);
this.mContext= base;
}

@Override
public File getDatabasePath(String name) {
Log.d("GreenDao","getDatabasePath");
Log.d("GreenDao",mContext.getDatabasePath(name).getAbsolutePath());
String filePath=mContext.getDatabasePath(name).getAbsolutePath();
File file=new File(filePath);
if (!file.exists()){
buildDatabase(filePath);
}
return file;
}
  
/**
* 創建資料庫文件,其實就是將raw文件夾下的資料庫文件複製到應用的database文件夾下:
* /data/data/com.xxxx/databases/
* @param filePath
*/

private void buildDatabase(String filePath){
        Log.d("GreenDao","buildDatabase");
InputStream inputStream=mContext.getResources().openRawResource(R.raw.accurmedicine);
FileOutputStream fos= null;
try {
fos = new FileOutputStream(filePath);
byte[] buffer=new byte[1024];
int length;
while ((length=inputStream.read(buffer))>0){
fos.write(buffer,0,length);
}
fos.close();
inputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}

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

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

這裡提一下:ContextWrapper是一個Context包裝類,需要包含一個真正的Context,詳細介紹看:

http://www.jianshu.com/p/94e0f9ab3f1d

  3)在創建DevOpenHelper的時候使用GreenDaoContextWrapper

String DBName="xxx";
DaoMaster.DevOpenHelper helper=new DaoMaster.DevOpenHelper(new GreenDaoContextWrapper(context),DBName,null);

這樣就大功告成了!

 

二、資料庫版本升級

這個辦法是從網上看到的,還不錯,就搬過來了。

public class MigrationHelper {

    private static MigrationHelper instance;

    public static MigrationHelper getInstance() {
        if (instance==null){
            instance=new MigrationHelper();
        }
        return instance;
    }

    /**
     * 創建臨時表->刪除舊表->創建新表->導入數據
     * @param database
     * @param daoClasses
     */
    public void migrate(SQLiteDatabase database, Class<? extends AbstractDao<?,?>>...daoClasses){
        generateTempTables(database,daoClasses);
        DaoMaster.dropAllTables(database,true);
        DaoMaster.createAllTables(database,false);
        restoreData(database,daoClasses);
    }

    /**
     * 臨時表生產
     * @param database
     * @param daoClasses
     */
    private void generateTempTables(SQLiteDatabase database,Class<? extends AbstractDao<?,?>>...daoClasses){
        for (int i=0;i<daoClasses.length;i++){
            DaoConfig config=new DaoConfig(database,daoClasses[i]);
            String divider="";
            String tableName=config.tablename;
            String tmpTableName=config.tablename.concat("_TEMP");
            ArrayList<String > properties=new ArrayList<>();
            StringBuilder createTableStringBuilder=new StringBuilder();
            createTableStringBuilder.append("CREATE TABLE ").append(tmpTableName).append(" (");
            List<String> columns = getColumns(database, tableName);
            for (int j=0;j<config.properties.length;j++){
                String columnName=config.properties[j].columnName;
                if (columns.contains(columnName)){
                    properties.add(columnName);
                    String type=null;
                    try {
                        type=getTypeByClass(config.properties[j].type);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    createTableStringBuilder.append(divider).append(columnName).append(" ").append(type);
                    if (config.properties[j].primaryKey){
                        createTableStringBuilder.append("  PRIMARY KEY");
                    }
                    divider=",";
                }
            }
            createTableStringBuilder.append(");");
            Log.d("xxxxx","sql="+createTableStringBuilder.toString());
            database.execSQL(createTableStringBuilder.toString());
            StringBuilder insertTableString=new StringBuilder();
            insertTableString.append("insert into ").append(tmpTableName).append(" (");
            insertTableString.append(TextUtils.join(",",properties));
            insertTableString.append(") select ");
            insertTableString.append(TextUtils.join(",",properties));
            insertTableString.append(" from ").append(tableName).append(";");
            Log.d("xxxxx","sql="+insertTableString.toString());
            database.execSQL(insertTableString.toString());
        }
    }

    /**
     * 數據欄位與Java數據類型匹配
     * @param type
     * @return
     * @throws Exception
     */
    private String getTypeByClass(Class<?> type) throws Exception {
        if (type.equals(String.class)){
            return "TEXT";
        }
        if (type.equals(Long.class)||type.equals(Integer.class)){
            return "INTEGER";
        }
        if (type.equals(Boolean.class)){
            return "BOOLEAN";
        }
        String strException="數據表數據類型匹配錯誤";
        Exception exception=new Exception(strException.concat("- Class").concat(type.toString()));
        throw exception;
    }

    /**
     * 獲取當前數據表欄位列表
     * @param database
     * @param tableName
     * @return
     */
    private static List<String > getColumns(SQLiteDatabase database,String tableName){
        List<String > columns=new ArrayList<>();
        Cursor cursor=null;
        /**
         * 通過查詢數據表
         */
        cursor=database.rawQuery("select * from "+tableName+" limit 1",null);
        try {
            if (cursor!=null){
                String[] columnNames = cursor.getColumnNames();
                for (String name:columnNames){
                    columns.add(name.toUpperCase());
                }
//                columns=new ArrayList<>(Arrays.asList(cursor.getColumnNames()));
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if (cursor!=null){
                cursor.close();
            }
        }
        return columns;
    }

    /**
     * 數據恢復->刪除臨時表
     * @param database
     * @param daoClasses
     */
    private void restoreData(SQLiteDatabase database,Class<? extends AbstractDao<?,?>>...daoClasses){
        for (int i=0;i<daoClasses.length;i++){
            DaoConfig config=new DaoConfig(database,daoClasses[i]);
            String tableName=config.tablename;
            String tmpTableName=config.tablename.concat("_TEMP");
            ArrayList<String > properties=new ArrayList<>();
            for (int j=0;j<config.properties.length;j++){
                String columnName = config.properties[j].columnName;

                if(getColumns(database, tmpTableName).contains(columnName)) {
                    properties.add(columnName);
                }
            }
            StringBuilder insertTableStringBuilder = new StringBuilder();

            insertTableStringBuilder.append("INSERT INTO ").append(tableName).append(" (");
            insertTableStringBuilder.append(TextUtils.join(",", properties));
            insertTableStringBuilder.append(") SELECT ");
            insertTableStringBuilder.append(TextUtils.join(",", properties));
            insertTableStringBuilder.append(" FROM ").append(tmpTableName).append(";");

            StringBuilder dropTableStringBuilder = new StringBuilder();

            dropTableStringBuilder.append("DROP TABLE ").append(tmpTableName);

            database.execSQL(insertTableStringBuilder.toString());
            database.execSQL(dropTableStringBuilder.toString());
        }
    }

}

 

然後在需要資料庫版本設計的時候修改DaoMaster中的SCHEMA_VERSION 

 

public static final int SCHEMA_VERSION = 1;

這個變數就是用於在創建OpenHelper時指定資料庫版本號。

緊接著修改DevOpenHelper的onUpgrade代碼:

 @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            Log.i("greenDAO", "Upgrading schema from version " + oldVersion + " to " + newVersion + " by dropping all tables");
//            dropAllTables(db, true);
//            onCreate(db);
            MigrationHelper.getInstance()
                    .migrate(db,
                    paramsDao.class,
                    );
        }

最後大功告成!

 順帶需要補充一下,查看SQLite資料庫版本可以執行以下這句語句:

 

PRAGMA user_version

 

設置SQLite資料庫版本的語句:

PRAGMA user_version =<你的版本號>

以上的這兩句話其實可以在SQLiteDatabase以及SQLiteOpenHelper的源碼中看到:

在android-24的SQLiteOpenHelper源碼中可以看到:

/**
     * Gets the database version.
     *
     * @return the database version
     */
    public int getVersion() {
        return ((Long) DatabaseUtils.longForQuery(this, "PRAGMA user_version;", null)).intValue();
    }

    /**
     * Sets the database version.
     *
     * @param version the new database version
     */
    public void setVersion(int version) {
        execSQL("PRAGMA user_version = " + version);
    }

而SQLiteOpenHelper設置和獲取資料庫版本就是通過調用這兩句話來實現的,具體源碼可以查看SQLiteOpenHelper的getDatabaseLocked函數,這裡不再贅述。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Android SDK版本號 與 API Level 對應關係如下表: Code nameVersionAPI level (no code name) 1.0 API level 1 (no code name) 1.1 API level 2 2008 年9月發佈的Android第一版 Cupc ...
  • APP簡介 律師查詢是基於聚合數據的律師查詢介面做的,這個介面目前處於停用狀態,但是,由於我是之前申請的,所以,還可以用,應該是無法再申請了。 效果圖 開發 一、HttpHelper 既然是請求介面的,那就少不了請求方式,這裡是比較常用的HttpClient,用起來也比較方便,單獨封裝一個HttpH ...
  • 作者:Antonio Leiva 時間:Jan 5, 2017 原文鏈接:https://antonioleiva.com/lambdas-kotlin/ 由於Lambda表達式允許更簡單的方式建模式函數,所以它是Kotlin和任何其他現代開發語言的最強工具之一。 在Java6中,我們僅能下麵方法這 ...
  • 看完這篇你學到什麼: 熟悉gradle的構建配置 熟悉代碼構建環境的目錄結構,你知道的不僅僅是只有src/main 開發、生成環境等等環境可以任意切換打包 多渠道打包 APK輸出文件配置 需求 一般我們開發的環境分為:debug 和 release,但是你想再分內測1環境、內測2環境等等怎麼辦呢? ...
  • UI層複習筆記 在main文件中,UIApplicationMain函數一共做了三件事 根據第三個參數創建了一個應用程式對象 預設寫nil,即創建的是UIApplication類型的對象,此對象看成是整個應用程式的一個抽象,負責存儲應用程式的狀態。 根據第四個參數創建了一個應用程式代理類對象 所謂代 ...
  • 昨天 提交App Store 的時候被拒了 We discovered one or more bugs in your app when reviewed on iPhone running iOS 10.2 on Wi-Fi connected to an IPv6 network. Speci ...
  • 我們都知道,開發一個app很大程度依賴服務端:服務端提供介面數據,然後我們展示;另外,開發一個app,還需要美工協助切圖。沒了介面,沒了美工,app似乎只能做成單機版或工具類app,真的是這樣的嗎?先來展示下我的個人app,沒有服務端,沒有美工完成的,換言之,我幹了所有人的活: 這個app叫“微言” ...
  • 1.如果可以重置模擬器 首先試試重置模擬器 2.如果不能重置,可以選擇使用如下命令殺死模擬器服務: killall -9 com.apple.CoreSimulator.CoreSimulatorService //殺死模擬器服務 3.使用如下命令刪除模擬器目錄下是所有文件 rm -rf ~/Lib ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...