android資料庫如何進行版本升級?架構之資料庫框架升級
前言
在本篇裡,將會講解令開發者比較頭疼的資料庫升級。
話不多說,先來看程式碼效果,看看是否是想要的
如圖所示
- 當前APP版本號為V007;
- V001、V002升級到V007有對應的處理邏輯;
- V003、V004、V005、V006升級到V007也有對應的處理邏輯;
- 同理可實現任意版本可闊多個版本升級到最新資料庫;
開始之前我們先理一下資料庫升級的邏輯
- 任何資料庫在操作之前,我們最好有一個資料庫備份,所以這裡得要備份對應的資料庫File檔案;
- 任何資料表在操作之前,也要有一個資料表備份,所以這裡會在原表名加前字尾操作;
- 在資料表升級的時候,有些時候可能會對錶名、表列做任意增刪改的操作,所以這裡每次都要建立一個全新的表;
- 全新表建立好了,但是一張空表,這裡就需要查詢對應加了前字尾的原表資料,將對應資料新增至新表裡;
- 資料全部複製完成時,為了讓使用者有良好的體驗,我們需要刪除對應加了前字尾的原表;
- 對應原表刪除完畢時,我們需要刪除對應備份資料庫的File檔案。
總結:
- 操作【1】和【6】 這倆操作 屬於 java程式碼執行
- 其他【2】、【3】、【4】、【5】 這些操作,都屬於SQL操作
- 但SQL操作,透過效果圖發現,是寫在XML檔案裡面的,所以需要寫一個XML解析器
現在我們就按照對應步驟一一講解
1、備份原資料庫File檔案
/** * 複製單個檔案(可更名複製) * * @param oldPathFile 準備複製的檔案源 * @param newPathFile 複製到新絕對路徑帶檔名(注:目錄路徑需帶檔名) * @return */ public static void CopySingleFile(String oldPathFile, String newPathFile) { try {// int bytesum = 0; int byteread = 0; File oldfile = new File(oldPathFile); File newFile = new File(newPathFile); File parentFile = newFile.getParentFile(); if (!parentFile.exists()) { parentFile.mkdirs(); } if (oldfile.exists()) { //檔案存在時 InputStream inStream = new FileInputStream(oldPathFile); //讀入原檔案 FileOutputStream fs = new FileOutputStream(newPathFile); byte[] buffer = new byte[1024]; while ((byteread = inStream.read(buffer)) != -1) {// bytesum += byteread; //位元組數 檔案大小 fs.write(buffer, 0, byteread); } inStream.close(); } } catch (Exception e) { e.printStackTrace(); } } 複製程式碼
總結:這也沒啥可說的,就一個很簡單的檔案複製。
2、資料庫升級XML編寫 updateXml.xml
<?xml version="1.0" encoding="utf-8"?><updateXml> <createVersion version="V007"> <createDb name="hqk"> <!-- 要升級資料庫對應名 ,如果應用含多個資料庫,那麼可以建立多個 createDb 標籤--> <sql_createTable> <!-- 建立最新的表結構 --> <!-- @DbFiled("time") private String time; @DbFiled("id") private Long id; @DbFiled("path") private String path; --> create table if not exists tb_photo ( id Long,tb_time TEXT ,tb_path TEXT,tb_name TEXT); </sql_createTable> </createDb> </createVersion> <!-- V001,V002對應版本的app升級到 最新V007版本的升級邏輯--> <updateStep versionFrom="V001,V002" versionTo="V007"> <!-- 對應資料升級邏輯,對應上面的 createDb 標籤name ,如果有多對 createDb,這裡也可執行多對 updateDb--> <updateDb name="hqk"> <sql_before> <!-- 將V001,V002對應的舊錶重新命名備份--> alter table tb_photo rename to bak_tb_photo; </sql_before> <sql_after> <!-- 查詢重新命名後舊錶資料,將對應資料新增至新表裡--> insert into tb_photo(tb_time,id, tb_path) select tb_time,tb_id,tb_path from bak_tb_photo; </sql_after> <sql_after><!-- 刪除舊錶備份--> drop table if exists bak_tb_photo; </sql_after> </updateDb> </updateStep> <updateStep versionFrom="V003,V004,V005,V006" versionTo="V007"> <updateDb name="hqk"> <sql_before> alter table tb_photo rename to bak_tb_photo; </sql_before> <sql_after> insert into tb_photo(tb_time,id, tb_path) select tb_time,tb_id,tb_path from bak_tb_photo; </sql_after> <sql_after> drop table if exists bak_tb_photo; </sql_after> </updateDb> </updateStep></updateXml>複製程式碼
總結:
- createVersion 標籤 ,表示 當前 最新APP版本需要操作的內容
- createDb 標籤,表示當前最新對應的資料庫要操作的內容,可多組標籤,實現多個資料庫升級
- sql_createTable 標籤,表示當前最新對應的資料表要操作的內容,可多組標籤,實現多表升級
- updateStep 標籤,表示不同版本要升級的物件,可多組標籤,達到不同版本資料庫升級到最新資料庫的效果
- updateDb 標籤,表示舊版本要修改的對應資料庫
- sql_before 標籤,表示資料庫升級時優先順序最高的SQL,(在新表建立前執行)
- sql_after 標籤,表示資料庫升級時優先順序最低並按順序執行的SQL(在新表建立後執行)
更多Android技術分享可以關注@我,也可以加入QQ群號:1078469822,學習交流Android開發技能。
3、建立XML解析器
對應工具類 DomUtils.class
public class DomUtils { /** * 讀取升級xml * * @param context * @return */ public static UpdateDbXml readDbXml(Context context) { InputStream is = null; Document document = null; try { is = context.getAssets().open("updateXml.xml"); DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); document = builder.parse(is); } catch (Exception e) { e.printStackTrace(); } finally { if (is != null) { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } if (document == null) { return null; } UpdateDbXml xml = new UpdateDbXml(document); return xml; } /** * 新表插入資料 * * @param xml * @param lastVersion 上個版本 * @param thisVersion 當前版本 * @return */ public static UpdateStep findStepByVersion(UpdateDbXml xml, String lastVersion, String thisVersion) { if (lastVersion == null || thisVersion == null) { return null; } // 更新指令碼 UpdateStep thisStep = null; if (xml == null) { return null; } List<UpdateStep> steps = xml.getUpdateSteps(); if (steps == null || steps.size() == 0) { return null; } for (UpdateStep step : steps) { if (step.getVersionFrom() == null || step.getVersionTo() == null) { } else { // 升級來源以逗號分隔 String[] lastVersionArray = step.getVersionFrom().split(","); if (lastVersionArray != null && lastVersionArray.length > 0) { for (int i = 0; i < lastVersionArray.length; i++) { // 有一個配到update節點即升級資料 if (lastVersion.equalsIgnoreCase(lastVersionArray[i]) && step.getVersionTo().equalsIgnoreCase(thisVersion)) { thisStep = step; break; } } } } } return thisStep; } /** * 解析出對應版本的建表指令碼 * * @return */ public static CreateVersion findCreateByVersion(UpdateDbXml xml, String version) { CreateVersion cv = null; if (xml == null || version == null) { return cv; } List<CreateVersion> createVersions = xml.getCreateVersions(); if (createVersions != null) { for (CreateVersion item : createVersions) { Log.i("david", "item=" + item.toString()); // 如果表相同則要支援xml中逗號分隔 String[] createVersion = item.getVersion().trim().split(","); for (int i = 0; i < createVersion.length; i++) { if (createVersion[i].trim().equalsIgnoreCase(version)) { cv = item; break; } } } } return cv; } } 複製程式碼
對應XML的實體類
UpdateDbXml
/** * @ClassName: UpdateDbXml * @Description: 升級更新資料庫 * */public class UpdateDbXml { /** * 升級指令碼列表 */ private List<UpdateStep> updateSteps; /** * 升級版本 */ private List<CreateVersion> createVersions; public UpdateDbXml(Document document) { { // 獲取升級指令碼 NodeList updateSteps = document.getElementsByTagName("updateStep"); this.updateSteps = new ArrayList<UpdateStep>(); for (int i = 0; i < updateSteps.getLength(); i++) { Element ele = (Element) (updateSteps.item(i)); Log.i("jett","updateSteps 各個升級的版本:"+ele.toString()); UpdateStep step = new UpdateStep(ele); this.updateSteps.add(step); } } { /** * 獲取各升級版本 */ NodeList createVersions = document.getElementsByTagName("createVersion"); this.createVersions = new ArrayList<CreateVersion>(); for (int i = 0; i < createVersions.getLength(); i++) { Element ele = (Element) (createVersions.item(i)); Log.i("jett","各個升級的版本:"+ele.toString()); CreateVersion cv = new CreateVersion(ele); this.createVersions.add(cv); } } } public List<UpdateStep> getUpdateSteps() { return updateSteps; } public void setUpdateSteps(List<UpdateStep> updateSteps) { this.updateSteps = updateSteps; } public List<CreateVersion> getCreateVersions() { return createVersions; } public void setCreateVersions(List<CreateVersion> createVersions) { this.createVersions = createVersions; } } 複製程式碼
UpdateStep.class
/** * @ClassName: UpdateStep * @Description: 資料庫升級指令碼資訊 */public class UpdateStep{ /** * 舊版本 */ private String versionFrom; /** * 新版本 */ private String versionTo; /** * 更新資料庫指令碼 */ private List<UpdateDb> updateDbs; // ================================================== public UpdateStep(Element ele) { versionFrom = ele.getAttribute("versionFrom"); versionTo = ele.getAttribute("versionTo"); updateDbs = new ArrayList<UpdateDb>(); NodeList dbs = ele.getElementsByTagName("updateDb"); for (int i = 0; i < dbs.getLength(); i++) { Element db = (Element) (dbs.item(i)); UpdateDb updateDb = new UpdateDb(db); this.updateDbs.add(updateDb); } } public List<UpdateDb> getUpdateDbs() { return updateDbs; } public void setUpdateDbs(List<UpdateDb> updateDbs) { this.updateDbs = updateDbs; } public String getVersionFrom() { return versionFrom; } public void setVersionFrom(String versionFrom) { this.versionFrom = versionFrom; } public String getVersionTo() { return versionTo; } public void setVersionTo(String versionTo) { this.versionTo = versionTo; } } 複製程式碼
UpdateDb.class
** * @ClassName: UpdateDb * @Description: 更新資料庫指令碼 * */public class UpdateDb{ /** * 資料庫名稱 */ private String dbName; /** * */ private List<String> sqlBefores; /** * */ private List<String> sqlAfters; public UpdateDb(Element ele) { dbName = ele.getAttribute("name"); sqlBefores = new ArrayList<String>(); sqlAfters = new ArrayList<String>(); { NodeList sqls = ele.getElementsByTagName("sql_before"); for (int i = 0; i < sqls.getLength(); i++) { String sql_before = sqls.item(i).getTextContent(); this.sqlBefores.add(sql_before); } } { NodeList sqls = ele.getElementsByTagName("sql_after"); for (int i = 0; i < sqls.getLength(); i++) { String sql_after = sqls.item(i).getTextContent(); this.sqlAfters.add(sql_after); } } } public String getName() { return dbName; } public void setDbName(String dbName) { this.dbName = dbName; } public List<String> getSqlBefores() { return sqlBefores; } public void setSqlBefores(List<String> sqlBefores) { this.sqlBefores = sqlBefores; } public List<String> getSqlAfters() { return sqlAfters; } public void setSqlAfters(List<String> sqlAfters) { this.sqlAfters = sqlAfters; } } 複製程式碼
CreateVersion.class
public class CreateVersion{ /** * 版本資訊 */ private String version; /** * 建立資料庫表指令碼 */ private List<CreateDb> createDbs; public CreateVersion(Element ele) { version = ele.getAttribute("version"); Log.i("jett","CreateVersion="+version); { createDbs = new ArrayList<CreateDb>(); NodeList cs = ele.getElementsByTagName("createDb"); for (int i = 0; i < cs.getLength(); i++) { Element ci = (Element) (cs.item(i)); CreateDb cd = new CreateDb(ci); this.createDbs.add(cd); } } } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public List<CreateDb> getCreateDbs() { return createDbs; } public void setCreateDbs(List<CreateDb> createDbs) { this.createDbs = createDbs; } } 複製程式碼
CreateDb.class
/** * @ClassName: CreateDb * @Description: 建立資料庫指令碼 * */public class CreateDb{ /** * 資料庫表名 */ private String name; /** * 建立表的sql語句集合 */ private List<String> sqlCreates; public CreateDb(Element ele) { name = ele.getAttribute("name"); { sqlCreates = new ArrayList<String>(); NodeList sqls = ele.getElementsByTagName("sql_createTable"); for (int i = 0; i < sqls.getLength(); i++) { String sqlCreate = sqls.item(i).getTextContent(); this.sqlCreates.add(sqlCreate); } } } public String getName() { return name; } public void setName(String name) { this.name = name; } public List<String> getSqlCreates() { return sqlCreates; } public void setSqlCreates(List<String> sqlCreates) { this.sqlCreates = sqlCreates; } } 複製程式碼
4、萬事俱備只欠東風: UpdateManager.class
public class UpdateManager { private File parentFile = ContUtils.parentFile; private File bakFile = ContUtils.bakFile; private List<User> userList; public void startUpdateDb(Context context) { //讀取XML檔案,將XML內容轉化為對應物件 UpdateDbXml updateDbxml = DomUtils.readDbXml(context); // 下載 上一個版本 --》下一個版本 【注:在下載時,需要將舊版本、新版本以逗號的形式寫入檔案快取】 String[] versions = FileUtil.getLocalVersionInfo(new File(parentFile, "update.txt")); String lastVersion = versions[0];//拿到上一個版本 String thisVersion = versions[1];//拿到當前版本 //資料庫File原地址 String userFile = ContUtils.sqliteDatabasePath; //資料庫File備份地址 String user_bak = ContUtils.copySqliteDatabasePath; //升級前,資料庫File備份 FileUtil.CopySingleFile(userFile, user_bak); //根據對應新舊版本號查詢XML轉化物件裡面的指令碼,得到對應升級指令碼 UpdateStep updateStep = DomUtils.findStepByVersion(updateDbxml, lastVersion, thisVersion); if (updateStep == null) { return; } //拿到對應升級指令碼 List<UpdateDb> updateDbs = updateStep.getUpdateDbs(); try { //將原始資料庫中所有的表名 更改成 bak_表名(資料還在) executeBeforesSql(updateDbs); //檢查新表,建立新表 CreateVersion createVersion = DomUtils.findCreateByVersion(updateDbxml, thisVersion); executeCreateVersion(createVersion); //將原來bak_表名 的資料遷移到 新表中 executeAftersSql(updateDbs); } catch (Exception e) { e.printStackTrace(); } } private void executeAftersSql(List<UpdateDb> updateDbs) throws Exception { for (UpdateDb db : updateDbs) { if (db == null || db.getName() == null) { throw new Exception("db or dbName is null;"); } List<String> sqls = db.getSqlAfters(); SQLiteDatabase sqlitedb = getDb(); //執行資料庫語句 executeSql(sqlitedb, sqls); sqlitedb.close(); } } private void executeCreateVersion(CreateVersion createVersion) throws Exception { if (createVersion == null || createVersion.getCreateDbs() == null) { throw new Exception("createVersion or createDbs is null;"); } for (CreateDb cd : createVersion.getCreateDbs()) { if (cd == null || cd.getName() == null) { throw new Exception("db or dbName is null when createVersion;"); } // 建立資料庫表sql List<String> sqls = cd.getSqlCreates(); SQLiteDatabase sqlitedb = getDb(); executeSql(sqlitedb, sqls); sqlitedb.close(); } } //所有的表名 更改成 bak_表名(資料還在) private void executeBeforesSql(List<UpdateDb> updateDbs) throws Exception { for (UpdateDb db : updateDbs) { if (db == null || db.getName() == null) { throw new Exception("db or dbName is null;"); } List<String> sqls = db.getSqlBefores(); SQLiteDatabase sqlitedb = getDb(); //執行資料庫語句 executeSql(sqlitedb, sqls); sqlitedb.close(); } } private SQLiteDatabase getDb() { String dbfilepath = null; SQLiteDatabase sqlitedb = null; dbfilepath = ContUtils.sqliteDatabasePath;// logic對應的資料庫路徑 if (dbfilepath != null) { File f = new File(dbfilepath); f.mkdirs(); if (f.isDirectory()) { f.delete(); } sqlitedb = SQLiteDatabase.openOrCreateDatabase(dbfilepath, null); } return sqlitedb; } private void executeSql(SQLiteDatabase sqlitedb, List<String> sqls) { // 檢查引數 if (sqls == null || sqls.size() == 0) { return; } sqlitedb.beginTransaction(); for (String sql : sqls) { sql = sql.replaceAll("\r\n", " "); sql = sql.replaceAll("\n", " "); if (!"".equals(sql.trim())) { try { // Logger.i(TAG, "執行sql:" + sql, false); sqlitedb.execSQL(sql); } catch (SQLException e) { } } } sqlitedb.setTransactionSuccessful(); sqlitedb.endTransaction(); } } 複製程式碼
總結:這裡沒啥好說的,詳細都寫在註釋裡面了,都是按照上面的思路寫的
更多Android技術分享可以關注@我,也可以加入QQ群號:1078469822,學習交流Android開發技能。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70007350/viewspace-2793802/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 資料庫升級之-Dataguard滾動升級資料庫
- 資料庫升級之-資料泵資料庫
- 資料庫升級之-XTTS資料庫TTS
- 資料庫升級和工具資料庫
- 靜默方式安裝、升級oracle(三): 升級資料庫軟體及資料庫Oracle資料庫
- iOS CoreData (二) 版本升級和資料庫遷移iOS資料庫
- Android 中的升級資料庫最佳方法實踐Android資料庫
- Android 資料庫綜述(一) 資料庫片的升級與資料的遷移操作Android資料庫
- oracle資料庫11.2.0.3升級到11.2.0.4Oracle資料庫
- 13 使用SQL Apply 升級資料庫SQLAPP資料庫
- iOS 資料庫升級資料遷移解決方案iOS資料庫
- 資料庫升級-物理重新整理資料字典資料庫
- 如何理解騰訊雲資料庫戰略升級?資料庫
- 自定義開發資料庫升級程式資料庫
- 故障分析 | MySQL 資料庫升級後,資料庫怎麼卡住了MySql資料庫
- 最佳實踐:騰訊HTAP資料庫TBase助力某省核心IT架構升級資料庫架構
- phpStudy2018 升級資料庫 MySQL5.7PHP資料庫MySql
- 【資料庫升級】Oracle指令碼升級12c CDB to 19c CDB資料庫Oracle指令碼
- Oracle資料庫升級或資料遷移的方法探討Oracle資料庫
- 雲圖說|雲資料庫MySQL核心小版本升級全攻略資料庫MySql
- 在Rainbond中實現資料庫結構自動化升級AI資料庫
- 【資料庫】mysql5.6升級至5.7(物理方式)資料庫MySql
- 成為MySQL DBA 部落格系列-資料庫升級MySql資料庫
- Grafana的版本升級和資料遷移Grafana
- 如何將SQL Server 2000備份的資料庫檔案還原(升級)為SQL Server 2005或更高版本的資料庫?SQLServer資料庫
- 不起介面,用response file的方式升級資料庫資料庫
- 靜默方式安裝、升級oracle(二): 建立資料庫Oracle資料庫
- Java之學生資訊管理系統升級版(資料庫程式設計)Java資料庫程式設計
- 大資料:小白升級版大資料
- 阿里雲Polardb國產資料庫補丁升級 實踐阿里資料庫
- 雲資料庫PostgreSQL版重磅升級開年釋出會資料庫SQL
- Nginx如何進行平滑升級Nginx
- 金融級分散式資料庫架構設計要點分散式資料庫架構
- XView 架構升級之路View架構
- oracle資料庫升級的時候oracle universal installer has detected........Oracle資料庫
- java實現“資料平滑升級”Java
- LinkedIn大資料工程的升級大資料
- 如何升級fedora的版本