android:数据保存之SQLite

android开发的时候,数据的保存时少不了的一个重要工作开发内容。今天以本人在开发中总结的经验,来记录说明一下数据保存的一个重要的方法,SQLite数据保存。

SQLite时谷歌提供的一个对于较大数据处理的一种方法,功能比较强大,但是对于手机上的一些数据存储,维护,管理等还是比较方便实用的。它具备以下几大特点:

1.轻量级:使用 SQLite 只需要带一个动态库,就可以享受它的全部功能,而且那个动态库的尺寸想当小。

2.独立性:SQLite 数据库的核心引擎不需要依赖第三方软件,也不需要所谓的“安装”。

3.隔离性:SQLite 数据库中所有的信息(比如表、视图、触发器等)都包含在一个文件夹内,方便管理和维护。

4.跨平台:SQLite 目前支持大部分操作系统,不至电脑操作系统更在众多的手机系统也是能够运行,比如:Android。

5.多语言接口:SQLite 数据库支持多语言编程接口。

6.安全性:SQLite 数据库通过数据库级上的独占性和共享锁来实现独立事务处理。这意味着多个进程可以在同一时间从同一数据库读取数据,但只能有一个可以写入数据。

一:SQLiteOpenHelper类

讲到SQLite,那不得不提一下这个类:SQLiteOpenHelper,称它为一个辅助帮助类也未尝不可,它的作用是数据库的创建和更新等作用。作为一个抽象类,它不是被继承的还能干啥,一般我们都继承这个类,实现onCreate和onUpgrade方法,onCreate只有在数据库第一次创建的时候才执行,而onUpgrade方法,当数据库版本升级的时候要执行的,所以数据库结果变更的时候,处理方法要写在此方法中,如数据库新增表或者表中新增字段等。具体方

法如下:

SQLiteOpenHelper(Context context,String name,SQLiteDatabase.CursorFactory factory,int version)
构造方法,其中
context:程序上下文环境
name:数据库名字
factory:游标工厂。
onCreate(SQLiteDatabase db)
数据库创建时调用,只在创建时调用
onUpgrade(SQLiteDatabase db,int oldVersion , int newVersion)
数据库版本更新的时候调用
getReadableDatabase()
创建或者打开一个只读数据库
getWritableDatabase()
创建或者打开一个读写数据库

具体使用方法如下面:

package com.dandy.sqlite;

import com.dandy.util.LogUtils;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
import android.text.TextUtils;

public class DataBaseHelper extends SQLiteOpenHelper{

	private static final String TAG = DataBaseHelper.class.getSimpleName();
	
	/**
	 * 数据库名字 
	 */
	private static final String DATABASE_NAME = "dandy.db";
	
	/**
	 * 学生信息表名 
	 */
	protected static final String TABLE_NAME_STUDENT = "students";
	
	/**
	 * 老师信息表 
	 */
	protected static final String TABLE_NAME_TEACHER = "teachers";
	
	public DataBaseHelper(Context context,int version){
		this(context,DATABASE_NAME,null,version);
	}
	
	public DataBaseHelper(Context context, String name, CursorFactory factory,int version) {
		super(context, DATABASE_NAME, factory, version);
	}

