冠亚体育手机网站SQLite数据库版本升级的管理实现,数据库升级与降级

Android SQLite数据库版本升级的管理实现

一、概述

我们知道在SQLiteOpenHelper的构造方法:

     
SQLite是Android内置的一个很小的关系型数据库。SQLiteOpenHelper是一个用来辅助管理数据库创建和版本升级问题的抽象类。我们可以继承这个抽象类,实现它的一些方法来对数据库进行自定义操作。下面两个方法必须重写:

super(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) 

public void onCreate(SQLiteDatabase db)

中最后一个参数表示数据库的版本号.当新的版本号大于当前的version时会调用方法:

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

onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) 

SQLiteOpenHelper里的onCreate执行:

所以我们的重点是在该方法中实现SQLite数据库版本升级的管理

1.只在数据库还没有建立时运行一次.如果数据库已经存在了,就不再执行onCreate方法,即在第一次打开数据库的时候才会执行

当我们项目刚开始的时候第一版SQLiteOpenHelper是这样写的:

2.在清除数据之后再次运行–>打开数据库,这个方法会执行

package cc.database; 

import android.content.Context; 
import android.database.sqlite.SQLiteDatabase; 
import android.database.sqlite.SQLiteDatabase.CursorFactory; 
import android.database.sqlite.SQLiteOpenHelper; 
/** 
 * Demo描述: 
 * SQLite数据库版本升级的管理实现 
 * 
 * 参考资料: 
 * http://blog.csdn.net/guolin_blog 
 * Thank you very much 
 */ 
public class DataBaseOpenHelper extends SQLiteOpenHelper { 
  private final static String DATABASE_NAME="test.db"; 
  private static DataBaseOpenHelper mDataBaseOpenHelper; 

  public static final String CREATE_PERSON= 
  "create table person(personid integer primary key autoincrement,name varchar(20),phone VARCHAR(12))"; 

  public DataBaseOpenHelper(Context context,String name,CursorFactory factory,int version) { 
    super(context, name, factory, version); 
  } 


  //注意: 
  //将DataBaseOpenHelper写成单例的. 
  //否则当在一个for循环中频繁调用openHelper.getWritableDatabase()时 
  //会报错,提示数据库没有执行关闭操作 
  static synchronized DataBaseOpenHelper getDBInstance(Context context) { 
    if (mDataBaseOpenHelper == null) { 
      mDataBaseOpenHelper = new DataBaseOpenHelper(context,DATABASE_NAME,null,1); 
    } 
    return mDataBaseOpenHelper; 
  }  

  @Override 
  public void onCreate(SQLiteDatabase db) { 
    db.execSQL(CREATE_PERSON); 
  } 

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

  } 

} 

3.没有清除数据,不会执行这个方法

在几天之后根据项目需求,需要添加一张student表,于是DataBaseOpenHelper就出现了第二版:

4.数据库升级的时候这个方法不会执行,想执行需要先删除数据,再调用onCreate方法

package cc.database; 

import android.content.Context; 
import android.database.sqlite.SQLiteDatabase; 
import android.database.sqlite.SQLiteDatabase.CursorFactory; 
import android.database.sqlite.SQLiteOpenHelper; 

public class DataBaseOpenHelper extends SQLiteOpenHelper { 
  private final static String DATABASE_NAME="test.db"; 
  private static DataBaseOpenHelper mDataBaseOpenHelper; 

  public static final String CREATE_PERSON= 
  "create table person(personid integer primary key autoincrement,name varchar(20),phone VARCHAR(12))"; 

  public static final String CREATE_STUDENT= 
  "create table student(studentid integer primary key autoincrement,name varchar(20),phone VARCHAR(12))"; 

  public DataBaseOpenHelper(Context context,String name,CursorFactory factory,int version) { 
    super(context, name, factory, version); 
  } 


  //注意: 
  //将DataBaseOpenHelper写成单例的. 
  //否则当在一个for循环中频繁调用openHelper.getWritableDatabase()时 
  //会报错,提示数据库没有执行关闭操作 
  static synchronized DataBaseOpenHelper getDBInstance(Context context) { 
    if (mDataBaseOpenHelper == null) { 
      //改动1 
      mDataBaseOpenHelper = new DataBaseOpenHelper(context,DATABASE_NAME,null,2); 
    } 
    return mDataBaseOpenHelper; 
  }  

