GreenDao 数据库:使用Raw文件夹下的数据库文件以及数据库升级
一、使用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函数,这里不再赘述。