	@Override
	public void onCreate(SQLiteDatabase db) {
		createTables(db);
	}

	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
		if(oldVersion < newVersion){
			LogUtils.d(TAG, "database should update.oldVersion = " + oldVersion + ",newVersion = "+newVersion);
			
			upgradeTables_method1(db,oldVersion);
//			upgradeTables_method2(db,oldVersion);
		}
	}

	/**
	 * 表创建 
	 */
	private void createTables(SQLiteDatabase db){
		createStudentTable(db);
//		createTeacherTable(db);
	}
	
	/**
	 * 学生信息表的创建 
	 */
	private void createStudentTable(SQLiteDatabase db){
		
		StringBuffer createStudentTableSql = new StringBuffer();
		createStudentTableSql.append(ConstantString.CTEATETABLE)
							 .append(TABLE_NAME_STUDENT)
							 .append(ConstantString.LEFT_PARENTHESES)
							 .append(BaseColum.ID).append(ConstantString.ID_AUTO)
							 .append(BaseColum.NAME).append(ConstantString.TEXT).append(ConstantString.COMMA)
							 .append(BaseColum.AGE).append(ConstantString.TEXT).append(ConstantString.COMMA)
							 .append(BaseColum.SEX).append(ConstantString.DEFAULT).append("0")
							 .append(ConstantString.RIGHT_PARENTHESES);
		
		LogUtils.d(TAG, "createStudentTableSql = " + createStudentTableSql.toString());
		db.execSQL(createStudentTableSql.toString());
	}		
	
	/**
	 * 老师信息表的创建 
	 */
	private void createTeacherTable(SQLiteDatabase db){
		StringBuffer createTeacherTableSql = new StringBuffer();
		createTeacherTableSql.append(ConstantString.CTEATETABLE)
							 .append(TABLE_NAME_TEACHER)
							 .append(ConstantString.LEFT_PARENTHESES)
							 .append(BaseColum.ID).append(ConstantString.ID_AUTO)
							 .append(BaseColum.NAME).append(ConstantString.TEXT).append(ConstantString.COMMA)
							 .append(BaseColum.AGE).append(ConstantString.TEXT)
							 .append(ConstantString.RIGHT_PARENTHESES);
		LogUtils.d(TAG, "createTeacherTableSql = "+createTeacherTableSql.toString());
		db.execSQL(createTeacherTableSql.toString());
	}
	
	/**
	 * 数据库升级,确定相邻的2个版本之间的差异,数据库可以依次迭代,如:V1-->V2,V2-->V3
	 * 
	 * 优点:每次更新数据库的时候只需要在onUpgrade方法的末尾加一段从上个版本升级到新版本的代码,易于理解和维护
	 * 
	 * 缺点:当版本变多之后,多次迭代升级可能需要花费不少时间,增加用户等待
	 */
	private void upgradeTables_method1(SQLiteDatabase db,int oldVersion){
		int upgradeVersion = oldVersion;
		
		//V1-->V2
		if(upgradeVersion == 1){
			upgradeTables1(db);
			upgradeVersion = 2;
		}
		
		//V2-->V3
		if(upgradeVersion == 2){
			upgradeTables2(db);
		}
	}
	
	/**
	 * 数据库升级,为 每个版本 确定与现在数据库的差别,为每个case撰写专门的升级代码。
	 * 
	 * 优点:可以保证每个版本的用户都可以在消耗最少的时间升级到最新的数据库而无需做无用的数据多次转存
	 * 
	 * 缺点:强迫开发者记忆所有版本数据库的完整结构,且每次升级时onUpgrade方法都必须全部重写
	 */
	private void upgradeTables_method2(SQLiteDatabase db,int oldVersion){
		switch (oldVersion) {
		case 1:
			upgradeTables1(db);
			upgradeTables2(db);
			break;

		case 2:
			upgradeTables2(db);
			break;
		}
	}
	
	/**
	 * 数据库第一次升级,实现增加一张新表(如:teachers)
	 */
	private void upgradeTables1(SQLiteDatabase db){
		createTeacherTable(db);
	}
	
	/**
	 * 数据库第二次升级,新增表的列数(students表中增加address列)
	 */
	private void upgradeTables2(SQLiteDatabase db){
		try {
			db.beginTransaction();
			addTableColums(db, TABLE_NAME_STUDENT, BaseColum.ADD, "text", "China");
			db.setTransactionSuccessful();
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			db.endTransaction();
		}
	}
	
	/**
	 * 增加数据库表中字段 
	 * @param db
	 * @param table:表名
	 * @param coloum:字段名
	 * @param type:字段属性
	 * @param def:字段默认值
	 */
	private void addTableColums(SQLiteDatabase db,String table,String colum,String type,String def){
		try {
			StringBuffer addSql = new StringBuffer();
			addSql.append(ConstantString.ALTER)
				  .append(table)
				  .append(ConstantString.ADD)
				  .append(colum)
				  .append(type.startsWith(ConstantString.SPACE)?type:ConstantString.SPACE+type);
			if(!TextUtils.isEmpty(def)){
				if(def.contains("default")){
					addSql.append(def.startsWith(ConstantString.SPACE)?def:ConstantString.SPACE+def);
				}else{
					addSql.append(ConstantString.DEFAULT).append(def);
				}
			}
			LogUtils.i(TAG, table+"add new colum:addSql = "+addSql.toString());
			db.execSQL(addSql.toString());
		} catch (Exception e) {
			LogUtils.e(TAG, table+" add "+colum+" error.");
			e.printStackTrace();
		}
	}
	
	/**
	 * 学生信息表字段名 
	 */
	static class BaseColum{
		public static final String ID = "id";
		public static final String NAME = "name";
		public static final String AGE = "age";
		public static final String SEX = "sex";
		public static final String ADD = "address";
	}
	
	/**
	 * 字符串常量 
	 */
	static class ConstantString{
		public static final String CTEATETABLE = "create table if not exists ";
		public static final String ID_AUTO = " integer primary key autoincrement,";
		public static final String TEXT = " text ";
		public static final String TEXT_NOT_NULL = "text not null";
		public static final String LEFT_PARENTHESES = "(";
		public static final String RIGHT_PARENTHESES = ")";
		public static final String COMMA = ",";
		public static final String ALTER = "alter table ";
		public static final String RENAME = " rename to ";
		public static final String INSERT  = "insert into ";
		public static final String DROPTABLE = "drop table if exists ";
		public static final String SELECT = " select ";
		public static final String ADD = " add ";
		public static final String FROM = " from ";
		public static final String SPACE = " ";
		public static final String DEFAULT = " default ";
	}
	
}
方法的说明以及实现过程代码里面都有说明,在此指示简要说一下流程:数据库创建的时候执行onCreate方法,当数据库版本升级的时候执行 onUpgrade方法,具体的升级方法例子如代码中描述,第一次升级时候新建一张teachers表,第二次升级的时候在students表中新增一个address字段。表升级的时候提供了两种方法,具体采用哪种可以依据实际情况来选择。

