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
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...