目录:
前言 导入依赖和Schema设置 一、数据库模型生成及读取操作 二、修改数据库文件路径 三、获取加密的数据库 四、数据库升级又不删除数据 五、总结

前言:      在Android开发中,或多或少总要接触SQLite。然而在使用它时,我们往往需要做许多额外的工作,像编写 SQL 语句与解析查询结果等。所以,适用于 Android 的ORM 框架也就孕育而生了,现在市面上主流的框架有 OrmLite、SugarORM、Active Android、Realm 与 GreenDAO。而greenDAO号称是速度最快的ORM(见官网)。 简单的讲,greenDAO 是一个将对象映射到 SQLite 数据库中的轻量且快速的 ORM 解决方案。
下面,我将详解地介绍如何在 Android Studio 上使用 greenDAO,并结合代码总结一些使用过程中的心得。
导入依赖和Schema设置:      导入依赖和Schema参数设置详细内容可参见官网和如下网址: http://www.jianshu.com/p/4e6d72e7f57a     我的Schema设置如下:
greendao{    schemaVersion 1    targetGenDir 'src/main/java'}
一、数据库模型生成及读取操作:  1、greenDao支持的数据模型生成方式包括以下两种:
  (1)编写greendaoGenerator的纯Java类库,以生成DaoMaster、DaoSession、bean和beanDao等;        可参见: http://www.open-open.com/lib/view/open1438065400878.html        注:greenDao 3.0版本以下只能采用这种方式。在升级3.0以后,因支持了注释方式,该方式不再推荐。   (2)利用注释的方式,生成 DaoMaster、DaoSession、bean和beanDao等;       2、下文仅就注释方式生成流程进行简单的介绍,详细的讲解可参见 http://www.jianshu.com/p/4e6d72e7f57a      首先,新建datamodel包,用以包含 DaoMaster、DaoSession、bean和beanDao等。      然后新建Area实体类,代码如下:
@Entitypublic class Area {    @Id    private String AreaCode;    private String AreaName;}
     最后,Build->Make Module 'app',即可自动生成 DaoMaster、DaoSession、Area和AreaDao。此时Area实体类的代码如下:
@Entitypublic class Area {    @Id    private String AreaCode;    private String AreaName;    @Generated(hash = 262290694)    public Area(String AreaCode, String AreaName) {        this.AreaCode = AreaCode;        this.AreaName = AreaName;    }    @Generated(hash = 179626505)    public Area() {    }    public String getAreaCode() {        return this.AreaCode;    }    public void setAreaCode(String AreaCode) {        this.AreaCode = AreaCode;    }    public String getAreaName() {        return this.AreaName;    }    public void setAreaName(String AreaName) {        this.AreaName = AreaName;    }}
     添加其他实体类的方法与Area一样。需要注意的是,不要手动修改 DaoMaster、DaoSession、bean和beanDao的代码,因为每一次编译项目,都会重新生成一次DaoMaster、DaoSession、bean和beanDao。如果修改的话,就会被覆盖掉。      为了便于数据的读取和添加,新建GreenDaoHelper辅助类,代码如下:
