一.SQLite简介和常用语法
二.数据库创建,升级及降级
上篇文章简介和常用语法介绍了SQLite数据库的基本信息和一些常用的语法操作,本篇文章主要介绍Android开发过程中SQLite数据库的创建使用和常见问题处理。
对于Android平台来说,我们可以使用系统提供的API轻松实现对SQLite数据库的操作
Android提供SQLiteOpenHelper类以便我们创建操作数据库。通常情况下我们会新建一个SQLiteOpenHelper的子类作为一个数据库的操作类。
数据库操作类示例 :
DatabaseHelper
public class DatabaseHelper extends SQLiteOpenHelper {
/**
* 数据库名称
*/
private static final String DATABASE_NAME = "account.db";
/**
* 数据库版本-升级用
*/
private static final int DATABASE_VERSION = 1;
public DatabaseHelper(Context context) {
//CursorFactory设置为null,使用默认值
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
/**
* 数据库第一次创建时调用
*/
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE IF NOT EXISTS account " +
"(userId VERCHAR PRIMARY KEY,userName VERCHAR,totalAccount INTEGER)");
}
/**
* @param db 数据库实际操作类
* @param oldVersion 旧版本
* @param newVersion 新版本
*/
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
数据库的创建以及版本,是由SQLiteOpenHelper的构造方法参数决定的。
SQLiteOpenHelper 有三个构造构造方法:
/**
* @param context 用来打开或者创建数据库
* @param name 数据库文件名称或者传null为一个内存数据库
* @param factory 用来创建cursor对象或者传null使用默认的
* @param version 数据库版本号 (从1开始);如果数据库版本比当前版旧,onUpgrade方法会被调用来更新数据库,
如果比当前新onDowngrade方法会被调用来降级数据库
*/
public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version) {
this(context, name, factory, version, null);
}
/**
* @param errorHandler DatabaseErrorHandler数据库损坏时回调.
*/
public SQLiteOpenHelper(Context context, String name, CursorFactory factory,
int version, DatabaseErrorHandler errorHandler) {
this(context, name, factory, version, 0, errorHandler);
}
/**
* @param minimumSupportedVersion 支持调用onUpgrade更新到version版本的最版本号,
如果当前版本比最小支持的版本还下数据库就会删除然后重新创建version版本的数据库。
在删除前onBeforeDelete会被调用我们可以在此方法中做一些处理。minimumSupportedVersion 默认为0
* @hide
*/
public SQLiteOpenHelper(Context context, String name, CursorFactory factory,
int version, int minimumSupportedVersion, DatabaseErrorHandler errorHandler) {
if (version < 1) throw new IllegalArgumentException("Version must be >= 1, was " +
version);
mContext = context;
mName = name;
mFactory = factory;
mNewVersion = version;
mErrorHandler = errorHandler;
mMinimumSupportedVersion = Math.max(0, minimumSupportedVersion);
}
最终都会调用第三个构造方法,方法中判断如果version 小于1会抛出异常,所以Android SQLite数据库版本必须从1开始。实际上最后一个构造方法被@hide注解了,所以一般情况下在子类中我们是没有办法主动调用的,只能调用前两个构造方法,minimumSupportedVersion 也始终是0。
数据库首次创建的时候onCreate方法会被执行。表的创建和初始化需要在方法中进行。这个是抽象方法,实体类中必须实现。实现很简单就是做一些创建表的操作。
示例:
/**
* 数据库第一次创建时调用
*/
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE IF NOT EXISTS account " +
"(userId VERCHAR PRIMARY KEY,userName VERCHAR,totalAccount INTEGER)");
}
/**
* @param db The database.
* @param oldVersion 旧数据库版本
* @param newVersion 新数据库版本
*/
public abstract void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion);
当数据库升级时(当前数据库版本大于已创建的数据库版本)会执行此方法,首次创建时不会执行。这是个抽象方法,在我们的实体类中必须实现。我们可以在这个方法中根据数据库版本做一些删除表,创建表,新增表字段或者重命名表等操作保证数据库与最新版本数据库同步。
常用数据库升级操作:
数据库的升级一般有三种方式如下:
1.onCreate方法始终保证是最新的,在onUpgrade方法中删除所有已存在表,在调用onCreate中创建表的方法重新创建所有表,保证表是最新的。(当然如果你想保存数据也可以先将数据保存到内存再插入,如果表很多或者数据量很大的是不建议怎么操作的,非常影响性能)
2.onCreate方法始终保证是最新的,再onUpgrade方法中根据版本号判断执行创建新表或者更新字段等操作
版本号需要 判断上下限:
上限(为了再从当前版本x更新到新版本y时不重复执行更新操作):假如我们要升级到新版本x,是我们的当前版本x,需要小于当前版本号x。
下限:如果是创建新表下限就是当前版本之前的所有版本,如果是更新表字段或者重命名表下限是表创建的版本需要大于等于下限版本
按照上面的方式我们需要更新表版本号为假设为2(可以为更大的,不要求连续,连续方便之后的版本更新迭代),在onCreate方法中修改原来创建表account的SQL新增字段subAccountId,同时新增创建表subAccount的SQL并执行。
然后在onUpgrade中判断oldVersion版本号大于等于原版本1小于现版本2,执行新增字段和新增表SQL。
具体代码如下:
public class DatabaseHelper extends SQLiteOpenHelper {
/**
* 数据库名称
*/
private static final String DATABASE_NAME = "account.db";
/**
* 数据库版本-升级用
*/
private static final int DATABASE_VERSION = 2;
public DatabaseHelper(Context context) {
//CursorFactory设置为null,使用默认值
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
/**
* 数据库第一次创建时调用
*/
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE IF NOT EXISTS account " +
"(userId VERCHAR PRIMARY KEY,userName VERCHAR,totalAccount INTEGER," +
"subAccountId VARCHAR default '')");
db.execSQL("CREATE TABLE IF NOT EXISTS subAccount " +
"(userId VERCHAR PRIMARY KEY,subAccountId VERCHAR,totalAccount INTEGER)");
}
/**
* @param db 数据库实际操作类
* @param oldVersion 旧版本
* @param newVersion 新版本
*/
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (oldVersion >= 1 && oldVersion < 2) {
db.execSQL("ALTER TABLE account ADD COLUMN subAccountId VARCHAR default ''");
db.execSQL("CREATE TABLE IF NOT EXISTS subAccount " +
"(userId VERCHAR PRIMARY KEY,subAccountId VERCHAR,totalAccount INTEGER)");
}
}
}
/**
* 数据库版本-升级用
* 版本2:新增数据库subAccount
*/
private static final int DATABASE_VERSION = 3;
public DatabaseHelper(Context context) {
//CursorFactory设置为null,使用默认值
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
/**
* 数据库第一次创建时调用
*/
@Override
public void onCreate(SQLiteDatabase db) {
createAccountTable(db);
createSubAccountTable(db);
}
/**
* 创建subAccount表(最好封装下,保证onCreate和onUpgrade方法中创建的都是最新表)
*/
private void createSubAccountTable(SQLiteDatabase db) {
db.execSQL("CREATE TABLE IF NOT EXISTS subAccount " +
"(userId VERCHAR PRIMARY KEY,subAccountId VERCHAR,totalAccount INTEGER," +
"userName VARCHAR default '')");
}
private void createAccountTable(SQLiteDatabase db) {
db.execSQL("CREATE TABLE IF NOT EXISTS account " +
"(userId VERCHAR PRIMARY KEY,userName VERCHAR,totalAccount INTEGER," +
"subAccountId VARCHAR default '')");
}
/**
* @param db 数据库实际操作类
* @param oldVersion 旧版本
* @param newVersion 新版本
*/
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (oldVersion >= 1 && oldVersion < 2) {
db.execSQL("ALTER TABLE account ADD COLUMN subAccountId VARCHAR default ''");
createSubAccountTable(db);
}
if (oldVersion >= 2 && oldVersion < 3) {
db.execSQL("ALTER TABLE subAccount ADD COLUMN userName VARCHAR default ''");
}
}
}
这种方式更新数据库一定要注意表的创建版本,所以必要的注释还是要详细点。
3.第三种方式和第二种基本是一样,只是在onUpgrade方法中一步步往上升级,已上面2.2例子说明,初始版本为1,升级到版本3,先升级到版本2再升级到版本3这样就不需要判断oldVersion 版本号的上下限了。
使用这种方式升级,onCreate中可以是最新表创建(3.1示例),也可以只进行最初版本的表创建然后调用onUpgrade方法升级数据库(3.2示例)
/**
* 数据库名称
*/
private static final String DATABASE_NAME = "account.db";
/**
* 数据库版本-升级用
*/
private static final int DATABASE_VERSION = 3;
public DatabaseHelper3(Context context) {
//CursorFactory设置为null,使用默认值
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
/**
* 数据库第一次创建时调用
*/
@Override
public void onCreate(SQLiteDatabase db) {
createAccountTable(db);
createSubAccountTable(db);
}
private void createSubAccountTable(SQLiteDatabase db) {
db.execSQL("CREATE TABLE IF NOT EXISTS subAccount " +
"(userId VERCHAR PRIMARY KEY,subAccountId VERCHAR,totalAccount INTEGER," +
"userName VARCHAR default '')");
}
private void createAccountTable(SQLiteDatabase db) {
db.execSQL("CREATE TABLE IF NOT EXISTS account " +
"(userId VERCHAR PRIMARY KEY,userName VERCHAR,totalAccount INTEGER," +
"subAccountId VARCHAR default '')");
}
/**
* @param db 数据库实际操作类
* @param oldVersion 旧版本
* @param newVersion 新版本
*/
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
int version = oldVersion;
if (version == 1){
db.execSQL("ALTER TABLE account ADD COLUMN subAccountId VARCHAR default ''");
db.execSQL("CREATE TABLE IF NOT EXISTS subAccount " +
"(userId VERCHAR PRIMARY KEY,subAccountId VERCHAR,totalAccount INTEGER)");
version = 2;
}
if (version == 2){
db.execSQL("ALTER TABLE subAccount ADD COLUMN userName VARCHAR default ''");
}
}
/**
* 数据库名称
*/
private static final String DATABASE_NAME = "account.db";
/**
* 最初版本
*/
private static final int FIRST_DATABASE_VERSION = 1;
/**
* 数据库版本-升级用
*/
private static final int DATABASE_VERSION = 3;
public DatabaseHelper2(Context context) {
//CursorFactory设置为null,使用默认值
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
/**
* 数据库第一次创建时调用
*/
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE IF NOT EXISTS account " +
"(userId VERCHAR PRIMARY KEY,userName VERCHAR,totalAccount INTEGER)");
onUpgrade(db,FIRST_DATABASE_VERSION,DATABASE_VERSION);
}
/**
* @param db 数据库实际操作类
* @param oldVersion 旧版本
* @param newVersion 新版本
*/
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (oldVersion >= newVersion){
return;
}
int version = oldVersion;
if (version == 1){
db.execSQL("ALTER TABLE account ADD COLUMN subAccountId VARCHAR default ''");
db.execSQL("CREATE TABLE IF NOT EXISTS subAccount " +
"(userId VERCHAR PRIMARY KEY,subAccountId VERCHAR,totalAccount INTEGER)");
version = 2;
}
if (version == 2){
db.execSQL("ALTER TABLE subAccount ADD COLUMN userName VARCHAR default ''");
}
}
总结:
以上三种方式均可以实现数据库版本的升级。
如果数据库版本升级不需要保存旧表数据可以选择方式1升级数据库,删除所有表重新创建。
如果数据库版本升级需要保存数据库表数据,则可以选择方式二或者方式三。
个人比较建议选择方式二,新安装APP创建数据库不会做很多多余的操作且逻辑也比较清晰。
当然大家可以根据各自的需求选择,如果数据库修改不是很频繁而且修改较多的情况下,方式三也不失为一个好的选择,代码逻辑很清晰。
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
throw new SQLiteException("Can't downgrade database from version " +
oldVersion + " to " + newVersion);
}
举个栗子:
@Override
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
dropTable(db,"account");
dropTable(db,"subAccount");
}
/**
* 删除表
* @param db 数据库
* @param tableName 表名
*/
public void dropTable(SQLiteDatabase db, String tableName) {
if (TextUtils.isEmpty(tableName)) {
return;
}
db.execSQL("drop table " + tableName);
}