Android 架構(gòu)之?dāng)?shù)據(jù)庫(kù)框架升級(jí)
前言:
上一篇講解了Android 架構(gòu)之?dāng)?shù)據(jù)框架搭建 ,里面含有數(shù)據(jù)庫(kù)最基礎(chǔ)的增刪改查功能,不過(guò)只考慮了單數(shù)據(jù)庫(kù),開(kāi)發(fā)者可以舉一反三按照對(duì)應(yīng)思路設(shè)計(jì)多數(shù)據(jù)庫(kù)架構(gòu)。 在本篇里,將會(huì)講解令開(kāi)發(fā)者比較頭疼的數(shù)據(jù)庫(kù)升級(jí)。
話不多說(shuō),先來(lái)看代碼效果,看看是否是想要的

如上圖所示:
- 當(dāng)前APP版本號(hào)為V007;
- V001、V002升級(jí)到V007有對(duì)應(yīng)的處理邏輯;
- V003、V004、V005、V006升級(jí)到V007也有對(duì)應(yīng)的處理邏輯;
- 同理可實(shí)現(xiàn)任意版本可闊多個(gè)版本升級(jí)到最新數(shù)據(jù)庫(kù);
開(kāi)始之前我們先理一下數(shù)據(jù)庫(kù)升級(jí)的邏輯
- 任何數(shù)據(jù)庫(kù)在操作之前,我們最好有一個(gè)數(shù)據(jù)庫(kù)備份,所以這里得要備份對(duì)應(yīng)的數(shù)據(jù)庫(kù)File文件;
- 任何數(shù)據(jù)表在操作之前,也要有一個(gè)數(shù)據(jù)表備份,所以這里會(huì)在原表名加前后綴操作;
- 在數(shù)據(jù)表升級(jí)的時(shí)候,有些時(shí)候可能會(huì)對(duì)表名、表列做任意增刪改的操作,所以這里每次都要?jiǎng)?chuàng)建一個(gè)全新的表;
- 全新表創(chuàng)建好了,但是一張空表,這里就需要查詢對(duì)應(yīng)加了前后綴的原表數(shù)據(jù),將對(duì)應(yīng)數(shù)據(jù)添加至新表里;
- 數(shù)據(jù)全部拷貝完成時(shí),為了讓用戶有良好的體驗(yàn),我們需要?jiǎng)h除對(duì)應(yīng)加了前后綴的原表;
- 對(duì)應(yīng)原表刪除完畢時(shí),我們需要?jiǎng)h除對(duì)應(yīng)備份數(shù)據(jù)庫(kù)的
File文件。
總結(jié):
- 操作【1】和【6】 這倆操作 屬于 java代碼執(zhí)行
- 其他【2】、【3】、【4】、【5】 這些操作,都屬于SQL操作
- 但SQL操作,通過(guò)效果圖發(fā)現(xiàn),是寫(xiě)在XML文件里面的,所以需要寫(xiě)一個(gè)XML解析器
現(xiàn)在我們就按照對(duì)應(yīng)步驟一一講解
1、備份原數(shù)據(jù)庫(kù)File文件
/**
* 復(fù)制單個(gè)文件(可更名復(fù)制)
*
* @param oldPathFile 準(zhǔn)備復(fù)制的文件源
* @param newPathFile 拷貝到新絕對(duì)路徑帶文件名(注:目錄路徑需帶文件名)
* @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()) { //文件存在時(shí)
InputStream inStream = new FileInputStream(oldPathFile); //讀入原文件
FileOutputStream fs = new FileOutputStream(newPathFile);
byte[] buffer = new byte[1024];
while ((byteread = inStream.read(buffer)) != -1) {
// bytesum += byteread; //字節(jié)數(shù) 文件大小
fs.write(buffer, 0, byteread);
}
inStream.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
總結(jié):這也沒(méi)啥可說(shuō)的,就一個(gè)很簡(jiǎn)單的文件復(fù)制。
2、數(shù)據(jù)庫(kù)升級(jí)XML編寫(xiě) updateXml.xml
<?xml version="1.0" encoding="utf-8"?>
<updateXml>
<createVersion version="V007">
<createDb name="hqk"> <!-- 要升級(jí)數(shù)據(jù)庫(kù)對(duì)應(yīng)名 ,如果應(yīng)用含多個(gè)數(shù)據(jù)庫(kù),那么可以創(chuàng)建多個(gè) createDb 標(biāo)簽-->
<sql_createTable> <!-- 創(chuàng)建最新的表結(jié)構(gòu) -->
<!--
@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對(duì)應(yīng)版本的app升級(jí)到 最新V007版本的升級(jí)邏輯-->
<updateStep versionFrom="V001,V002" versionTo="V007">
<!-- 對(duì)應(yīng)數(shù)據(jù)升級(jí)邏輯,對(duì)應(yīng)上面的 createDb 標(biāo)簽name ,如果有多對(duì) createDb,這里也可執(zhí)行多對(duì) updateDb-->
<updateDb name="hqk">
<sql_before> <!-- 將V001,V002對(duì)應(yīng)的舊表重命名備份-->
alter table tb_photo rename to bak_tb_photo;
</sql_before>
<sql_after> <!-- 查詢重命名后舊表數(shù)據(jù),將對(duì)應(yīng)數(shù)據(jù)添加至新表里-->
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>
總結(jié):
createVersion標(biāo)簽 ,表示 當(dāng)前 最新APP版本需要操作的內(nèi)容createDb標(biāo)簽,表示當(dāng)前最新對(duì)應(yīng)的數(shù)據(jù)庫(kù)要操作的內(nèi)容,可多組標(biāo)簽,實(shí)現(xiàn)多個(gè)數(shù)據(jù)庫(kù)升級(jí)sql_createTable標(biāo)簽,表示當(dāng)前最新對(duì)應(yīng)的數(shù)據(jù)表要操作的內(nèi)容,可多組標(biāo)簽,實(shí)現(xiàn)多表升級(jí)updateStep標(biāo)簽,表示不同版本要升級(jí)的對(duì)象,可多組標(biāo)簽,達(dá)到不同版本數(shù)據(jù)庫(kù)升級(jí)到最新數(shù)據(jù)庫(kù)的效果updateDb標(biāo)簽,表示舊版本要修改的對(duì)應(yīng)數(shù)據(jù)庫(kù)sql_before標(biāo)簽,表示數(shù)據(jù)庫(kù)升級(jí)時(shí)優(yōu)先級(jí)最高的SQL,(在新表創(chuàng)建前執(zhí)行)sql_after標(biāo)簽,表示數(shù)據(jù)庫(kù)升級(jí)時(shí)優(yōu)先級(jí)最低并按順序執(zhí)行的SQL(在新表創(chuàng)建后執(zhí)行)
3、創(chuàng)建XML解析器
3.1 對(duì)應(yīng)工具類 DomUtils.class
public class DomUtils {
/**
* 讀取升級(jí)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;
}
/**
* 新表插入數(shù)據(jù)
*
* @param xml
* @param lastVersion 上個(gè)版本
* @param thisVersion 當(dāng)前版本
* @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 {
// 升級(jí)來(lái)源以逗號(hào)分隔
String[] lastVersionArray = step.getVersionFrom().split(",");
if (lastVersionArray != null && lastVersionArray.length > 0) {
for (int i = 0; i < lastVersionArray.length; i++) {
// 有一個(gè)配到update節(jié)點(diǎn)即升級(jí)數(shù)據(jù)
if (lastVersion.equalsIgnoreCase(lastVersionArray[i]) && step.getVersionTo().equalsIgnoreCase(thisVersion)) {
thisStep = step;
break;
}
}
}
}
}
return thisStep;
}
/**
* 解析出對(duì)應(yīng)版本的建表腳本
*
* @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中逗號(hào)分隔
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;
}
}
3.2 對(duì)應(yīng)XML的實(shí)體類
UpdateDbXml
/**
* @ClassName: UpdateDbXml
* @Description: 升級(jí)更新數(shù)據(jù)庫(kù)
*
*/
public class UpdateDbXml {
/**
* 升級(jí)腳本列表
*/
private List<UpdateStep> updateSteps;
/**
* 升級(jí)版本
*/
private List<CreateVersion> createVersions;
public UpdateDbXml(Document document) {
{
// 獲取升級(jí)腳本
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 各個(gè)升級(jí)的版本:"+ele.toString());
UpdateStep step = new UpdateStep(ele);
this.updateSteps.add(step);
}
}
{
/**
* 獲取各升級(jí)版本
*/
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","各個(gè)升級(jí)的版本:"+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: 數(shù)據(jù)庫(kù)升級(jí)腳本信息
*/
public class UpdateStep
{
/**
* 舊版本
*/
private String versionFrom;
/**
* 新版本
*/
private String versionTo;
/**
* 更新數(shù)據(jù)庫(kù)腳本
*/
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: 更新數(shù)據(jù)庫(kù)腳本
*
*/
public class UpdateDb
{
/**
* 數(shù)據(jù)庫(kù)名稱
*/
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;
/**
* 創(chuàng)建數(shù)據(jù)庫(kù)表腳本
*/
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: 創(chuàng)建數(shù)據(jù)庫(kù)腳本
*
*/
public class CreateDb
{
/**
* 數(shù)據(jù)庫(kù)表名
*/
private String name;
/**
* 創(chuàng)建表的sql語(yǔ)句集合
*/
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、萬(wàn)事俱備只欠東風(fēng): 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內(nèi)容轉(zhuǎn)化為對(duì)應(yīng)對(duì)象
UpdateDbXml updateDbxml = DomUtils.readDbXml(context);
// 下載 上一個(gè)版本 --》下一個(gè)版本 【注:在下載時(shí),需要將舊版本、新版本以逗號(hào)的形式寫(xiě)入文件緩存】
String[] versions = FileUtil.getLocalVersionInfo(new File(parentFile,
"update.txt"));
String lastVersion = versions[0];//拿到上一個(gè)版本
String thisVersion = versions[1];//拿到當(dāng)前版本
//數(shù)據(jù)庫(kù)File原地址
String userFile = ContUtils.sqliteDatabasePath;
//數(shù)據(jù)庫(kù)File備份地址
String user_bak = ContUtils.copySqliteDatabasePath;
//升級(jí)前,數(shù)據(jù)庫(kù)File備份
FileUtil.CopySingleFile(userFile, user_bak);
//根據(jù)對(duì)應(yīng)新舊版本號(hào)查詢XML轉(zhuǎn)化對(duì)象里面的腳本,得到對(duì)應(yīng)升級(jí)腳本
UpdateStep updateStep = DomUtils.findStepByVersion(updateDbxml, lastVersion, thisVersion);
if (updateStep == null) {
return;
}
//拿到對(duì)應(yīng)升級(jí)腳本
List<UpdateDb> updateDbs = updateStep.getUpdateDbs();
try {
//將原始數(shù)據(jù)庫(kù)中所有的表名 更改成 bak_表名(數(shù)據(jù)還在)
executeBeforesSql(updateDbs);
//檢查新表,創(chuàng)建新表
CreateVersion createVersion = DomUtils.findCreateByVersion(updateDbxml, thisVersion);
executeCreateVersion(createVersion);
//將原來(lái)bak_表名 的數(shù)據(jù)遷移到 新表中
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();
//執(zhí)行數(shù)據(jù)庫(kù)語(yǔ)句
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;");
}
// 創(chuàng)建數(shù)據(jù)庫(kù)表sql
List<String> sqls = cd.getSqlCreates();
SQLiteDatabase sqlitedb = getDb();
executeSql(sqlitedb, sqls);
sqlitedb.close();
}
}
//所有的表名 更改成 bak_表名(數(shù)據(jù)還在)
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();
//執(zhí)行數(shù)據(jù)庫(kù)語(yǔ)句
executeSql(sqlitedb, sqls);
sqlitedb.close();
}
}
private SQLiteDatabase getDb() {
String dbfilepath = null;
SQLiteDatabase sqlitedb = null;
dbfilepath = ContUtils.sqliteDatabasePath;// logic對(duì)應(yīng)的數(shù)據(jù)庫(kù)路徑
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) {
// 檢查參數(shù)
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, "執(zhí)行sql:" + sql, false);
sqlitedb.execSQL(sql);
} catch (SQLException e) {
}
}
}
sqlitedb.setTransactionSuccessful();
sqlitedb.endTransaction();
}
}
到此這篇關(guān)于Android 架構(gòu)之?dāng)?shù)據(jù)庫(kù)框架升級(jí)的文章就介紹到這了,更多相關(guān)Android 架構(gòu)之?dāng)?shù)據(jù)庫(kù)框架內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android基于opencv實(shí)現(xiàn)多通道分離與合并
針對(duì)圖像多通道的分離與混合,OpenCV 4中提供了split()函數(shù)和merge()函數(shù)用于解決這些需求。本文講解一下Android如何調(diào)用這些函數(shù)實(shí)現(xiàn)多通道分離與合并2021-06-06
Android 退出應(yīng)用程序的實(shí)現(xiàn)方法
這篇文章主要介紹了Android 退出應(yīng)用程序的實(shí)現(xiàn)方法的相關(guān)資料,需要的朋友可以參考下2017-04-04
解決Error:All flavors must now belong to a named flavor dimens
這篇文章主要介紹了解決Error:All flavors must now belong to a named flavor dimension. Learn more at https://d.android.com,需要的朋友可以參考下2017-11-11