  @Override 
  public void onCreate(SQLiteDatabase db) { 
    db.execSQL(CREATE_PERSON); 
    //改动2 
    db.execSQL(CREATE_STUDENT); 
  } 

  @Override 
  public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 
    //改动3 
    switch (oldVersion) { 

    case 1: 
    db.execSQL(CREATE_STUDENT); 

    default: 
    } 
  } 

} 

onUpgrade的执行:

较版本一在版本二中有三处修改的地方:

1.第一次创建数据库的时候,这个方法不会执行

1 版本号变成了2

2.清除数据后再次运行(相当于第一次创建)这个方法不会执行

2
在onCreate()方法中添加了代码db.execSQL(CREATE_STUDENT);创建student表

3.数据库已经存在,而且版本升高的时候,这个方法才会调用,但不会自动执行onCreate

  
因为有的用户根本就没有第一版本的APP,直接从市场下载了第二版本的App。所以当然会执行onCreate()而不会执行onUpgrade()

onDowngrade的执行:

3
在onUpgrade()做了处理:当oldVersion为1时调用db.execSQL(CREATE_STUDENT);创建student表
  
因为有的用户手机上本来就有第一版本的APP,所以在App升级到第二版本时会执行onUpgrade(),不会执行onCreate()

1.执行数据库的降级操作

通过这样的处理使得不同的情况下使用第二版APP时都会生成student表

2.只有新版本比旧版本低的时候才会执行

又过了一个月,根据项目变更,需要给person表添加一个字段genderid,于是DataBaseOpenHelper就出现了第三版:

3.如果不执行降级操作,会抛出异常

package cc.database; 

import android.content.Context; 
import android.database.sqlite.SQLiteDatabase; 
import android.database.sqlite.SQLiteDatabase.CursorFactory; 
import android.database.sqlite.SQLiteOpenHelper; 

public class DataBaseOpenHelper extends SQLiteOpenHelper { 
  private final static String DATABASE_NAME="test.db"; 
  private static DataBaseOpenHelper mDataBaseOpenHelper; 

  //改动1 
  public static final String CREATE_PERSON= 
  "create table person(personid integer primary key autoincrement,name varchar(20),phone VARCHAR(12)),genderid integer)"; 

  public static final String ALTER_PERSON="alter table person add column genderid integer"; 

  public static final String CREATE_STUDENT= 
  "create table student(studentid integer primary key autoincrement,name varchar(20),phone VARCHAR(12))"; 

  public DataBaseOpenHelper(Context context,String name,CursorFactory factory,int version) { 
    super(context, name, factory, version); 
  } 


  //注意: 
  //将DataBaseOpenHelper写成单例的. 
  //否则当在一个for循环中频繁调用openHelper.getWritableDatabase()时 
  //会报错,提示数据库没有执行关闭操作 
  static synchronized DataBaseOpenHelper getDBInstance(Context context) { 
    if (mDataBaseOpenHelper == null) { 
      //改动2 
      mDataBaseOpenHelper = new DataBaseOpenHelper(context,DATABASE_NAME,null,3); 
    } 
    return mDataBaseOpenHelper; 
  }  

  @Override 
  public void onCreate(SQLiteDatabase db) { 
    db.execSQL(CREATE_PERSON); 
    db.execSQL(CREATE_STUDENT); 
  } 

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

    case 1: 
    db.execSQL(CREATE_STUDENT); 

    //改动3 
    case 2: 
    db.execSQL(ALTER_PERSON); 

    default: 
    } 
  } 

} 

 

较版本二在版本三中有三处修改的地方:

二、升级数据库

