Android 数据库进阶之数据库升级连带保留数据

今天得空,写一篇文章,是关于Android数据库的。每个应用都需要对数据进行存储和更改,当然一些简单的数据,数据比较少,就不必用数据库存数,可以用Android提供的SharedPreferences,这个相对数据库存取数据来说消耗的资源更少,而且存储方式更容易让人理解。Android使用的是开源的、与操作系统无关的SQL数据库—SQLite。它是一款轻量级数据库,它的设计目标是嵌式的,占用资源同样也非常的低。

简单的介绍了下Android用的数据库,但是今天我不会给大家带来数据库的创建和存储数据等操作的实现,Sql语句比较好写,而且相信很多人也会创建数据库的,我这里带来的问题解决,主要是针对数据库创建完之后,APP已经成功上线,可是由于产品的需求,需要针对数据库中的表进行字段的增加,也就是需要进行数据库升级。

我们创建数据库的时候一般都是继承SQLiteOpenHelper,该类用于对数据库版本进行管理,该类是一个抽象类,必须继承它才能使用,为了实现对数据库版本进行管理,SQLiteOpenHelper类有两种重要的方法,分别是onCreate(SQLiteDatabase db) 和  onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion),当调用SQLiteOpenHelper的getWritableDatabase()或者getReadableDatabase()方法(PS:getWritableDatabase()和getReadableDatabase()方法都可以获取一个用于操作数据库的SQLiteDatabase实例,但getWritableDatabase()方法以读写方式打开数据库,一旦数据库的磁盘空间满了,数据库就只能读而不能写,倘若使用的是getWritableDatabase()方法就会出错, getReadableDatabase()方法先以读写方式打开数据库,如果数据库的磁盘空间满了,就会打开失败,当打开失败后会继续尝试以只读方式打开数据库),获取用于操作数据库的SQLiteDatabase实例的时候,如果数据库不存在,Android系统会自动生成一个数据库,接着调用onCreate()方法,

public DBHelper extends SQLiteOpenHelper {

public static final int DB_VERSION = 5;//version 必须大于0,否则报错
public static final String DB_NAME = "test20131218.db";

public DBHelper (Context context){

super(context,DB_NAME,DB_VERSION);

}

@Override

    public void onCreate(SQLiteDatabase db) {
        /**
         * 这个方法
         * 1、在第一次打开数据库的时候才会走
         * 2、在清除数据之后再次运行-->打开数据库,这个方法会走
         * 3、没有清除数据,不会走这个方法
         * 4、数据库升级的时候这个方法不会走
         */
        Log.e("", "#############数据库创建##############" );
    }
在数据库升级的时候,会执行onUpgrade()方法,

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        /**
         * 1、第一次创建数据库的时候,这个方法不会走
         * 2、清除数据后再次运行(相当于第一次创建)这个方法不会走
         * 3、数据库已经存在,而且版本升高的时候,这个方法才会调用
         */
        Log.e("", "#############数据库升级##############" );
        
    }

数据库降级的时候调用,
    @Override
    public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        /**
         * 执行数据库的降级操作
         * 1、只有新版本比旧版本低的时候才会执行
         * 2、如果不执行降级操作,会抛出异常
         */
        Log.e("", "#############数据库降级##############");
        super.onDowngrade(db, oldVersion, newVersion);
    }

}

下面我们就来针对数据库升级举个例子,正常上线的数据库是V1.0,有8个column,但是现在版本升级,也要升级数据库V1.1,要追加2个column,现在我们该怎么办?问题既然来了,那我们就给出解决方案。首先第一种方案:(适合复杂的数据表操作)

这是我们原有的数据库表:

 CREAT_CARD="create table IF NOT EXISTS " + CARD + "(" + CardExportColumns.DATE + " char(16)," + CardExportColumns.FILE_NAME + " char(32)," + CardExportColumns.FILE_URI + " char(50)" + ");"

现在我要在这里面加一个time的标签,

CREAT_CARD="create table IF NOT EXISTS " + CARD + "(" + CardExportColumns.DATE + " char(16)," + CardExportColumns.FILE_NAME + " char(32)," + CardExportColumns.FILE_URI + " char(50)" + CardExportColumns.CREATE_TIME +" long" + ");"

大概思路:

1.将表名重命名

2.创建新表

3.将数据插入新表

4.删除临时表

现在我们用代码具体实现下:

CREATE_TEMP_CARD = "alter table card rename to temp_card";//将表重命名

CREAT_CARD="create table IF NOT EXISTS " + CARD + "(" + CardExportColumns.DATE + " char(16)," + CardExportColumns.FILE_NAME + " char(32),"
+ CardExportColumns.FILE_URI + " char(50)" + CardExportColumns.CREATE_TIME +" long" + ");"//创建新表

INSERT_DATA_INTO = "insert into card select *,"" from temp_card";//(注意' '是为新加的字段插入默认值的必须加上,否则就会出错)

DROP_TEMP = "drop table temp_card"; // 删除临时表

public class DbBase extends SQLiteOpenHelper {

@Override

public void onCreate(SQLiteDatabase db) {

  db.execSQL(CREATE_CARD);

}

 @Override

 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

if(newVersiong>oldVersion){

  db.execSQL(CREATE_TEMP_CARD);
  db.execSQL(CREATE_CARD);
  db.execSQL(INSERT_DATA_INTO);
  db.execSQL(DROP_TEMP);

   }

}

}

这样我们就完成了新加入一些字段,升级数据库,并保留之前的数据,这种方式比较推荐有复杂的操作,比如在修改的同时,还要进行数据的转移,或者删除某些字段,如果数据库表中数据比较多的情况下,建议大家采取事务操作,在一个事务中执行。

下面我再给大家提供一个方法,就是获得旧数据的字段值,

// 获取升级前表中的字段
protected String getColumnNames(SQLiteDatabase db, String tableName)
{
Cursor c = null;
try
{
c = db.rawQuery( "PRAGMA table_info(" + tableName + ")", null );
if (null != c)
{
String[] columnNames = new String[cursor.getColumnCount()];

int count =c.getColumnCount();
for ( int i = 0;i<count ;i++)
{
coulunNames[i] = c.getgetColumnName(i);
}
}
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
if (c != null)
{
c.close();
}
}

return sb.toString();
}
}

数据库升级的逻辑,我觉得是很重要的,产品在开发之前就要想好升级策略,这样才能保证数据不会丢失。

比如现在我们发布了两个版本:V1.0,V1.1现在准备发布V1.2版本,在onUpgrade中我们判断的到是1.2和1.1,分别对应的版本号是10,11,12但是可能还存在V1.0的用户,所以我们就要做好V1.0的用户升级,数据库的升级策略还是很重要的。

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

第二种解决方案(有一定的条件约束):

当然添加的时候也可以用Sqlite提供的Alter字段,来增加字段或者更新字段,但是不能删除Column。增加字段,比如"Alter table " + CARD_TABLE_CARD + " add column " + CardColumns.OWNERSHIPOPT + " integer  default -1",但是必须满足一下限制:

字段不能有主键或唯一约束。

字段不能有这些缺省值:CURRENT_TIME, CURRENT_DATE 或CURRENT_TIMESTAMP

若定义了NOT NULL约束,则字段必须有一个非空的缺省值。

而且这样添加字段的话,新字段只能添加到表的末尾,而且只能增加有限子集,而且对一些复杂的操作,比如要更改又要移动数据,效率比较低,比较麻烦。对于复杂的操作还是推荐用重命名表的方式。





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值