在项目开发中,我们基本上会用到数据库去存储数据。虽然现在有着不少的数据库框架,像GreenDao 、LiteOrm、xUtils3等,基本可以满足我们数据库方面的使用需求。之所以写SQLite的使用,主要是用来了解数据库的基本使用,也好在面试中应付面试。
使用SQLite, 首先创建一个继承在SQLiteOpenHelper的类,并重写onCreate()和onUpgrade()方法。
package com.my.learn.code.sqlite;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
public class MySqliteHelper extends SQLiteOpenHelper {
private static final int DB_VERSION = 1;//数据库版本
private static final String DB_NAME = "myTest.db";//数据库名称
public static final String TABLE_NAME = "Orders";//表的名称
public MySqliteHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
}
/**
* 这个方法
* 1、在第一次打开数据库的时候才会走
* 2、在清除数据之后再次运行-->打开数据库,这个方法会走
* 3、没有清除数据,不会走这个方法
* 4、数据库升级的时候这个方法不会走
*/
@Override
public void onCreate(SQLiteDatabase db) {
//初始化数据表,可以再这里面对多个表进行处理
String sql = "create table if not exists " + TABLE_NAME + " (studentid text primary key, name text, sex text, age integer)";
db.execSQL(sql);
}
/**
* 数据库升级
* 1、第一次创建数据库的时候,这个方法不会走
* 2、清除数据后再次运行(相当于第一次创建)这个方法不会走
* 3、数据库已经存在,而且版本升高的时候,这个方法才会调用
*
* @param db
* @param oldVersion
* @param newVersion
*/
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
String sql = "DROP TABLE IF EXISTS " + TABLE_NAME;
db.execSQL(sql);
onCreate(db);
}
/**
* 执行数据库的降级操作
* 1、只有新版本比旧版本低的时候才会执行
* 2、如果不执行降级操作,会抛出异常
*/
@Override
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
super.onDowngrade(db, oldVersion, newVersion);
}
}
这个类主要是用来创建数据库、数据库表以及维护数据库升级、数据表字段更新。
然后创建一个DAO类来操作数据表的CRUD。
public class StudentDao {
private static StudentDao INSTANCE = null;
private MySqliteHelper mHelper = null;
private SQLiteDatabase mDB = null;
private StudentDao(Context context){
mHelper = new MySqliteHelper(context);
mDB = mHelper.getWritableDatabase();
}
public static StudentDao getInstance(Context context){
if(INSTANCE == null){
return new StudentDao(context);
}
return INSTANCE;
}
}
在这里面SQLiteDatabase mDB使用了操作数据库的实例,这里我能用getWritableDatabase();来获取,在SQLiteOpenHelper中提供了getWritableDatabase()跟getReadableDatabase()两种方式来获取SQLiteDatabase,那么这两个方法的区别是什么呢?
我们看下彼此的代码就基本能清楚。
getReadableDatabase源码:
public SQLiteDatabase getReadableDatabase() {
synchronized (this) {
return getDatabaseLocked(false);
}
}
private SQLiteDatabase getDatabaseLocked(boolean writable) {
if (mDatabase != null) {
if (!mDatabase.isOpen()) {
// Darn! The user closed the database by calling mDatabase.close().
mDatabase = null;
} else if (!writable || !mDatabase.isReadOnly()) {
// The database is already open for business.
return mDatabase;
}
}
if (mIsInitializing) {
throw new IllegalStateException("getDatabase called recursively");
}
SQLiteDatabase db = mDatabase;
try {
mIsInitializing = true;
if (db != null) {
if (writable && db.isReadOnly()) {
db.reopenReadWrite();
}
} else if (mName == null) {
db = SQLiteDatabase.createInMemory(mOpenParamsBuilder.build());
} else {
final File filePath = mContext.getDatabasePath(mName);
SQLiteDatabase.OpenParams params = mOpenParamsBuilder.build();
try {
db = SQLiteDatabase.openDatabase(filePath, params);
// Keep pre-O-MR1 behavior by resetting file permissions to 660
setFilePermissionsForDb(filePath.getPath());
} catch (SQLException ex) {
if (writable) {
throw ex;
}
Log.e(TAG, "Couldn't open " + mName
+ " for writing (will try read-only):", ex);
params = params.toBuilder().addOpenFlags(SQLiteDatabase.OPEN_READONLY).build();
db = SQLiteDatabase.openDatabase(filePath, params);
}
}
onConfigure(db);
final int version = db.getVersion();
if (version != mNewVersion) {
if (db.isReadOnly()) {
throw new SQLiteException("Can't upgrade read-only database from version " +
db.getVersion() + " to " + mNewVersion + ": " + mName);
}
if (version > 0 && version < mMinimumSupportedVersion) {
File databaseFile = new File(db.getPath());
onBeforeDelete(db);
db.close();
if (SQLiteDatabase.deleteDatabase(databaseFile)) {
mIsInitializing = false;
return getDatabaseLocked(writable);
} else {
throw new IllegalStateException("Unable to delete obsolete database "
+ mName + " with version " + version);
}
} else {
db.beginTransaction();
try {
if (version == 0) {
onCreate(db);
} else {
if (version > mNewVersion) {
onDowngrade(db, version, mNewVersion);
} else {
onUpgrade(db, version, mNewVersion);
}
}
db.setVersion(mNewVersion);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
}
}
onOpen(db);
if (db.isReadOnly()) {
Log.w(TAG, "Opened " + mName + " in read-only mode");
}
mDatabase = db;
return db;
} finally {
mIsInitializing = false;
if (db != null && db != mDatabase) {
db.close();
}
}
}
在getReadableDatabase()方法中,调用getDatabaseLocked()来获取SQLiteDatabase,getDatabaseLocked中在首先判断是否已存在数据库实例并且是打开状态,如果是,则直接返回该实例,否则试图获取一个可读写模式的数据库实例,如果遇到磁盘空间已满等情况获取失败的话,再以只读模式打开数据库,获取数据库实例并返回,然后为mDatabase赋值为最新打开的数据库实例。
getWritableDatabase代码:
public SQLiteDatabase getWritableDatabase() {
synchronized (this) {
return getDatabaseLocked(true);
}
}
可以看到getWritableDatabase同样是调用了getDatabaseLocked(),只不过在getReadableDatabase中传的是false,getWritableDatabase中传的是true。在几个关键步骤是,首先判断mDatabase如果不为空已打开并不是只读模式则直接返回,否则如果mDatabase不为空则加锁,然后开始打开或创建数据库,比较版本,根据版本号来调用相应的方法,为数据库设置新版本号,最后释放旧的不为空的mDatabase并解锁,把新打开的数据库实例赋予mDatabase,并返回最新实例。
通过代码可以看出,如果不是在遇到磁盘空间已满等情况,getReadableDatabase()一般都会返回和getWritableDatabase()一样的数据库实例,所以我们在StudentDao构造方法中使用getWritableDatabase()获取整个应用所使用的数据库实例。下面看数据的操作部分。
1.增加数据
在Android中提供了两种增加数据操作:
初始化数据
直接采用execSQL方法:
mDB.execSQL("insert into " + MySqliteHelper.TABLE_NAME + " (studentid, name, sex, age) values (1, '张三', '男', 10)");
添加数据:
可以使用insert(String table,String nullColumnHack,ContentValues values)方法来插入,ContentValues内部实现就是HashMap,但是两者还是有差别的,ContenValues Key只能是String类型,Value只能存储基本类型的数据,像string,int之类的,不能存储对象。
public void addstudent(Student student){
mDB.beginTransaction();
ContentValues contentValues=new ContentValues();
contentValues.put("studentid",student.getId());
contentValues.put("name",student.getName());
contentValues.put("sex",student.getSex());
contentValues.put("age",student.getAge());
mDB.insertOrThrow( MySqliteHelper.TABLE_NAME,null,contentValues);
mDB.setTransactionSuccessful();
mDB.endTransaction();
}
删除数据
删除数据的方法除了execSQL还有delete(String table,String whereClause,String[] whereArgs),whereClause是删除条件,whereArgs是删除条件值数组。
/**
* 删除数据
*/
public void deletestudent(String id){
mDB.beginTransaction();
mDB.delete(MySqliteHelper.TABLE_NAME, "studentid = ?", new String[]{id});
mDB.setTransactionSuccessful();
}
修改数据
修改数据和插入数据很相似,调用的方法除了execSQL还可以是update(String table,ContentValues values,String whereClause, String[] whereArgs):
/**
* 修改数据
*/
public void updatestudent(ContentValues contentValues,String id){
mDB.beginTransaction();
mDB.update(MySqliteHelper.TABLE_NAME,
contentValues,
"Id = ?",
new String[]{id});
mDB.setTransactionSuccessful();
}
查找数据
查找数据有两个方法,一是public Cursor query(String table,String[] columns,String selection,String[] selectionArgs,String groupBy,String having,String orderBy,String limit);,另外一个是public Cursor rawQuery(String sql, String[] selectionArgs)。rawQuery的写法类似上面的execSQL。
/**
* 查询数据
*/
public List getstudentlist() {
// select * from Orders where CustomName = 'Bor'
Cursor cursor = mDB.query(MySqliteHelper.TABLE_NAME,
new String[]{"name","sex","age"},
"CustomName = ?",
new String[]{"Bor"},
null, null, null);
if (cursor.getCount() > 0) {
List studentList = new ArrayList(cursor.getCount());
while (cursor.moveToNext()) {
Student student = parseStudent(cursor);
studentList.add(student);
}
cursor.close();
return studentList;
}
return null;
}
友情提示:记得 cursor.close();记得 cursor.close();记得 cursor.close();
至于为什么,自己想好吧。
数据库版本更新
数据库版本更新的情况有:新增/删除表、表字段的增加删除
1.首先把数据库的版本号+1;
2.在onCreate中写当前数据库最新版本的建表数据;
3.由于数据库更新对于新安装的用户来说其实是不存在更新一说,因为是第一次安装,所以会直接走onCreate而不会走onUpgrade,所以对于之前安装的用户,数据库更新只走onUpgrade,所以要在onUpgrade对不同版本进行判断,然后执行对应的操作。
添加字段
添加字段我们需要用到 alter [tabname] add column [字段名] [字段类型]
switch (oldVersion){
case 1:
db.execSQL("alter table Student add column studentgrade text");
}
删除字段
由于SQLite不支持直接在原表上删除字段,如果要用到删除字段的情景,可以新建一个表,字段跟原表一致,但是要去掉要删除的字段,可以使用一下语句完成:
//创建新表
create table [新表] as select [字段名] from [原表] where 1=2;
//复制原表内容
INSERT INTO [新表] SELECT 字段名] FROM [原表] ;
然后删除原来的表,修改新表的名称即可