二:SQLiteDatabase类

此类的获取方法有两种,getReadableDatabase和getWritableDatabase,获取的数据库有何不同上面已经说明。SQLiteDatabase类用于执行数据的具体操作,insert,delete,update,query等方法。

insert(String table,String nullColumnHack,ContentValues values)
插入数据到数据库
delete(String table,String whereClause,String[] whereArgs)
删除数据库中的数据
update(String table, ContentValues values, String whereClause, String[] whereArgs)
更新数据库的数据
 query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy,
String having, String orderBy, String limit)

查询数据并返回一个带游标的数据集cursor
execSQL(String sql)
执行一个sql语句
rawQuery(String sql, String[] selectionArgs)
运行一个预置的SQL语句,返回带游标的数据集(与上面的语句最大的区别就是防止SQL注入)
close()
关闭数据库

下面新建一个抽象类AbstractSQLManager,此类中实现对数据库的打开以及关闭等简单的方法,如下:

package com.dandy.sqlite;

import com.MvpApplication;
import com.dandy.util.AppUtil;
import com.dandy.util.LogUtils;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;

public abstract class AbstractSQLManager {
	
	private static final String TAG = AbstractSQLManager.class.getSimpleName();
	
	private DataBaseHelper mDataBaseHelper;
	
	private SQLiteDatabase mSQLitedb;
	
	public AbstractSQLManager() {
		openDataBase(MvpApplication.getApplication());
	}
	
	private void openDataBase(Context context){
		if(mDataBaseHelper == null){
			mDataBaseHelper = new DataBaseHelper(context, AppUtil.getVersionCode(context));
		}
		if(mSQLitedb == null){
			mSQLitedb = mDataBaseHelper.getWritableDatabase();
		}
	}
	
	/**
	 * @return
	 */
	protected final SQLiteDatabase sqliteDB(){
		if(mSQLitedb == null){
			openDataBase(MvpApplication.getApplication());
		}
		return mSQLitedb;
	}
	
	/**
	 * 数据库关闭 
	 */
	public void close(){
		try {
			if(mSQLitedb != null && mSQLitedb.isOpen()){
				mSQLitedb.close();
				mSQLitedb = null;
			}
			if(mDataBaseHelper != null){
				mDataBaseHelper.close();
				mDataBaseHelper = null;
			}
		} catch (Exception e) {
			LogUtils.e(TAG, "sqlite close error.");
			e.printStackTrace();
		}
	}
}
数据库的打开以及关闭其实很简单,通过api调用一下就可以了。然后就是具体的数据库针对某表的增删改查等的方法,因此新建类StudentSQLManager,继承自 AbstractSQLManager,具体如下:

package com.dandy.sqlite;

import java.util.ArrayList;
import java.util.List;
import android.content.ContentValues;
import android.database.Cursor;
import android.text.TextUtils;
import com.dandy.bean.Student;
import com.dandy.sqlite.DataBaseHelper.BaseColum;
import com.dandy.util.LogUtils;

public class StudentSQLManager extends AbstractSQLManager{
	
	private static final String TAG = StudentSQLManager.class.getSimpleName();
	
	private static StudentSQLManager instance;
	
	public static StudentSQLManager getInstance(){
		if(instance == null){
			synchronized (StudentSQLManager.class) {
				if(instance == null){
					instance = new StudentSQLManager();
				}
			}
		}
		return instance;
	}
	
	/**
	 * 学生信息的插入
	 * @param student 
	 */
	public void insertStudent(Student student){
		if(student == null){
			LogUtils.d(TAG, "insertStudent_param student is null.");
			return;
		}
		ContentValues values = null;
		try {
			values = new ContentValues();
			values.put(BaseColum.NAME, student.getName());
			values.put(BaseColum.AGE, student.getAge());
			if(!TextUtils.isEmpty(student.getSex())){
				values.put(BaseColum.SEX, student.getSex());
			}
			sqliteDB().insert(DataBaseHelper.TABLE_NAME_STUDENT, null, values);
		} catch (Exception e) {
			LogUtils.e(TAG, "insert student error.");
			e.printStackTrace();
		}finally{
			if(values != null){
				values.clear();
				values = null;
			}
		}
	}
	
