第三方資料庫框架 - GreenDao簡介
1. 概述
在android開發中,可能或多或少的都會接觸 Sqlite資料庫,然而我們在使用它的時候通常需要做很多的額外工作,比如就像編寫 Sqlite語句、解析查詢結果等等,所以適用於Android的 ORM框架 橫空出世,現在市面上邊主流的框架有 Sqlite、LitePal、GreenDao、Realm、OrmLite、SugarORM、Active Android,而 GreenDao號稱是速度最快的 ORM框架,在使用之前需要配置一些地方,那麼接下來我們就來看下它的具體配置及時如何使用的。
ORM還不是很清楚的,可以看下我之前的文章 第三方資料庫框架 - LitePal簡介
2. 需要配置的地方
2.1>:project下的build.gradle
2.2>:app下的 build.gradle,這裡需要配置3個地方
需要注意下邊配置的地方:
/*targetGenDirTest:設定生成單元測試目錄
generateTests:設定自動生成單元測試用例*/
schemaVersion 1 // 資料庫schema版本,也就是資料庫的版本號
daoPackage 'cn.novate.greendao.greendao' // DaoMaster、DaoSession、UserDao所在的包名
targetGenDir 'src/main/java' // DaoMaster、DaoSession、UserDao所在的目錄
以上就已經配置好了,然後點選 Sync Now,就是立即構建,就會在 cn.novate.greendao包下邊生成 DaoMaster、DaoSession、UserDao這3個類,注意這3個類都是 在 上邊自己配置的cn.novate.greendao.greendao包下邊,接下來就是具體使用了。
3. 具體使用
3.1>: 寫一個JavaBean,也就是我們的 User 實體類物件,就是我們資料庫中的表;
/**
* Email: 2185134304@qq.com
* Created by JackChen 2018/4/11 9:07
* Version 1.0
* Params:
* Description:
*/
/**
* @Entity: 將我們普通的java類變為一個能夠被 greendao 識別的資料庫型別的實體類
* @Id: 通過 @Id 註解 標記的欄位必須是 Long型別的,注意是包裝型別的,這個欄位在資料庫中表示它就是主鍵,並且預設是自增的
* @NotNul: 資料庫的表當前的列不能為空
*/
@Entity
public class User {
@Id
private Long id ;
private String name ;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
@Generated(hash = 873297011)
public User(Long id, String name) {
this.id = id;
this.name = name;
}
@Generated(hash = 586692638)
public User() {
}
}
3.2>:然後點選 build下邊的 Make Project,然後就會發現自己的 User實體類中多了好多程式碼,沒錯,這個就是 GreenDao給我們自動生成的
需要注意:
第一:如果你想再次新增實體類Age,可以直接寫一個實體類Age,然後點選 Build下的 Make Project會重新為你生成AgeDao;
第二:不要手動修改DaoMaster、DaoSession、UseDao和User中的程式碼,因為每一次編譯專案的時候,都會重新生成一次DaoMaster、DaoSession、UserDao和User,所以說如果修改了的話就會被覆蓋;
3.3>:為了便於資料的讀取和新增,這裡新建GreenDaoHelper,用於獲取DaoMaster、DaoSession,程式碼如下:
* Email: 2185134304@qq.com
* Created by JackChen 2018/4/11 13:18
* Version 1.0
* Params:
* Description: 便於資料的讀取和新增,新建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;
}
}
需要注意:
到這裡就已經建立好User實體類,並且也可以直接獲取 DaoMaster、DaoSession物件,接下來就可以進行增刪改查操作了。
4. 新增資料和查詢
4.1>:新增資料
第一:建立User物件,然後設定資料,引數一是id,Long包裝型別的,引數二是name,傳遞時候傳遞的是null目的就是在插入的過程中,id會自增長,
第二:呼叫 UserDao的 insert方法,用於新增資料;
具體程式碼如下:
public class MainActivity extends AppCompatActivity {
private DaoSession daoSession;
private TextView textview;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//初始化許可權
initPermission();
textview = (TextView) findViewById(R.id.textview);
daoSession = GreenDaoHelper.getDaoSession(this);
daoSession.getUserDao().deleteAll(); // 清空所有記錄
// 新增資料
// 引數1:id 傳遞null表示
User user = new User(null , "王子文") ;
User user1 = new User(null , "北京-Novate") ;
daoSession.getUserDao().insert(user) ;
daoSession.getUserDao().insert(user1) ;
// 查詢資料
StringBuilder sb = new StringBuilder() ;
List<User> users = daoSession.getUserDao().loadAll() ;
for (int i = 0; i < users.size(); i++) {
sb.append("id: ").append(users.get(i).getId()).
append(", name: ").append(users.get(i).getName()).append("\n") ;
}
textview.setText(sb);
}
/**
* 初始化許可權事件
*/
private void initPermission() {
//檢查許可權
String[] permissions = CheckPermissionUtils.checkPermission(this);
if (permissions.length == 0) {
//許可權都申請了
//是否登入
} else {
//申請許可權
ActivityCompat.requestPermissions(this, permissions, 100);
}
}
}
執行結果如下:
5. 修改存放資料庫路徑
一般情況下,新建的資料庫預設位置是存放在 data/data/包名/database下邊的,手機如果不root的話,根本就無法檢視 test.db資料庫檔案,更別提想要去操作該 test.db資料庫檔案。而在實際的開發過程中,可能需要copy資料庫,或者使用第三方工具開啟 該 test.db資料庫檔案來檢視裡邊的資料,此時可以通過重寫 Context的 getDatabasePath()、openOrCreateDatabase()、openOrCreateDatabase()這3個方法來修改 test.db的資料庫檔案的存放路徑。
// 方法一
getDatabasePath(String name)
// 方法二
openOrCreateDatabase(String name, int mode, CursorFactory factory)
// 方法三
openOrCreateDatabase(String name, int mode, CursorFactory factory, DatabaseErrorHandler errorHandler)
上邊已經說了,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;
}
}
通過上邊修改後的 GreenDaoHelper的程式碼後,我們可以在 手機儲存 --> Android --> 裡邊就會有 test.db資料庫檔案,不清楚自己建立的 test.db資料庫檔案存放路徑在哪,可以看下邊我的手機截圖:
然後可以通過 qq或者微信 傳送到電腦桌面,通過第三方工具開啟該 test.db資料庫檔案,就可以看到自己在程式碼中寫的User物件實體類對應的 --> USER表及該表中的欄位如下圖所示:
當然也可以使用 手機檢視,都是可以的。
6. 資料庫加密
可以直接呼叫 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加解密資料庫(包括加解密已有的資料庫)》。
7. 資料庫升級但又不刪除資料
使用DevOpenHelper升級資料庫時,表都會刪除重建。因此,在實際開發過程中都是在 GreenDaoHelper中自己寫一個類繼承 DaoMaster.OpenHelper實現 onUpdate()方法,使得資料庫升級。我們示例程式碼中是這樣做的:
7.1>:複製 MigrationHelper類到專案中;
7.2>:然後在 GreenDaoHelper中自定義MySQLiteOpenHelper繼承 DaoMaster.OpenHelper,實現 onUpdate()方法;程式碼如下:
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");
}
}
}
7.3>:然後新建一個 People實體類類,自己只需要寫下邊程式碼即可,然後直接 build --> Make Model app就會生成下邊的程式碼;
@Entity
public class People {
@Id
private Long id ;
private String Name ;
private String Sex ;
}
@Entity
public class People {
@Id
private Long id ;
private String Name ;
private String Sex ;
public String getSex() {
return this.Sex;
}
public void setSex(String Sex) {
this.Sex = Sex;
}
public String getName() {
return this.Name;
}
public void setName(String Name) {
this.Name = Name;
}
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
@Generated(hash = 1284135911)
public People(Long id, String Name, String Sex) {
this.id = id;
this.Name = Name;
this.Sex = Sex;
}
@Generated(hash = 1406030881)
public People() {
}
}
7.4>:修改 schemaVersion 版本號為更高的;
7.5>:然後修改 onUpdate()方法如下:
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
MigrationHelper.migrate(db,AreaDao.class, PeopleDao.class);
Log.e(UPGRADE,"upgrade run success");
}
然後執行程式,發現會報如下的錯,意思就是找不到People這張表:
通過閱讀原始碼發現,程式會根據傳入的 beanDao會對所有的 JavaBean建立臨時表, 然後把 該 JavaBean表中的資料 複製到 bean_temp臨時表中,但是這個時候 People實體類是新建立的,資料庫中並沒有這個表,所以會報上邊的錯誤,所以我們只需要對原始碼稍作修改,讓它只對資料庫中已有的表建立臨時表並且儲存資料,還有,原始碼中是按照欄位恢復資料,我們把它修改為全表查詢恢復;
程式碼如下:
public final class MigrationHelper {
public static boolean DEBUG = false;
private static String TAG = "MigrationHelper";
private static List<String> tablenames = new ArrayList<>();
public static List<String> getTables(SQLiteDatabase db){
List<String> 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實體類,修改版本號,同時修改 onUpdate()方法;
Product程式碼如下:
@Entity
public class Product {
@Id
private Long Id ;
private String Name ;
public String getName() {
return this.Name;
}
public void setName(String Name) {
this.Name = Name;
}
public Long getId() {
return this.Id;
}
public void setId(Long Id) {
this.Id = Id;
}
@Generated(hash = 2099832872)
public Product(Long Id, String Name) {
this.Id = Id;
this.Name = Name;
}
@Generated(hash = 1890278724)
public Product() {
}
}
onUpdate()方法及執行結果如下:
注意:
1>:MigrationHelper.migrate()暫時只接收 SQLiteDatabase,不接收 DataBase;
2>:對加密的資料庫更新是無效的;
我們在開發過程中,由於要保證資料的安全性,所以一般都是需要對 資料庫加密的,那麼對於 加密的資料庫,該如何更新呢?我們採用的思想就是 —— 逆推,也就是說首先分析MigrationHelper.migrate()為什麼不支援 對 加密資料庫的更新,然後再找出對應的解決方法。
8. 分析MigrationHelper.migrate()為什麼不支援對 加密資料庫的 更新?
由上圖可知,MigrationHelper.migrate()更新資料庫時呼叫的是 DatabaseOpenHelper中內部類 EncrytedHelper類中的 onUpdate()方法,而該方法呼叫的是 DatabaseOpenHelper中的 onUpdate()方法,點選進去後發現 該onUpdate()方法沒有做任何操作,如下圖所示;
所以 MigrationHelper.migrate()方法 不支援 加密資料庫的 更新。
9. 對加密資料庫的更新 的 解決方案
9.1>:在 GreenDaoHelper 中 新建一個類 MyEncryptedSQLiteOpenHelper 繼承 DaoMaster.OpenHelper,然後實現 onUpdate()、getEncryptedWritableDb(String password)方法;
9.2>:然後在 MyEncryptedSQLiteOpenHelper 內部中 再去 寫一個MyEncryptedHelper類 繼承 net.sqlcipher.database.SQLiteOpenHelper,目的就是代替 DatabaseOpenHelper中的 EncryptedHelper內部類
1>:首先需要新增對 sqlcipher 的依賴:
compile 'net.zetetic:android-database-sqlcipher:3.5.4@aar'
2>:然後修改 GreenDaoHelper程式碼如下:
/**
* Email: 2185134304@qq.com
* Created by JackChen 2018/4/11 13:18
* Version 1.0
* Params:
* Description: 便於資料的讀取和新增,新建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) {
/**
* 獲得資料庫路徑,如果不存在,則建立物件物件
*/
@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);
}
};
/** 如果使用DevOpenHelper升級資料庫時,表都會刪除重建,所以需要自定義 一個類繼承 DaoMaster.OpenHelper,實現onUpdate()方法即可 */
/*DaoMaster.OpenHelper helper = new DaoMaster.DevOpenHelper(wrapper,"test.db",null);
daoMaster = new DaoMaster(helper.getWritableDatabase());*/ //獲取未加密的資料庫
// daoMaster = new DaoMaster(helper.getEncryptedWritableDb("1234")) ; // 獲取加密的資料庫 下邊的2種方法都是可以的
// daoMaster = new DaoMaster(helper.getEncryptedReadableDb("1234")) ;
//適用於未加密的資料庫
DaoMaster.OpenHelper helper = new MySQLiteOpenHelper(wrapper,"test.db",null);
daoMaster = new DaoMaster(helper.getWritableDatabase()); //獲取未加密的資料庫
// 適用於加密的資料庫
// MyEncryptedSQLiteOpenHelper helper = new MyEncryptedSQLiteOpenHelper(wrapper , "test.db" , null) ;
}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,UserDao.class, PeopleDao.class, ProductDao.class);
Log.e("TAG" ,"upgrade run success"); // TAG: upgrade run success
}
}
/**
* 適用於加密的資料庫
*/
private static class MyEncryptedSQLiteOpenHelper extends DaoMaster.OpenHelper{
public MyEncryptedSQLiteOpenHelper(Context context, String name) {
super(context, name);
}
public MyEncryptedSQLiteOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory) {
super(context, name, factory);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
super.onUpgrade(db, oldVersion, newVersion);
MigrationHelper.migrate(db , UserDao.class , PeopleDao.class , ProductDao.class);
Log.e("TAG" , "update run success") ;
}
}
}
3>:需要拷貝 EncryptedMigrationHelper類到專案中,該類與 MigrationHelper類類似,只是將 android.database.sqlite.SQLiteDatabase 替換為 net.sqlcipher.database.SQLiteDatabase,然後修改了一小部分程式碼,這個類的程式碼就不貼了;
最後,一定不要忘記新增許可權:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
相關文章
- greenDAO資料庫之修改儲存地址資料庫
- 大資料框架原理簡介大資料框架
- MongoDB資料庫簡介MongoDB資料庫
- WIOD資料庫簡介資料庫
- oceanbase資料庫簡介資料庫
- HSQL 資料庫介紹(1)--簡介SQL資料庫
- MySQL資料庫索引簡介MySql資料庫索引
- Oracle:容器資料庫簡介Oracle資料庫
- redis(1)NoSQL資料庫簡介RedisSQL資料庫
- H2 資料庫介紹(1)--簡介資料庫
- Oracle - 資料庫的組成簡介Oracle資料庫
- mysql 資料庫效能分析工具簡介MySql資料庫
- MySQL資料庫儲存引擎簡介MySql資料庫儲存引擎
- 國產資料庫OushuDB(Database)簡介資料庫Database
- 圖資料庫基礎簡介 -KDnuggets資料庫
- 第1章 Oracle資料庫簡介-RMOracle資料庫
- 第1章 Oracle資料庫簡介-DBMSOracle資料庫
- 達夢資料庫全文索引簡介資料庫索引
- ASP.NET MVC – SQL 資料庫簡介ASP.NETMVCSQL資料庫
- HTML5 Web SQL 資料庫簡介HTMLWebSQL資料庫
- 資料庫 Mysql 邏輯架構簡介資料庫MySql架構
- 對GaussDB資料庫和資料管理的簡單介紹資料庫
- 手擼一個簡易Android資料庫框架Android資料庫框架
- larvael 引入第三方輕量級 PHP 資料庫框架 MedooPHP資料庫框架
- DB2--資料庫管理系統簡介DB2資料庫
- 科研資料庫備案平臺簡介(RDD)資料庫
- Scrapy框架簡介框架
- SpringMVC框架簡介②SpringMVC框架
- SpringMVC框架簡介①SpringMVC框架
- Hibernate框架簡介⑤框架
- Hibernate框架簡介④框架
- Hibernate框架簡介③框架
- Hibernate框架簡介②框架
- Hibernate框架簡介①框架
- Spring框架簡介⑩Spring框架
- Spring框架簡介⑨Spring框架
- Spring框架簡介⑧Spring框架
- Spring框架簡介⑦Spring框架