1 改变了CREATE_PERSON语句,在改语句中增加了一个字段genderid 

 1 public class MyDatabaseHelper extends SQLiteOpenHelper{
 2 
 3     public static final String CREATE_BOOK="create table BOOK("
 4             +"id integer primary key autoincrement,"
 5             +"author text,"
 6             +"price real,"
 7             +"pages integer,"
 8             +"name text,"
 9             +"category_id integer)";
10     public static final String CREATE_CATECORY="create table Category("
11             +"id integer primary key autoincrement,"
12             +"category_name text"
13             +"category_code integer)";
14     private Context mContext;
15     public MyDatabaseHelper(Context context, String name,
16             CursorFactory factory, int version) {
17         super(context, name, factory, version);
18         mContext=context;
19     }
20 
21     @Override//创建数据库是调用
22     public void onCreate(SQLiteDatabase db) {
23         db.execSQL(CREATE_BOOK);
24         db.execSQL(CREATE_CATECORY);
25         Toast.makeText(mContext, "Create succeeded", Toast.LENGTH_SHORT).show();
26     }
27 
28     @Override//版本更新时调用
29     public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
30         //Log.i("db","测试");
31         switch(oldVersion){
32         case 1://版本1升级版本二是执行上面的创建表的语句
33             db.execSQL(CREATE_CATECORY);
34             Log.i("db","创建成功表Category成功");
35         case 2://版本2升级版本3时执行向Book表中添加一个字段
36             db.execSQL("alter table Book add column category_id integer");
37             Log.i("db","添加字段成功");
38             default:
39         }
40     }
41 }

  和前面的描述类似,有的用户第一次安装该APP时就直接下载了第三版

   可以看到,在 onCreate()方法里我们新增了一条建表语句,然后又在
onUpgrade()方法中添加了一个
switch 判断,如果用户当前数据库的版本号是
1,就只会创建一张 Category 表。这样当用户是直接安装的第二版的程序时,就会将两张表一起创建。而当用户是使用第二版的程序覆盖安装第一版的程序时,就会进入到升级数据库的操作中,此时由于
Book 表已经存在了,因此只需要创建一张
Category 表即可。

2 修改版本号为3

   可以看到,首先我们在
Book 表的建表语句中添加了一个
category_id 列,这样当用户直接安装第三版的程序时,这个新增的列就已经自动添加成功了。然而,如果用户之前已经安装了某一版本的程序,现在需要覆盖安装,就会进入到升级数据库的操作中。在
onUpgrade() 方法里,我们添加了一个新的 case,如果当前数据库的版本号是 2,就会执行 alter 命令来为Book 表新增一个 category_id
列。

   应对了用户从第一版本或者第二版本升级到第三版本的情况(见下分析)

       这里请注意一个非常重要的细节,switch 中每一个 case 的最后都是没有使用 break 的,为什么要这么做呢?这是为了保证在跨版本升级的时候,每一次的数据库修改都能被全部执行到。比如用户当前是从第二版程序升级到第三版程序的,那么
case 2 中的逻辑就会执行。而如果用户是直接从第一版程序升级到第三版程序的,那么
case 1 和 case 2 中的逻辑都会执行。使用这种方式来维护数据库的升级,不管版本怎样更新,都可以保证数据库的表结构是最新的,而且表中的数据也完全不会丢失了。

3 在onUpgrade()方法中)做了处理:当oldVersion为2时调用
db.execSQL(ALTER_PERSON);修改person表,增加genderid字段

三、降级

   应对了用户从第二版本升级到第三版本的情况(见下分析)

 1 private static final String DB_NAME = "mydata.db"; // 数据库名称
 2     private static final int version = 2; // 数据库版本
 3 
 4     public MyDatabaseOpenHelper(Context context) {
 5         super(context, DB_NAME, null, version);
 6     }
 7 
 8     // 问题:什么时候执行
 9     // 没有前生
