android sqlite activity跳转出错,Android sqlite 跨版本升级时复现报错 duplicate column

背景

最近在bugly上看到少量的duplicate column 类似报错。准备看下数据库相关的代码。咋一看没什么问题,但仔细看数据库升级的代码和提交日志发现了一个非常容易犯的错误。什么错误?报错很明了,就是某个数据库中的列重复了。

场景复现

1,假设数据在version1的时候创建了一个表person:

public class DataBaseOpenHelper extends SQLiteOpenHelper {

private final static String DATABASE_NAME = "test.db";

private static DataBaseOpenHelper mDataBaseOpenHelper;

//version1

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, SQLiteDatabase.CursorFactory factory,

int version) {

super(context, name, factory, version);

}

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) {

}

}

2,升级到version2的时候又创建一个表student:

import android.content.Context;

import android.database.sqlite.SQLiteDatabase;

import android.database.sqlite.SQLiteOpenHelper;

import android.util.Log;

public class DataBaseOpenHelper extends SQLiteOpenHelper {

private final static String DATABASE_NAME = "test.db";

private static DataBaseOpenHelper mDataBaseOpenHelper;

//version1

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, SQLiteDatabase.CursorFactory factory,

int version) {

super(context, name, factory, version);

}

static synchronized DataBaseOpenHelper getDBInstance(Context context) {

if (mDataBaseOpenHelper == null) {

mDataBaseOpenHelper = new DataBaseOpenHelper(context, DATABASE_NAME, null, 2);

}

return mDataBaseOpenHelper;

}

@Override

public void onCreate(SQLiteDatabase db) {

db.execSQL(CREATE_PERSON);

//version2

db.execSQL(CREATE_STUDENT);

}

@Override

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

//数据库升级逻辑,记得要break

for (int i = oldVersion; i < newVersion; i++) {

switch (i) {

case 1:

db.execSQL(CREATE_STUDENT);

break;

default:

break;

}

}

}

}

3,升级到version3的时候,我们需要在student表中加一个新字段genderid integer。注意,version3如何做?

3.1 错误做法: 直接在CREATE_STUDENT 语句中添加genderid integer,然后在数据库升级中处理升级逻辑,代码:

import android.content.Context;

import android.database.sqlite.SQLiteDatabase;

import android.database.sqlite.SQLiteOpenHelper;

import android.util.Log;

public class DataBaseOpenHelper extends SQLiteOpenHelper {

private final static String DATABASE_NAME = "test.db";

private static DataBaseOpenHelper mDataBaseOpenHelper;

//version1

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),genderid integer)";

//version3

public static final String ALTER_STUDENT = "alter table student add column genderid integer";

public DataBaseOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory,

int version) {

super(context, name, factory, version);

}

static synchronized DataBaseOpenHelper getDBInstance(Context context) {

if (mDataBaseOpenHelper == null) {

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) {

for (int i = oldVersion; i < newVersion; i++) {

switch (i) {

case 1:

db.execSQL(CREATE_STUDENT);

break;

case 2:

//version3

db.execSQL(ALTER_STUDENT);

break;

default:

break;

}

}

}

}

至此,一切都非常完美。version2升级到version3,走的是onUpgrade逻辑,执行alert语句直接插入genderid列;直接安装version3走的是onCreate逻辑,直接创建新表student肯定包含genderid。

但当从version1直接升级到version3的时候就会报错!!! 根据逐级升级逻辑,首先走的是case1,执行语句CREATE_STUDENT(注意,此时的语句已经不是version2时候的原始语句,而是version3改动后加了genderid列的语句),因此,当逐级执行到case2时,再新增genderid列时候就报错。

正确的做法:当数据库升级过程中向原有数据库新增字段时候,不要直接改动原来创建表的sql语句,而是另外写包含新字段的语句

import android.content.Context;

import android.database.sqlite.SQLiteDatabase;

import android.database.sqlite.SQLiteOpenHelper;

import android.util.Log;

public class DataBaseOpenHelper extends SQLiteOpenHelper {

private final static String DATABASE_NAME = "test.db";

private static DataBaseOpenHelper mDataBaseOpenHelper;

//version1

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 static final String CREATE_STUDENT_NEW =

"create table student(studentid integer primary key autoincrement,name varchar(20)," +

"phone VARCHAR(12),genderid integer)";

//version3

public static final String ALTER_STUDENT = "alter table student add column genderid integer";

public DataBaseOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory,

int version) {

super(context, name, factory, version);

}

static synchronized DataBaseOpenHelper getDBInstance(Context context) {

if (mDataBaseOpenHelper == null) {

mDataBaseOpenHelper = new DataBaseOpenHelper(context, DATABASE_NAME, null, 3);

}

return mDataBaseOpenHelper;

}

@Override

public void onCreate(SQLiteDatabase db) {

db.execSQL(CREATE_PERSON);

// db.execSQL(CREATE_STUDENT); ///注意这里

db.execSQL(CREATE_STUDENT_NEW); ///注意这里

}

@Override

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

for (int i = oldVersion; i < newVersion; i++) {

switch (i) {

case 1:

db.execSQL(CREATE_STUDENT);

break;

case 2:

//version3

db.execSQL(ALTER_STUDENT);

break;

default:

break;

}

}

}

}

这样不管是从哪个旧版本升级还是直接安装最新版本都能不会报错了。ok,细节很重要。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值