一.数据库升级:

  在我们的程序中,或多或少都会涉及到数据库,使用数据库必定会涉及到数据库的升级,数据库升级带来的一些问题,如旧版本数据库的数据记录的保持,对新表的字段的添加等等一系列问题,还记得当我来西安的时候,面试的第二家公司,做音乐播放客户端的,就问到了这个问题;

  我们开发了一个程序,当前是1.0版本。该程序用到了数据库。到1.1版本时,在数据库的某个表中增加了一个字段。那么软件1.0版本用的数据库在软件1.1版本就要被升级了。软件的1.0版本升级到1.1版本时,老的数据不能丢。那么在1.1版本的程序中就要有地方能够检测出来新的软件版本与老的数据库不兼容,并且把1.0软件的数据库升级到1.1软件能够使用的数据库。也就是说,要在1.0软件的数据库的那个表中增加那个字段,并赋予这个字段默认值。

  程序如何知道我们的数据库需要升级呢?SQLiteOpenHelper类的构造函数有一个参数是version即数据库版本号。比如在软件1.0版本中,我们使用SQLiteOpenHelper访问数据库时,该参数为1,那么数据库版本号1就会写在我们的数据库中。到了1.1版本,我们的数据库需要发生变化,那么我们1.1版本的程序中就要使用一个大于1的整数来构造SQLiteOpenHelper类,用于访问新的数据库,比如2。当我们的1.1新程序读取1.0版本的老数据库时,就发现老数据库里存储的数据库版本是1,而我们新程序访问它时填的版本号为2,系统就知道数据库需要升级。

  当系统在构造SQLiteOpenHelper类的对象时,如果发现版本号不一样,就会自动调用onUpgrade函数,在这个方法里对数据库进行升级。在这个函数中把老版本数据库的相应表中增加字段,并给每条记录增加默认值即可。新版本号和老版本号都会作为onUpgrade函数的参数传进来,便于开发者知道数据库应该从哪个版本升级到哪个版本。升级完成后,数据库会自动存储最新的版本号为当前数据库版本号。

  SQLite提供了ALTER TABLE命令,允许用户重命名或添加新的字段到已有表中,但是不能从表中删除字段。并且只能在表的末尾添加字段,比如,为Test添加一个字段:"ALTER TABLE Test ADDCOLUMN age"
下面是我的一个测试:

public class DataSQL extends SQLiteOpenHelper {
public DataSQL(Context context, int version) {
super(context, "Test", null, version);
} @Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("create table test(id integer primary key autoincrement, name)");
} @Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (newVersion == 2) {
db.execSQL("ALTER TABLE test ADD COLUMN age");
Cursor cr = db.rawQuery("select * from test", null);
while (cr.moveToNext()) {
String name = cr.getString(cr.getColumnIndex("name"));
ContentValues values = new ContentValues();
values.put("name", name);
values.put("age", 23);
db.update("test", values, "name=?", new String[] { name });
}
}
}
}

添加数据及读取数据

        DataSQL sql = new DataSQL(this, 2);
SQLiteDatabase db = sql.getWritableDatabase();
Cursor cr = db.query("test", null, null, null, null, null, null);
while (cr.moveToNext()) {
Log.e("cr-name", "" + cr.getString(cr.getColumnIndex("name")));
Log.e("cr-age", "" + cr.getInt(cr.getColumnIndex("age")));
}

我只添加了一条数据,可以看到这条数据被打印出来了.

如果遇到复杂的修改操作,比如在修改的同时,需要进行数据的转移,那么可以采取在一个事务中执行如下语句来实现修改表的需求。
  1. 将表名改为临时表
         ALTERTABLE Test RENAME TO _Test;
  2. 创建新表
        CREATETABLE Test(id VARCHAR(32) PRIMARYKEY ,UserName VARCHAR(32) NOTNULL , Age VARCHAR(16) NOTNULL);
      3. 导入数据  
         INSERTINTO Test SELECT id, “”, Age FROM _Test;
  或者  
        INSERTINTO Test() SELECT id, “”, Age FROM _Test;
  * 注意 双引号”” 是用来补充原来不存在的数据的!
      4. 删除临时表  
        DROPTABLE _Test;
  通过以上四个步骤,就可以完成旧数据库结构向新数据库结构的迁移,并且其中还可以保证数据不会因为升级而流失。
当然,如果遇到减少字段的情况,也可以通过创建临时表的方式来实现。

