之前的文章有提到,可以在xml文件中配置數據庫信息:http://www.cnblogs.com/wenjiang/p/4492303.html,現在就講如何利用這些信息類構建數據庫。
xml文件大概如下:
<?xml version="1.0" encoding="utf-8"?> <database> <!-- 數據庫名稱 --> <dbname value="zwb.db"></dbname> <!-- 數據庫版本 --> <version value="1"></version> <!-- 數據庫表 --> <list> <mapping class="com.zwb.args.dbpratice.model.Status"></mapping> <mapping class="com.zwb.args.dbpratice.model.User"></mapping> </list> </database>
讀取出來的信息其實很簡單:數據庫名字,數據庫版本和數據庫表。
Android原本就提供了SQLiteOpenHelper來幫助我們完成數據庫的一些基本操作,所以這里還是需要實現SQLiteOpenHelper的。
定義一個SQLiteOpenHelper,在初始化的時候將數據庫名字和數據庫版本傳進去:
public BaseSQLiteOpenHelper(Context context, String name, int version) { super(context, name, null, version); }
接下來就是在onCreate方法中實現創建表的動作:
考慮到數據升級的問題,我將有關數據升級的操作都放在一個方法中,然后在onUpgrade方法中調用onCreate就行了。
這里只考慮數據升級的問題,當然,降級也是可以的,不過一般這種情況在移動端比較少見。
之前將包含數據庫的表名的List存儲進SharedPreferencesManager中,當然,一般的SharedPreferencesManager是不能存儲容器類的,但這里我進行了擴展,具體的實現后面有需要再拿出來。
數據庫是在xml文件中配置數據庫表的信息,所以必須判斷從xml文件中讀取出來的數據庫信息是否還是和之前一致,如果原先的配置信息沒有的話,就要創建表(哪怕只是修改表名,都當做是重新創建表,這是無可置疑的,因為修改舊表后還指望能夠將數據遷移到新表中,說明一開始建表時候實在是太草率了。。。),如果原先的配置有,現在沒有,說明是要刪除舊表。
List<String> oldTableList = SharedPreferencesManager.getInstance().getListString("tables"); if (oldTableList.size() != 0) { for (String table : oldTableList) { if (!DatabaseCache.tableSet.contains(table)) { dropTable(db, table); } } for (String table : DatabaseCache.tableSet) { if (!oldTableList.contains(table)) { createTable(db, table); } } }
dropTable的實現非常簡單:
String deleteSql = "drop table if exists " + table;
db.execSQL(deleteSql);
createTable的實現也不復雜,同樣是拼接SQL語句:
private void createTable(SQLiteDatabase db, String table) { try { StringBuilder sql = new StringBuilder("create table if not exists "); BaseTable entity = (BaseTable) (Class.forName(table).newInstance()); String tableName = getTableName(entity); sql.append(tableName); sql.append(" (id integer primary key autoincrement, "); List<String> columnList = getColumns(entity); for (int i = 0, length = columnList.size(); i < length; i++) { sql.append(columnList.get(i) + " "); if (i == length - 1) { break; } sql.append(", "); } sql.append(");"); db.execSQL(sql.toString()); } catch (InstantiationException e) { LogUtil.e(e.toString()); } catch (IllegalAccessException e) { LogUtil.e(e.toString()); } catch (ClassNotFoundException e) { LogUtil.e(e.toString()); } catch (NoSuchTableException e) { LogUtil.e(e.toString()); } }
Android本身也提供了對SQL語句的封裝,但實際上,使用Android的API還是不如自己寫SQL。。。
如果應用是第一次安裝,那么數據庫的創建動作就結束了,最后只要將新的配置信息存儲到SharedPreferencesManager中就行。
但如果是數據庫升級,在執行完上面的操作,表是該刪的刪,該建的的建,但如果要做數據遷移,比如說,某張表增加了一些字段(這里不做刪除字段的處理,就算有多余字段的數據保留也並不是個問題,比起丟失數據的嚴重性來說,一定程度的冗余字段還是可以接受的)。
實現也是比較簡單,就是幾個步驟:將舊表重命名為一個臨時表保存數據,創建舊表,將臨時表的數據遷移到舊表中,刪除臨時表,每個步驟都是SQL語句的拼接而已:
for (String tableEntity : DatabaseCache.tableSet) { try { BaseTable entity = (BaseTable) (Class.forName(tableEntity).newInstance()); db.beginTransaction(); String table = getTableName(entity); String selectSql = "select * from " + table; Cursor cursor = db.rawQuery(selectSql, null); List<String> oldColumns = new ArrayList<>(); for (String column : cursor.getColumnNames()) { oldColumns.add(column); } String alterSql = "alter table " + table + " rename to " + table + "_temp"; db.execSQL(alterSql); createTable(db, tableEntity); List<String> newColumns = getColumnFrom(tableEntity); StringBuilder upgradeSqlBuilder = new StringBuilder("insert into " + table + " select id, "); int i = 0; for (String column : newColumns) { if (oldColumns.contains(column)) { upgradeSqlBuilder.append(column + ", "); i++; } } if ((i != 0) && (i < newColumns.size())) { for (int j = 0, length = newColumns.size() - i; j < length; j++) { upgradeSqlBuilder.append("'', "); } } String upgradeStr = upgradeSqlBuilder.toString(); String upgradeSql = upgradeStr.substring(0, upgradeStr.length() - 2) + " from " + table + "_temp"; db.execSQL(upgradeSql); dropTable(db, table + "_temp"); db.setTransactionSuccessful(); db.endTransaction(); } catch (InstantiationException e) { LogUtil.e(e.toString()); } catch (IllegalAccessException e) { LogUtil.e(e.toString()); } catch (ClassNotFoundException e) { LogUtil.e(e.toString()); } catch (NoSuchTableException e) { LogUtil.e(e.toString()); } }
由於涉及到的數據庫操作比較多,我就將這些操作放在事務中執行。
到了這里,根據讀取的xml配置信息,應用的數據庫信息就創建出來了。