public class GreenDaoHelper extends Application {    private GreenDaoHelper Instance;    private static DaoMaster daoMaster;    private static DaoSession daoSession;    public GreenDaoHelper getInstance() {        if (Instance == null) {            Instance = this;        }        return Instance;    }    /**    * 获取DaoMaster    *    * @param context    * @return    */    public static DaoMaster getDaoMaster(Context context) {        if (daoMaster == null) {            try{                DaoMaster.OpenHelper helper = new DaoMaster.DevOpenHelper(context,"test.db",null);                daoMaster = new DaoMaster(helper.getWritableDatabase()); //获取未加密的数据库            }catch (Exception e){                e.printStackTrace();            }        }        return daoMaster;    }    /**    * 获取DaoSession对象    *    * @param context    * @return    */    public static DaoSession getDaoSession(Context context) {        if (daoSession == null) {            if (daoMaster == null) {                getDaoMaster(context);            }            daoSession = daoMaster.newSession();        }        return daoSession;    }}
      在读写数据库之前,要添加读写权限:
     在MainActivity.java中添加读写代码:
public class MainActivity extends AppCompatActivity {    private TextView textview;    private DaoSession session;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        textview=(TextView)findViewById(R.id.textview);        session = GreenDaoHelper.getDaoSession(this);        session.getAreaDao().deleteAll();//清空所有记录        //添加记录        Area area = new Area("01","北京");        Area area1 = new Area("02","天津");        session.getAreaDao().insert(area);        session.getAreaDao().insert(area1);        //查询记录        StringBuilder stringBuilder = new StringBuilder();        List areas = session.getAreaDao().loadAll();        for (int i = 0,n = areas.size();i
     运行结果如下图所示:
      二、修改数据库文件路径:      默认情况下,新创建的数据存储在data的包名目录下,设备如果不root的话,是无法查看SQLite数据库文件的。而实际应用中,我们往往需要copy数据库,或借用第三方工具查阅或编辑数据库内容。此时我们可以通过重写Context的 getDatabasePath( String name ) openOrCreateDatabase(String name,  int  mode, CursorFactory factory) openOrCreateDatabase(String name,  int  mode, CursorFactory factory, DatabaseErrorHandler errorHandler) 等三个方法 来修改SQLite文件的存储路径。      通过查询资料,发现 http://blog.csdn.net/chenzhenlindx/article/details/39183691 中的内容基本符合我们的需求。但是博主是在DaoMaster中重写方法的。通过上文我们知道,DaoMaster的代码是不能修改的。因此,我们可以将重写的方法放到GreenDaoHelper中去。 代码如下:
public class GreenDaoHelper extends Application {    private GreenDaoHelper Instance;    private static DaoMaster daoMaster;    private static DaoSession daoSession;    public GreenDaoHelper getInstance() {        if (Instance == null) {            Instance = this;        }        return Instance;    }    /**    * 获取DaoMaster    *    * @param context    * @return    */    public static DaoMaster getDaoMaster(Context context) {        if (daoMaster == null) {            try{                ContextWrapper wrapper = new ContextWrapper(context) {                /**                * 获得数据库路径,如果不存在,则创建对象对象                *                * @param name                */                @Override                public File getDatabasePath(String name) {                    // 判断是否存在sd卡                    boolean sdExist = android.os.Environment.MEDIA_MOUNTED.equals(android.os.Environment.getExternalStorageState());                    if (!sdExist) {// 如果不存在,                        Log.e("SD卡管理:", "SD卡不存在,请加载SD卡");                        return null;                    } else {// 如果存在                        // 获取sd卡路径                        String dbDir = android.os.Environment.getExternalStorageDirectory().getAbsolutePath();                        dbDir += "/Android";// 数据库所在目录                        String dbPath = dbDir + "/" + name;// 数据库路径                        // 判断目录是否存在,不存在则创建该目录                        File dirFile = new File(dbDir);                        if (!dirFile.exists())                            dirFile.mkdirs();                        // 数据库文件是否创建成功                        boolean isFileCreateSuccess = false;                        // 判断文件是否存在,不存在则创建该文件                        File dbFile = new File(dbPath);                        if (!dbFile.exists()) {                            try {                                isFileCreateSuccess = dbFile.createNewFile();// 创建文件                            } catch (IOException e) {                                e.printStackTrace();                            }                        } else                            isFileCreateSuccess = true;                        // 返回数据库文件对象                        if (isFileCreateSuccess)                            return dbFile;                        else                            return super.getDatabasePath(name);                    }                }                /**                * 重载这个方法,是用来打开SD卡上的数据库的,android 2.3及以下会调用这个方法。                *                * @param name                * @param mode                * @param factory                */                @Override                public SQLiteDatabase openOrCreateDatabase(String name, int mode, SQLiteDatabase.CursorFactory factory) {                    return SQLiteDatabase.openOrCreateDatabase(getDatabasePath(name), null);                }                /**                * Android 4.0会调用此方法获取数据库。                *                * @see android.content.ContextWrapper#openOrCreateDatabase(java.lang.String,                *      int,                *      android.database.sqlite.SQLiteDatabase.CursorFactory,                *      android.database.DatabaseErrorHandler)                * @param name                * @param mode                * @param factory                * @param errorHandler                */                @Override                public SQLiteDatabase openOrCreateDatabase(String name, int mode, SQLiteDatabase.CursorFactory factory, DatabaseErrorHandler errorHandler) {                    return SQLiteDatabase.openOrCreateDatabase(getDatabasePath(name), null);                }                };                DaoMaster.OpenHelper helper = new DaoMaster.DevOpenHelper(wrapper,"test.db",null);                daoMaster = new DaoMaster(helper.getWritableDatabase()); //获取未加密的数据库            }catch (Exception e){                e.printStackTrace();            }        }        return daoMaster;    }    /**    * 获取DaoSession对象    *    * @param context    * @return    */    public static DaoSession getDaoSession(Context context) {        if (daoSession == null) {            if (daoMaster == null) {                getDaoMaster(context);            }            daoSession = daoMaster.newSession();        }        return daoSession;    }}
     此时,再运行上述代码,就会在Android目录下发现我们的test.db文件。通过第三方工具,即可查看我们的数据库内容。下图是我用手机端的SqliteLookup工具查看到的数据库内容:

三、获取加密的数据库:      修改GreenDaoHelper.java,通过调用DaoMaster.OpenHelper类的getEncryptedWritableDb(password)或者 getEncryptedReadableDb(password)方法, 即可获取加密的数据库。
public static DaoMaster getDaoMaster(Context context) {    if (daoMaster == null) {        try{            ContextWrapper wrapper = new ContextWrapper(context) {                 ...            };            DaoMaster.OpenHelper helper = new DaoMaster.DevOpenHelper(wrapper,"test.db",null);            daoMaster = new DaoMaster(helper.getEncryptedWritableDb("1234"));//获取加密的数据库            //daoMaster = new DaoMaster(helper.getEncryptedReadableDb("1234"));//获取加密的数据库            //daoMaster = new DaoMaster(helper.getWritableDatabase()); //获取未加密的数据库        }catch (Exception e){            e.printStackTrace();        }    }    return daoMaster;}
     此时,若要解密或重新加密数据库,可参考 博客《利用SQLCipher加解密数据库(包括加解密已有的数据库)》。
四、数据库升级又不删除数据:      在实际开发的过程中,数据库的结构可能会有所改变。而 使用 DevOpenHelper 每次升级数据库时,表都会删除重建。因此,实际使用中需要建立类继承 DaoMaster.OpenHelper,实现 onUpgrade()方法。通过查询资料,对未加密的数据库,推荐使用 升级辅助库 GreenDaoUpgradeHelper( 可参见 https://github.com/yuweiguocn/GreenDaoUpgradeHelper/blob/master/README_CH.md )。该库通过 MigrationHelper在删表重建的过程中,使用临时表保存数据并还原。      示例程序 直接导入 MigrationHelper.java 源码。同时修改GreenDaoHelper.java文件,新建一个继承自 DaoMaster.OpenHelper的内部类MySQLiteOpenHelper。具体代码如下:
public class GreenDaoHelper extends Application {    private GreenDaoHelper Instance;    private static DaoMaster daoMaster;    private static DaoSession daoSession;    public GreenDaoHelper getInstance() {        if (Instance == null) {            Instance = this;        }        return Instance;    }    /**    * 获取DaoMaster    *    * @param context    * @return    */    public static DaoMaster getDaoMaster(Context context) {        if (daoMaster == null) {            try{                ContextWrapper wrapper = new ContextWrapper(context) {                          ...                };                DaoMaster.OpenHelper helper = new MySQLiteOpenHelper(wrapper,"test.db",null);                //daoMaster = new DaoMaster(helper.getEncryptedWritableDb("1234"));//获取加密的数据库                //daoMaster = new DaoMaster(helper.getEncryptedReadableDb("1234"));//获取加密的数据库                daoMaster = new DaoMaster(helper.getWritableDatabase()); //获取未加密的数据库            }catch (Exception e){                e.printStackTrace();            }        }        return daoMaster;    }    /**    * 获取DaoSession对象    *    * @param context    * @return    */    public static DaoSession getDaoSession(Context context) {        if (daoSession == null) {            if (daoMaster == null) {                getDaoMaster(context);            }            daoSession = daoMaster.newSession();        }        return daoSession;    }    private static class MySQLiteOpenHelper extends DaoMaster.OpenHelper {        public MySQLiteOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory) {            super(context, name, factory);        }        private static final String UPGRADE="upgrade";        @Override        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {            MigrationHelper.migrate(db,AreaDao.class);            Log.e(UPGRADE,"upgrade run success");        }    }}
     另外添加一个People实体类,并修改schemaVersion为更高的版本号,然后 Build->Make Module 'app',生成新的模型类。修改OnUpgrade方法如下:
@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {    MigrationHelper.migrate(db,AreaDao.class, PeopleDao.class);    Log.e(UPGRADE,"upgrade run success");}
     此时,运行程序。虽然程序成功启动,但是报如下错误:
12-30 10:02:12.503 6312-6312/com.wjk.greendaoexample E/SQLiteLog: (1) no such table: PEOPLE12-30 10:02:12.508 6312-6312/com.wjk.greendaoexample E/MigrationHelper: 【Failed to generate temp table】PEOPLE_TEMP                                                                         android.database.sqlite.SQLiteException: no such table: PEOPLE (code 1): , while compiling: CREATE TEMPORARY TABLE PEOPLE_TEMP AS SELECT * FROM PEOPLE;
     通过阅读源码发现,程序根据传入的beanDao对 所有 bean表都创建了临时表,并从bean表复制数据到bean_temp表中。而此时,People实体是新创建的,数据库中并没有这个表,因此报上面的错误。此时,我们需对源码进行修改,仅对数据库中已有的表创建临时表并保存数据。此外,源码中是按字段恢复数据,为方便起见,本程序修改为全表查询恢复。代码如下:
public final class MigrationHelper {    public static boolean DEBUG = false;    private static String TAG = "MigrationHelper";    private static List tablenames = new ArrayList<>();    public static List getTables(SQLiteDatabase db){        List tables = new ArrayList<>();        Cursor cursor = db.rawQuery("select name from sqlite_master where type='table' order by name", null);        while(cursor.moveToNext()){            //遍历出表名            tables.add(cursor.getString(0));        }        cursor.close();        return tables;    }    public static void migrate(SQLiteDatabase db, Class<? extends AbstractDao<?, ?>>... daoClasses) {        Database database = new StandardDatabase(db);        if (DEBUG) {            Log.d(TAG, "【Database Version】" + db.getVersion());            Log.d(TAG, "【Generate temp table】start");        }        tablenames=getTables(db);        generateTempTables(database, daoClasses);        if (DEBUG) {            Log.d(TAG, "【Generate temp table】complete");        }        dropAllTables(database, true, daoClasses);        createAllTables(database, false, daoClasses);        if (DEBUG) {            Log.d(TAG, "【Restore data】start");        }        restoreData(database, daoClasses);        if (DEBUG) {            Log.d(TAG, "【Restore data】complete");        }    }    private static void generateTempTables(Database db, Class<? extends AbstractDao<?, ?>>... daoClasses) {        for (int i = 0; i < daoClasses.length; i++) {            String tempTableName = null;            try {                DaoConfig daoConfig = new DaoConfig(db, daoClasses[i]);                if(!tablenames.contains(daoConfig.tablename)){//如果数据库中没有该表,则继续下次循环                    continue;                }                String tableName = daoConfig.tablename;                tempTableName = daoConfig.tablename.concat("_TEMP");                StringBuilder dropTableStringBuilder = new StringBuilder();                dropTableStringBuilder.append("DROP TABLE IF EXISTS ").append(tempTableName).append(";");                db.execSQL(dropTableStringBuilder.toString());                StringBuilder insertTableStringBuilder = new StringBuilder();                insertTableStringBuilder.append("CREATE TEMPORARY TABLE ").append(tempTableName);                insertTableStringBuilder.append(" AS SELECT * FROM ").append(tableName).append(";");                db.execSQL(insertTableStringBuilder.toString());                if (DEBUG) {                    Log.d(TAG, "【Table】" + tableName +"\n ---Columns-->"+getColumnsStr(daoConfig));                    Log.d(TAG, "【Generate temp table】" + tempTableName);                }            } catch (SQLException e) {                Log.e(TAG, "【Failed to generate temp table】" + tempTableName, e);            }        }    }    private static String getColumnsStr(DaoConfig daoConfig) {        if (daoConfig == null) {            return "no columns";        }        StringBuilder builder = new StringBuilder();        for (int i = 0; i < daoConfig.allColumns.length; i++) {            builder.append(daoConfig.allColumns[i]);            builder.append(",");        }        if (builder.length() > 0) {            builder.deleteCharAt(builder.length() - 1);        }        return builder.toString();    }    private static void dropAllTables(Database db, boolean ifExists, @NonNull Class<? extends AbstractDao<?, ?>>... daoClasses) {        reflectMethod(db, "dropTable", ifExists, daoClasses);        if (DEBUG) {            Log.d(TAG, "【Drop all table】");        }    }    private static void createAllTables(Database db, boolean ifNotExists, @NonNull Class<? extends AbstractDao<?, ?>>... daoClasses) {        reflectMethod(db, "createTable", ifNotExists, daoClasses);        if (DEBUG) {            Log.d(TAG, "【Create all table】");        }    }    /**    * dao class already define the sql exec method, so just invoke it    */    private static void reflectMethod(Database 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(Database db, Class<? extends AbstractDao<?, ?>>... daoClasses) {        for (int i = 0; i < daoClasses.length; i++) {            String tempTableName = null;            try {                DaoConfig daoConfig = new DaoConfig(db, daoClasses[i]);                String tableName = daoConfig.tablename;                if(!tablenames.contains(tableName)){                    continue;                }                tempTableName = daoConfig.tablename.concat("_TEMP");                StringBuilder insertTableStringBuilder = new StringBuilder();                insertTableStringBuilder.append("INSERT INTO ").append(tableName).append(" SELECT * FROM ").append(tempTableName).append(";");                db.execSQL(insertTableStringBuilder.toString());                if (DEBUG) {                    Log.d(TAG, "【Restore data】 to " + tableName);                }                StringBuilder dropTableStringBuilder = new StringBuilder();                dropTableStringBuilder.append("DROP TABLE IF EXISTS ").append(tempTableName);                db.execSQL(dropTableStringBuilder.toString());                if (DEBUG) {                    Log.d(TAG, "【Drop temp table】" + tempTableName);                }            } catch (SQLException e) {                Log.e(TAG, "【Failed to restore data from temp table (probably new table)】" + tempTableName, e);            }        }    }}
     此时,新建一个实体类Product,修改版本号,同时 修改OnUpgrade方法 , 再次运行程序,则成功运行。      注意:MigrationHelper.migrate(),暂时只接收 SQLiteDatabase ,不接收 Database,且对 加密的 数据库是无效的。而实际应用中,由于数据的重要性,数据库往往是必须要加密的。因此,对于加密的 数据库该如何进行更新支持呢?      具体思路如下,首先分析 MigrationHelper.migrate() 为什么不支持 加密的 数据库的更新,然后再找出对应的解决方案。 (1)MigrationHelper.migrate()为什么不支持加密的数据库的更新      通过加断点调试发现,数据库更新时并没有走MySQLiteOpenHelper的onUpgrade()方法,而是走的DatabaseOpenHelper抽象类里的EncryptedHelper内部类的 onUpgrade()方法。如图所示:
        EncryptedHelper内部类的 onUpgrade()方法调用的是 DatabaseOpenHelper抽象类本身的 onUpgrade()方法,但DatabaseOpenHelper抽象类本身的 onUpgrade()方法默认是啥也不执行的,如下图所示。因此, MigrationHelper.migrate() 为什么不支持 加密的 数据库的更新也就显而易见了。
(2)加密的数据库的更新支持的解决方案      此时,可以通过修改greenDao的源码,在 EncryptedHelper内部类的 onUpgrade()方法中添加对 MigrationHelper.migrate()的调用。      但,如果我不想修改源码,该怎么解决呢?默认情况下,获取和更新加密的数据库,调用的是 DatabaseOpenHelper抽象类本身的getEncryptedWritableDb(String password)和 onUpgrade()方法 。此时,我们可以在GreenDaoHelper中定义一个新的类MyEncryptedSQLiteOpenHelper继承自DaoMaster.OpenHelper,并在这个类中对这两个方法进行重写,同时在内部自定义一个net.sqlcipher.database.SQLiteOpenHelper的继承类,以代替 DatabaseOpenHelper抽象类里的EncryptedHelper内部类。      在添加代码之前,要先添加对sqlcipher的依赖,如下:
compile 'net.zetetic:android-database-sqlcipher:3.5.4@aar'
     具体代码如下:
public class GreenDaoHelper extends Application {    ......    public static DaoMaster getDaoMaster(Context context) {        if (daoMaster == null) {            try{                ContextWrapper wrapper = new ContextWrapper(context) {                          ...                };                //DaoMaster.OpenHelper helper = new MySQLiteOpenHelper(wrapper,"test.db",null);                MyEncryptedSQLiteOpenHelper helper = new MyEncryptedSQLiteOpenHelper(wrapper,"test.db",null);                daoMaster = new DaoMaster(helper.getEncryptedWritableDb("1234"));//获取加密的数据库                //daoMaster = new DaoMaster(helper.getEncryptedReadableDb("1234"));//获取加密的数据库                //daoMaster = new DaoMaster(helper.getWritableDatabase()); //获取未加密的数据库            }catch (Exception e){                e.printStackTrace();            }        }        return daoMaster;    }    ......    private static class MyEncryptedSQLiteOpenHelper extends DaoMaster.OpenHelper {        private final Context context;        private final String name;        private final int version = DaoMaster.SCHEMA_VERSION;        private boolean loadSQLCipherNativeLibs = true;        public MyEncryptedSQLiteOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory) {            super(context, name, factory);            this.context=context;            this.name=name;        }        private static final String UPGRADE="upgrade";        @Override        public void onUpgrade(Database db, int oldVersion, int newVersion) {            EncryptedMigrationHelper.migrate((EncryptedDatabase) db,AreaDao.class, PeopleDao.class, ProductDao.class);            Log.e(UPGRADE,"upgrade run success");        }        @Override        public Database getEncryptedWritableDb(String password) {            MyEncryptedHelper encryptedHelper = new MyEncryptedHelper(context,name,version,loadSQLCipherNativeLibs);            return encryptedHelper.wrap(encryptedHelper.getReadableDatabase(password));        }        private class MyEncryptedHelper extends net.sqlcipher.database.SQLiteOpenHelper {            public MyEncryptedHelper(Context context, String name, int version, boolean loadLibs) {                super(context, name, null, version);                if (loadLibs) {                    net.sqlcipher.database.SQLiteDatabase.loadLibs(context);                }            }            @Override            public void onCreate(net.sqlcipher.database.SQLiteDatabase db) {                MyEncryptedSQLiteOpenHelper.this.onCreate(wrap(db));            }            @Override            public void onUpgrade(net.sqlcipher.database.SQLiteDatabase db, int oldVersion, int newVersion) {                MyEncryptedSQLiteOpenHelper.this.onUpgrade(wrap(db), oldVersion, newVersion);            }            @Override            public void onOpen(net.sqlcipher.database.SQLiteDatabase db) {                MyEncryptedSQLiteOpenHelper.this.onOpen(wrap(db));            }            protected Database wrap(net.sqlcipher.database.SQLiteDatabase sqLiteDatabase) {                return new EncryptedDatabase(sqLiteDatabase);            }        }    }}
     上述代码中引用了EncryptedMigrationHelper.java类。该类与 MigrationHelper.java类似,只不过将android.database.sqlite.SQLiteDatabase替换为net.sqlcipher.database.SQLiteDatabase,同时对代码做了微小的改动。 EncryptedMigrationHelper.java类的代码如下:
public class EncryptedMigrationHelper {    public static boolean DEBUG = true;    private static String TAG = "UpgradeHelper";    private static List tablenames = new ArrayList<>();    public static List getTables(SQLiteDatabase db){        List tables = new ArrayList<>();        Cursor cursor = db.rawQuery("select name from sqlite_master where type='table' order by name", null);        while(cursor.moveToNext()){            //遍历出表名            tables.add(cursor.getString(0));        }        cursor.close();        return tables;    }    public static void migrate(EncryptedDatabase db, Class<? extends AbstractDao<?, ?>>... daoClasses) {        Database database = db;        if (DEBUG) {            Log.d(TAG, "【Database Version】" + db.getSQLiteDatabase().getVersion());            Log.d(TAG, "【Generate temp table】start");        }        tablenames=getTables(db.getSQLiteDatabase());        generateTempTables(database, daoClasses);        if (DEBUG) {            Log.d(TAG, "【Generate temp table】complete");        }        dropAllTables(database, true, daoClasses);        createAllTables(database, false, daoClasses);        if (DEBUG) {            Log.d(TAG, "【Restore data】start");        }        restoreData(database, daoClasses);        if (DEBUG) {            Log.d(TAG, "【Restore data】complete");        }    }    private static void generateTempTables(Database db, Class<? extends AbstractDao<?, ?>>... daoClasses) {        for (int i = 0; i < daoClasses.length; i++) {            String tempTableName = null;            try {                DaoConfig daoConfig = new DaoConfig(db, daoClasses[i]);                if(!tablenames.contains(daoConfig.tablename)){                    continue;                }                String tableName = daoConfig.tablename;                tempTableName = daoConfig.tablename.concat("_TEMP");                StringBuilder dropTableStringBuilder = new StringBuilder();                dropTableStringBuilder.append("DROP TABLE IF EXISTS ").append(tempTableName).append(";");                db.execSQL(dropTableStringBuilder.toString());                StringBuilder insertTableStringBuilder = new StringBuilder();                insertTableStringBuilder.append("CREATE TEMPORARY TABLE ").append(tempTableName);                insertTableStringBuilder.append(" AS SELECT * FROM ").append(tableName).append(";");                db.execSQL(insertTableStringBuilder.toString());                if (DEBUG) {                    Log.d(TAG, "【Table】" + tableName +"\n ---Columns-->"+getColumnsStr(daoConfig));                    Log.d(TAG, "【Generate temp table】" + tempTableName);                }            } catch (SQLException e) {                Log.e(TAG, "【Failed to generate temp table】" + tempTableName, e);            }        }    }    private static String getColumnsStr(DaoConfig daoConfig) {        if (daoConfig == null) {            return "no columns";        }        StringBuilder builder = new StringBuilder();        for (int i = 0; i < daoConfig.allColumns.length; i++) {            builder.append(daoConfig.allColumns[i]);            builder.append(",");        }        if (builder.length() > 0) {            builder.deleteCharAt(builder.length() - 1);        }        return builder.toString();    }    private static void dropAllTables(Database db, boolean ifExists, @NonNull Class<? extends AbstractDao<?, ?>>... daoClasses) {        reflectMethod(db, "dropTable", ifExists, daoClasses);        if (DEBUG) {            Log.d(TAG, "【Drop all table】");        }    }    private static void createAllTables(Database db, boolean ifNotExists, @NonNull Class<? extends AbstractDao<?, ?>>... daoClasses) {        reflectMethod(db, "createTable", ifNotExists, daoClasses);        if (DEBUG) {            Log.d(TAG, "【Create all table】");        }    }    /**    * dao class already define the sql exec method, so just invoke it    */    private static void reflectMethod(Database 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(Database db, Class<? extends AbstractDao<?, ?>>... daoClasses) {        for (int i = 0; i < daoClasses.length; i++) {            String tempTableName = null;            try {                DaoConfig daoConfig = new DaoConfig(db, daoClasses[i]);                String tableName = daoConfig.tablename;                if(!tablenames.contains(tableName)){                    continue;                }                tempTableName = daoConfig.tablename.concat("_TEMP");                StringBuilder insertTableStringBuilder = new StringBuilder();                insertTableStringBuilder.append("INSERT INTO ").append(tableName).append(" SELECT * FROM ").append(tempTableName).append(";");                db.execSQL(insertTableStringBuilder.toString());                if (DEBUG) {                    Log.d(TAG, "【Restore data】 to " + tableName);                }                StringBuilder dropTableStringBuilder = new StringBuilder();                dropTableStringBuilder.append("DROP TABLE IF EXISTS ").append(tempTableName);                db.execSQL(dropTableStringBuilder.toString());                if (DEBUG) {                    Log.d(TAG, "【Drop temp table】" + tempTableName);                }            } catch (SQLException e) {                Log.e(TAG, "【Failed to restore data from temp table (probably new table)】" + tempTableName, e);            }        }    }}
     通过加断点调试发现,数据库更新时调用了MyEncryptedSQLiteOpenHelper的onUpgrade()方法 。如图所示:



     至此,我们已能够对加密的数据库进行更新支持。
五、总结:      该博客对greenDao模型生成、增删改查、存储路径 修改 、加解密及更新升级等都有了较为详细的描述,在实际开发中也基本够用。不过,需要注意的是,本文引用的EncryptedMigrationHelper.java类和 MigrationHelper.java类均只支持对数据库对象的新增和删除,对于对象字段有修改的情况没做考虑。如果读者有需要的话,可另行开发。
 参考文献如下:      官网: http://greenrobot.org/greendao/      github网站: https://github.com/greenrobot/greenDAO      greenDao Generator方式介绍: http://www.open-open.com/lib/view/open1438065400878.html      注释方式及增删改查详细介绍: http://www.jianshu.com/p/4e6d72e7f57a      greenDao数据库存储路径修改: http://blog.csdn.net/chenzhenlindx/article/details/39183691      greenDao数据库升级: https://github.com/yuweiguocn/GreenDaoUpgradeHelper

源码下载

我的github地址:https://github.com/WJKCharlie/GreenDaoExample

更多相关文章

  1. androidRSA加密,java解密出现错误或者乱码
  2. 如何Android数据库缓存进行管理
  3. Android(安卓)数据库开发之事务
  4. Android简易实战教程--第四十七话《使用OKhttp回调方式获取网络
  5. Android获取View的宽度和高度
  6. Android(安卓)sdcard媒体文件更新(程序控制刷新MediaStore数据库)
  7. 写了个Android聊天客户端框架,基本聊天功能、数据库、服务器都有
  8. Android(安卓)字体国际化适配方法以及源码解析
  9. Apk脱壳圣战之---脱掉“爱加密”的壳

随机推荐

  1. Android(安卓)服务器推送技术
  2. 总结的一些android公共库
  3. gravity与layout_gravity的区别
  4. android ftp 客户端编写(ftp4j)
  5. Android中资源管理机制详细分析
  6. 【Android(安卓)UI设计与开发】第15期:顶
  7. Android开发资源完全汇总
  8. Android(安卓)沉浸式状态栏 头部可拉伸带
  9. android下解析xml文件遇到中文问题
  10. Android中原生Progress几种常用的风格