下面仍然通过一个例子来进行测试:

  1.修改DataSQL的onUpgrade方法

        if (newVersion == 3) {
char str = '"';
db.beginTransaction();
db.execSQL("ALTER TABLE test RENAME TO _Test");
db.execSQL("CREATE TABLE test(id integer primary key autoincrement , PassWord VARCHAR(20) NOT NULL,"
+ " UserName VARCHAR(32) NOT NULL , Age VARCHAR(16) NOT NULL)");
db.execSQL("INSERT INTO test SELECT id, " + str + str
+ ", name, age FROM _Test");
db.setTransactionSuccessful();
db.endTransaction();
}

  2.修改Activity中打印信息

            Log.e("cr-name", "" + cr.getString(cr.getColumnIndex("UserName")));
Log.e("cr-age", "" + cr.getInt(cr.getColumnIndex("Age")));
Log.e("cr-password", "" + cr.getInt(cr.getColumnIndex("PassWord")));

在实际开发工作中,我们的处理可能比上面所述的复杂;

  假如我们开发的程序已经发布了两个版本:V1.0,V1.2,我们正在开发V1.3。每一版的数据库版本号分别是8,9,10。对于这种情况,我们应该如何实现升级?
用户的选择有:

  1) V1.0 -> V1.3  DB 8 -> 10                 
  2) V1.2 -> V1.3  DB 9 -> 10     
  3)注意:数据库的每一个版本所代表的数据库必须是定义好的,比如说V1.0的数据库,它可能只有两张表TableA和TableB,如果V1.2要添加一张表TableC,如果V1.3要修改TableC,那么每一个版本所对应的数据库结构如下:
V1.0  --->  TableA, TableB
V1.2  --->  TableA, TableB, TableC
V1.3  --->  TableA, TableB, TableC (Modify)

    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
{
int upgradeVersion = oldVersion;
if (8 == upgradeVersion) {
// Create table C
String sql = "CREATE TABLE ...";
db.execSQL(sql);
upgradeVersion = 9;
}
if (9 == upgradeVersion) {
// Modify table C
upgradeVersion = 10;
}
if (upgradeVersion != newVersion) {
// Drop tables
db.execSQL("DROP TABLE IF EXISTS " + tableName);
// Create tables
onCreate(db);
}
}

在onUpgrade()方法中,处理了数据库版本从8 -> 10的升级过程,这样做的话,不论用户从8 -> 10,还是从9 - 10,最终程序的数据库都能升级到V1.3所对应的数据库结构。

二.导入已有数据库

  在有的情况下,我们要在程序一开始运行的时候就导入某些固定的数据,而这些数据过大,又不可能直接代码写死,此时就需要通过导入已有数据库的方法导入数据,我们知道raw文件夹下的东西,android会原封不动的拷贝到程序中,而不会转换为二进制文件,所以,我们把数据库放到raw文件夹下供程序导入使用;

public class DBImporter {
public static final String PACKAGE_NAME = "com.example.sql";
public static final String DB_NAME = "xxx.db";
public static String DB_PATH = "/data/data/" + PACKAGE_NAME;
private Context context; public DBImporter(Context mContext) {
this.context = mContext;
} public SQLiteDatabase openDataBase() {
return SQLiteDatabase.openOrCreateDatabase(DB_PATH + "/" + DB_NAME, null);
} public void copyDB() {
File file = new File(DB_PATH + "/" + DB_NAME);
if (!file.exists()) {
try {
FileOutputStream out = new FileOutputStream(file);
int buffer = 400000;
          // 读取数据库并保存到data/data/packagename/xx.db...
InputStream ins = context.getResources().openRawResource(R.raw.sql_);
byte[] bts = new byte[buffer];
int length;
while ((length = ins.read(bts)) > 0) {
out.write(bts, 0, bts.length);
}
out.close();
ins.close();
SQLiteDatabase.openOrCreateDatabase(DB_PATH + "/" + DB_NAME, null);
} catch (FileNotFoundException e) {
} catch (IOException e) {
}
}
}
}

接下来便是在需要使用到该数据库的地方调用

        DBImporter importer = new DBImporter(this);
importer.copyDB();
SQLiteDatabase db = importer.openDataBase(); // 获取到数据库对象,接下来便可操作了~

