GreenDao 数据库:使用 Raw 文件夹下的数据库文件以及数据库升级

1,464 阅读4分钟

一、使用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函数,这里不再赘述。