10     @Override
11     public void onCreate(SQLiteDatabase db) {
12         // TODO Auto-generated method stub
13         // 编写【从0开始到最新状态】建表语句
14         Log.i("hi", "没有数据库,创建数据库,创建v2.0成功");
15         String sql_message = "create table t_message (id int primary key,tou1  varchar(50),userName varchar(50),lastMessage varchar(50),datetime  varchar(50))";
16         String sql_init_1 = "insert into t_message values (1,'abc','abc1','abcd1','hi1')";
17         String sql_init_2 = "insert into t_message values (2,'abc','abc2','abcd2','hi1')";
18         String sql_init_3 = "insert into t_message values (3,'abc','abc2','abcd2','hi1')";
19         db.execSQL(sql_message);
20         db.execSQL(sql_init_1);
21         db.execSQL(sql_init_2);
22         db.execSQL(sql_init_3);
23 
24     }
25 
26     // v2.0 现在进行时
27     @Override
28     public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
29         
30         if (oldVersion == 2){
31             String sql_upgrade_1 = "alter table t_message add column isdel bit default 0";
32             db.execSQL(sql_upgrade_1);
33             Log.i("db", "从2到3,升级成功!");
34         }
35 //        
36         if (oldVersion == 1) {
37             Log.i("db", "从1到3,升级成功!");
38             String sql_upgrade_1 = "alter table t_message add column isdel bit default 0";
39             db.execSQL(sql_upgrade_1);
40             String sql_init_1 = "insert into t_message values (1,'abc','abc1','abcd1','hi1',0)";
41             String sql_init_2 = "insert into t_message values (2,'abc','abc2','abcd2','hi1',0)";
42             String sql_init_3 = "insert into t_message values (3,'abc','abc2','abcd2','hi1',0)";
43             db.execSQL(sql_init_1);
44             db.execSQL(sql_init_2);
45             db.execSQL(sql_init_3);
46             Log.i("db", "从1到3,升级成功!");
47         }
48     }

 1 /* 模拟从3.0 降低会2.0 */
 2     @Override
 3     public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
 4         //正常来讲大于2.0的,应该有t_message 这张表,且2.0有的字段,3.0都有
 5         try {
 6             //第一、先把t_message 未来的表,改名
 7             String rename_sql = "alter table t_message rename to t_message_bak";
 8             db.execSQL(rename_sql);
 9             Log.i("down", "1.改名成功");
10             //第二、建立2.0的表结构
11             String sql_message = "create table t_message (id int primary key,tou1  varchar(50),userName varchar(50),lastMessage varchar(50),datetime  varchar(50))";
12             db.execSQL(sql_message);
13             Log.i("down", "2.建立2.0表结构成功");
14             //第三、把备份的数据,copy到 新建的2.0的表
15             String sql_copy = "insert into t_message select id,tou1,userName,lastMessage,datetime from t_message_bak";
16             db.execSQL(sql_copy);
17             Log.i("down", "3.copy到用户数据到 2.0的表");
18             //第四、把备份表drop掉
19             String drop_sql = "drop table if exists t_message_bak";
20             db.execSQL(drop_sql);
21             Log.i("down", "4.把备份表drop掉");
22             
23         } catch (Exception e) {
24             //失败
25             Log.i("hi", "降级失败,重新建立");
26             String sql_drop_old_table = "drop table if exists t_message";
27             String sql_message = "create table t_message (id int primary key,tou1  varchar(50),userName varchar(50),lastMessage varchar(50),datetime  varchar(50))";
28             String sql_init_1 = "insert into t_message values (1,'abc','abc1','abcd1','hi1')";
29             String sql_init_2 = "insert into t_message values (2,'abc','abc2','abcd2','hi1')";
30             String sql_init_3 = "insert into t_message values (3,'abc','abc2','abcd2','hi1')";
31             db.execSQL(sql_drop_old_table);
32             db.execSQL(sql_message);
33             db.execSQL(sql_init_1);
34             db.execSQL(sql_init_2);
35             db.execSQL(sql_init_3);
36         }
37     }

注意一个问题:为什么这里的switch语句在每个case中没有break???

 代码说明,这就不说了!

这是为了保证跨版本升级的时候每次数据库的升级都会执行到。

比如从第二版升级到第三版本,那么case 2会被执行。

比如从第一版直接升级到第三版本,那么case
1肯定会被调用,由于没有break所以会穿透switch语句又执行case
2语句继续升级,从而保证了数据的所有版本中的升级都会被执行到。

如有疑问请留言或者到本站社区交流讨论,感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

您可能感兴趣的文章:

  • Android中Activity和Fragment传递数据的两种方式
  • Android
    ListView填充数据的方法
  • 浅谈Android轻量级的数据缓存框架RxCache
  • Android中Retrofit
    2.0直接使用JSON进行数据交互
  • 冠亚体育手机网站,Android
    提交或者上传数据时的dialog弹框动画效果
  • Android常用的数据加密方式代码详解

Post Author: admin

发表评论

电子邮件地址不会被公开。 必填项已用*标注