Android 数据库升级中数据保持和导入已有数据库的更多相关文章

  1. Android中数据存储(三)——SQLite数据库存储数据

    当一个应用程序在Android中安装后,我们在使用应用的过程中会产生很多的数据,应用都有自己的数据,那么我们应该如何存储数据呢? 数据存储方式 Android 的数据存储有5种方式: 1. Share ...

  2. Android数据库升级,数据不丢失解决方案

    假设要更新TableC表,建议的做法是: 1) 将TableC重命名为TableC_temp SQL语句可以这样写:ALERT TABLE TableC RENAME TO TableC_temp; ...

  3. Android开发8:数据存储(二)——SQLite数据库和ContentProvider的使用

    前言 啦啦啦各位小伙伴们许久不见了~学期末和过年期间自己忙着做其他事没能及时更新Android开发系列课程的博客,实在是罪过罪过~ 好啦~废话不多说,进入我们今天的主题.今天我们将和大家学习其他的数据 ...

  4. mysql 导入 csv文件中数据,只能导入第一行

    用workbench导入csv数据,只能导入数据的第一行,也就是标注每一列的列名的那一行.但问题是,每次导入完成时,系统提示已经导入了500条记录(这个文件中的确有500条记录),可是刷新数据库后打开 ...

  5. 我们在删除SQL Sever某个数据库表中数据的时候,希望ID重新从1开始,而不是紧跟着最后一个ID开始需要的命令

    一.如果数据重要,请先备份数据 二.删除表中数据 SQL: Delete From ('表名')  如:Delete From abcd 三.执行新语句 SQL: dbcc checkident('表 ...

  6. Android开发——fragment中数据传递与刷新UI(更改控件)

    数据传递: 1.通过数据库进行数据的传递 如在fragment中将数据保存在数据库中,之后其他的fragment或者activity直接读取数据库中的数据,数据库使用还算简单,这里就不多说,建议使用l ...

  7. MySQL----DQL(查询数据库表中数据)

    ##DQL:查询表中的记录 1.语法: select 字段列名 from 表名列表 where 条件列表 group  by 分组字段 having  分组之后的条件 order  by 排序 lim ...

  8. Android数据库升级实例

    第一部分 Andoird的SQLiteOpenHelper类中有一个onUpgrade方法.帮助文档中只是说当数据库升级时该方法被触发.经过实践,解决了我一连串的疑问: 1. 帮助文档里说的“数据库升 ...

  9. Android数据库升级、降级、创建(onCreate() onUpgrade() onDowngrade())[4]

    数据库版本升级对软件的管理操作. 我们手机经常会收到xxx软件升级什么的提醒,你的软件版本更新,同时你的数据库对应的版本也要相应的更新. 数据库版本更新需要主要的问题: 软件的1.0版本升级到1.1版 ...

随机推荐

  1. Ruby & Rails学习资料

    ------------------教程------------- Ruby风格指南(代码规范) https://github.com/bbatsov/ruby-style-guide 笨方法學 Ru ...

  2. xencenter迁移云主机方法

    问题:POOL中计算节点内存不足. 解决方法:1.为计算节点添加内存(费用高)2.将部分资源迁移到其它POOL中. 方法: 1.选择要迁移的虚拟机 2.选择保存路径 这里可以看到可以批量导出: 注意: ...

  3. 根据wsdl生成服务端代码

    场景描述 最近在和一家公司做业务接口对接,由他们那边回调我们这边,对方直接扔过来一个webservice的wsdl文件,让我们按照他们的规范来做webservice服务, 大多数的对接应该是我们创建完 ...

  4. ReadyAPI教程和示例(一)

    声明:如果你想转载,请标明本篇博客的链接,请多多尊重原创,谢谢! 本篇使用的 ReadyAPI版本是2.5.0 通过下图你可以快速浏览一下主要的ReadyAPI中SoapUI功能: ​ 一.创建一个功 ...

  5. es6笔记4^_^function

    一.function默认参数 现在可以在定义函数的时候指定参数的默认值了,而不用像以前那样通过逻辑或操作符来达到目的了. es5 function sayHello(name){ //传统的指定默认参 ...

  6. 【转】unity3d 在UGUI中制作自适应调整大小的滚动布局控件

    转自 http://blog.csdn.net/rcfalcon/article/details/43459387 在游戏中,我们很多地方需要用到scroll content的概念:我们需要一个容器, ...

  7. Python决定一个变量时局部的,还是全局的,是在编译期

    Python中的变量名是在编译时就解析好的,换句话说,在编译时(也就是在交互控制台输入代码是或者import文件时),Python就已经决定一个变量应该是局部变量,还是全局变量.来看下面的例子: &g ...

  8. 常用算法Java实现之选择排序

    选择排序算法在每一步中选取最小值来重新排序,通过选择和交换来实现排序. 具体流程如下: 1.首先从原数组中选择最小的1个数据,将其置于第一个位置. 2.然后从剩下的数据中再选择其中最小的一个数据,并将 ...

  9. LintCode-5.第k大元素

    第k大元素 在数组中找到第k大的元素 注意事项 你可以交换数组中的元素的位置 样例 给出数组 [9,3,2,4,8],第三大的元素是 4 给出数组 [1,2,3,4,5],第一大的元素是 5,第二大的 ...

  10. 用SC命令 添加或删除windows服务提示OpenSCManager 失败5 拒绝访问

    在安装命令行中安装  windowsOpenSCManager 失败5  的错误,原因是当前用户的权限不足,需要做的是在注册表 HKEY_LOCAL_MACHINE\Software\Microsof ...