	/**
	 * 查找所有学生的信息 
	 */
	public List<Student> queryAllStudent(){
		List<Student> students = new ArrayList<Student>();
		Cursor cursor = null;
		try {
			cursor = sqliteDB().query(DataBaseHelper.TABLE_NAME_STUDENT, null, null, null, null, null, null);
			if(cursor != null && cursor.getCount() >0 && cursor.moveToFirst()){
				do {
					Student student = new Student();
					student.setId(cursor.getString(cursor.getColumnIndexOrThrow(BaseColum.ID)));
					student.setName(cursor.getString(cursor.getColumnIndexOrThrow(BaseColum.NAME)));
					student.setAge(cursor.getString(cursor.getColumnIndexOrThrow(BaseColum.AGE)));
					student.setSex(cursor.getString(cursor.getColumnIndexOrThrow(BaseColum.SEX)));
					students.add(student);
				} while (cursor.moveToNext());
			}
		} catch (Exception e) {
			LogUtils.e(TAG, "query all students error.");
			e.printStackTrace();
		}finally{
			if(cursor != null){
				cursor.close();
				cursor = null;
			}
		}
		return students;
	}
	
	/**
	 * 删除学生信息
	 *  
	 * @param id
	 */
	public int deleteStudent(String id){
		try {
			return sqliteDB().delete(DataBaseHelper.TABLE_NAME_STUDENT, BaseColum.ID+"=?", new String[]{id});
		} catch (Exception e) {
			LogUtils.e(TAG, "delete student error.");
			e.printStackTrace();
		}
		return -1;
	}
	
	/**
	 * 更新学生信息
	 * 
	 * @param student
	 */
	public int updateStudent(Student student){
		ContentValues values = null;
		try {
			values = new ContentValues();
			values.put(BaseColum.NAME, student.getName());
			values.put(BaseColum.AGE, student.getAge());
			if(!TextUtils.isEmpty(student.getSex())){
				values.put(BaseColum.SEX, student.getSex());
			}
			return sqliteDB().update(DataBaseHelper.TABLE_NAME_STUDENT, values, BaseColum.ID+"=?", new String[]{student.getId()});
		} catch (Exception e) {
			LogUtils.e(TAG, "update student error.");
			e.printStackTrace();
		}
		return -1;
	}
}

上面的代码中实现了简单的对student表的增删改查等操作方法,可以看到,里面用到了两个新名词ContentValues和Cursor,下面来简单的说一下这2个东西是啥。

三:ContentValues类

和HashTable类似,都是一种存储的机制,只不过ContentValues只能存储一下基本数据类型,像int,string等,而HashTable是可以存储对象类型的。以键值对的形式存储数据,键是string类型。存储方法中上面代码已经有所提及,请回去查看。

四:Cursor类

用cursor时我们就必须晓得,cursor是每行数据的集合,是一个随机的数据源集合,每条数据都是通过下标来获取的,每一列的名称以及每一列的数据类型都是明确知道的。使用 moveToFirst() 定位第一行,cursor.moveToNext()定位到下一条数据。它的诞生就是基于为数据库服务而活着的。它的一些重要方法如下:

close关闭游标,释放资源(数据读取完成后执行,必须要走)
copyStringToBuffer(int columnIndex, CharArrayBuffer buffer) 在缓冲区中检索请求的列的文本,将将其存储(本人没用过暂时)
getColumnCount() 返回所有列的个数
getColumnIndex(String columnName)返回指定列的下标,如果不存在返回-1
getColumnIndexOrThrow(String columnName) 从零开始返回指定列下标,如果不存在将抛出IllegalArgumentException 异常
getColumnName(int columnIndex) 根据下标返回列名,即字段名
getColumnNames() 返回一个字符串数组的列名
getCount() 返回Cursor 中的行数
moveToFirst() 移动光标到第一行
moveToLast()移动光标到最后一行
moveToNext() 移动光标到下一行
moveToPosition(int position) 移动光标到一个指定的位置
moveToPrevious() 移动光标到上一行

以上就是SQLite小型数据库的简单使用方法以及一些所涉及到的知识点概括,做个简单记录,呵呵!


参考资料:Cursor源码官网

http://www.cnblogs.com/Excellent/archive/2011/11/19/2254888.html

源代码下载地址

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值