Android_SQLite数据库详解

本博文为子墨原创,转载请注明出处!
http://blog.csdn.net/zimo2013/article/details/9463489

1.SQLite概述

SQLite是一款轻量级的关系型数据库,由于它占用的资源非常少,所以在很多嵌入式设备都是用SQLite来存储数据。Android作为目前主流的移动操作系统,完全符合SQLite占用资源少的优势,故在Android平台上,集成了一个嵌入式关系型数据库SQLite。

继承SQLiteOpenHelper类,同时覆盖onCreate(SQLiteDatabase),onUpgrade(SQLiteDatabase,int,int)和定义构造函数,使用该类实例对象的getReadableDatabase()或者getWritableDatabase()得到当前数据库的实例化对象,可直接对其进行操作

2.SQLite实现增删改查

//数据库的基本操作,增删改查
@Override
protected void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	setContentView(R.layout.activity_main);
	
	DbOpenHelper dbOpenHelper = new DbOpenHelper(this, "mytest.db", 1);
	SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
	
	//////////////////////////////////////////////////////////
	db.execSQL("insert into user(name, age) values(?,?)",new Object[]{"zhang", 10});		//1.1增
	
	db.execSQL("delete from user where _id = ?",new String[]{"1"});							//1.2删

	db.execSQL("update user set name=? where _id=?",new String[]{"haha", "2"});				//1.3改

	Cursor cursor = db.rawQuery("select * from user where age >= ?", new String[]{"1"});	//1.4查
	while(cursor.moveToNext()){
		Toast.makeText(this, cursor.getString(1), Toast.LENGTH_SHORT).show();
	}
	
	//////////////////////////////////////////////////////////
	ContentValues values = new ContentValues();												//2.1增
	values.put("name", "John");
	values.put("age", 20);
	db.insert("user", 					//表格名
			"name", 					//String nullColumnHack
										//	SQl不允许插入一个空行,当values的值为空时,将相当于执行于
										//	insert into age(name) values("null");
			values);					//插入值,相当于一个键值对的集合
	
	db.delete("user", "age>=? and age<=?", new String[]{"12", "18"});						//2.2删
	
	ContentValues values1 = new ContentValues();											//2.3改
	values.put("name", "liu");
	db.update("user", values1, "_id = ?", new String[]{"8"});
	
	Cursor cursor1 = db.query("user", 	//表格名												//2.4查
			null, 						//String[] columns 如果为空,则返回所有列
			"age >= ?",					//String selection 查询条件
			new String[]{"15"}, 		//String[] selectionArgs 查询参数
			null, 						//String groupBy
			null, 						//String having
			null);						//String orderBy
	while (cursor.moveToNext()) {
		int id = cursor1.getInt(cursor1.getColumnIndex("_id"));
		Toast.makeText(this, cursor1.getString(cursor1.getColumnIndex("name")), Toast.LENGTH_SHORT).show();
	}
}
/**
 * DbOpenHelper.java
 */
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class DbOpenHelper extends SQLiteOpenHelper{
	public DbOpenHelper(Context context, String name, int version) {
		super(context, name, null, version);
	}
	
	/**
	 * 数据库只有在第一次创建时才调用该方法,对于一个已经存在的数据库,该方法不执行
	 * SQLiteDatabase.openOrCreateDatabase()
	 */
	@Override
	public void onCreate(SQLiteDatabase db) {
		String sql = "create table user(_id integer primary key autoincrement, name varchar(10), age integer)";
		db.execSQL(sql);
	}

	/**
	 * 只有当数据库的版本号增加时,才会执行到该函数
	 * 初始默认数据库版本为0,当构造函数传递newVersion时,这时会有db.getVersion() 与 newVersion比较觉得该函数是否执行
	 */
	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
	}
}

3.getReadableDatabase()和getWritableDatabase()区别

(1).getReadableDatabase()

使用SQLiteDatabase实例对象的getReadableDatabase()方法,首先会检查数据库是否已经实例化并处于打开状态,如果满足要求直接返回数据库对象,否则试图返回一个可读写模式的数据库,但当磁盘空间已满,只能得到只读模式数据库对象

(2).getWritableDatabase()

使用SQLiteDatabase实例对象的getWritableDatabase()方法,目的是得到一个具有可读写的数据库实例对象,首先判断mDatabase是否不为空且已打开且不是只读模式,则直接返回该实例对象,否则如果mDatabase不为空则加锁,然后开始打开或创建数据库,进行版本相关操作并解锁,最后返回数据库实例对象!

总之,一般情况下,通过这两种方法获得的数据库实例对象时一样的,只有在数据库空间已满或其它情况下,通过getReadableDatabase得到的才是只读的实例对象

//得到一个具有可读写的数据库实例对象
public synchronized SQLiteDatabase getWritableDatabase() {
	if (mDatabase != null && mDatabase.isOpen() && !mDatabase.isReadOnly()) {
		return mDatabase;		//如mDatabase不为空已打开并且不是只读模式 则返回该对象
	}

	if (mIsInitializing) {		//判断是否正在初始化
		throw new IllegalStateException("getWritableDatabase called recursively");
	}

	boolean success = false;	//默认为false
	SQLiteDatabase db = null;
	if (mDatabase != null)
		mDatabase.lock();		//如果mDatabase不为空则加锁 阻止其他的操作
	try {
		mIsInitializing = true;	//初始化完成
		if (mName == null) {
			db = SQLiteDatabase.create(null);	
		} else {
			db = mContext.openOrCreateDatabase(mName, 0, mFactory);//打开或创建数据库
		}
		int version = db.getVersion();						//获取数据库版本(如果新创建的数据库版本为0)
		if (version != mNewVersion) {						//获得的数据库版本号与指定的数据库版本号进行比较
			db.beginTransaction();				//开始事务
			try {
				if (version == 0) {							//新建的数据库版本号为0,则直接执行onCreate方法
					onCreate(db);
				} else {
					onUpgrade(db, version, mNewVersion);	//如果版本号发生变化,且不是新建的数据库,则执行onUpgrade方法
				}
				db.setVersion(mNewVersion);					//为当前的数据库设置新版本号
				db.setTransactionSuccessful();	//设置事务成功提交
			} finally {
				db.endTransaction();			//结束事务
			}
		}

		onOpen(db);
		success = true;
		return db;							//返回当前的可读写模式数据库实例对象
	} finally {
		mIsInitializing = false;
		if (success) {
			if (mDatabase != null) {
				try {
					mDatabase.close();		//如果mDatabase存在则先关闭
				} catch (Exception e) {
				}
				mDatabase.unlock();			//为当前的数据库实例对象进行解锁操作
			}
			mDatabase = db;
		} else {
			if (mDatabase != null)
				mDatabase.unlock();
			if (db != null)
				db.close();
		}
	}
}
/*
 * 在getReadableDatabase()方法中,首先检查数据库是否已经实例化并处于打开状态
 * 如果满足要求直接返回数据库对象,否则试图获取一个可读写模式的数据库实例;
 * 	当磁盘空间已满,再以只读模式打开数据库
 */
public synchronized SQLiteDatabase getReadableDatabase() {
	if (mDatabase != null && mDatabase.isOpen()) {
		return mDatabase;		//如果发现mDatabase存在并已打开,则直接返回该数据库对象
	}

	if (mIsInitializing) {		//检查数据库是否正在进行初始化操作
		throw new IllegalStateException("初始化");
	}

	try {
		/******注意!!!调用了getWritableDatabase()方法*****/
		return getWritableDatabase();//一般情况下使用getReadableDatabase得到的是getWritableDatabase的数据库对象
	} catch (SQLiteException e) {
		if (mName == null)
			throw e; 
	}

	/*
	 * 使用可读写不能得到数据库对象,下面的操作只能得到对对象
	 * 常见数据库已满,不能再添加数据,此时只能写,不能读
	 */
	SQLiteDatabase db = null;
	try {
		mIsInitializing = true;
		String path = mContext.getDatabasePath(mName).getPath();
		db = SQLiteDatabase.openDatabase(path, mFactory, SQLiteDatabase.OPEN_READONLY);//以只读方式打开数据库
		if (db.getVersion() != mNewVersion) {	//只读数据库,不接收新版本数据库
			throw new SQLiteException("Can't upgrade read-only database from version " + db.getVersion() + " to "
					+ mNewVersion + ": " + path);
		}
		onOpen(db);
		Log.w(TAG, "Opened " + mName + " in read-only mode");
		mDatabase = db;							//mDatabase为打开的数据库对象
		return mDatabase;
	} finally {
		mIsInitializing = false;
		if (db != null && db != mDatabase)
			db.close();
	}
}

4.Cursor对象

(1).主要方法

c.move(int offset);					//以当前位置为参考,移动到指定行
c.moveToFirst();				//移动到第一行
c.moveToLast();						//移动到最后一行
c.moveToPosition(int position);	//移动到指定行
c.moveToPrevious();					//移动到前一行
c.moveToNext();					//移动到下一行
c.isFirst();						//是否指向第一条
c.isLast();						//是否指向最后一条
c.isBeforeFirst();					//是否指向第一条之前
c.isAfterLast();				//是否指向最后一条之后
c.isNull(int columnIndex);			//指定列是否为空(列基数为0)
c.isClosed();					//游标是否已关闭
c.getCount();						//总数据项数
c.getPosition();				//返回当前游标所指向的行数
c.getColumnIndex(String columnName);//返回某列名对应的列索引值
c.getString(int columnIndex);	//返回当前行指定列的值

(2).Cursor对象的管理

Activity提供了LoaderManager去管理cursor的生命周期,可参考cursor管理>>

(3).Cursor注意情况

1).要求

需要注意的是,在cursor的结果集中必须要包含一个“_id”的列,否则SimpleCursorAdapter会不识别,因为这源于SQLite的规范,主键以“_id”为标准。

2).措施

第一,建表时根据规范去做;
第二,查询时用别名,例如:SELECT id AS _id FROM person;
第三,使用CursorWrapper

CursorWrapper cursorWrapper = new CursorWrapper(c) {
	@Override
	public int getColumnIndexOrThrow(String columnName) throws IllegalArgumentException {
		if (columnName.equals("_id")) {
			return super.getColumnIndex("id");
		}
		return super.getColumnIndexOrThrow(columnName);
	}
}

5.防止数据重复插入

@Override
public void onCreate(SQLiteDatabase db) {
	String sql = "create table bitmap(" +
			"_id integer primary key autoincrement," +
			/*
			 * 防止重复,如果发现重复,数据不能添加
			 * fileName varchar(100) UNIQUE
			 * 
			 * 如果是下面这样声明,重复冲突后,原有的数据将会被替换
			 * fileName varchar(100) UNIQUE ON CONFLICT REPLACE
			 */
			" fileName varchar(100) UNIQUE," +	
			" latitude varchar(100)," +
			" longitude varchar(100)," +
			" addr varchar(100)," +
			" isSaved integer,"+
			" model varchar(100))";
	db.execSQL(sql);
}

6.事务Transacation

//数据库事务
public void dbTransacation() {
	db.beginTransaction();				//开始事务
	try {
		{
			//多个sql执行语句
		}
		db.setTransactionSuccessful(); 	//设置事务成功完成,将缓冲区的数据提交
	} finally {
		db.endTransaction(); 			//结束事务
	}
}

7.关于CursorIndexOutOfBoundsException

在使用SQLiteDatabase.openOrCreateDatabase()去打开一个sdcard上的db文件时,此时光标应该移动为记录的最后,如果没有使用cursor.moveToFirst(),就会报该异常,所有任何游标使用时,应该先moveToFirst()

if (cursor != null && cursor.getCount() > 0) {
	cursor.moveToFirst();
	int indexTrainsetType = cursor.getColumnIndex(TrainsetTypeMetaDate.trainsetType);
	do {
		String trainsetType = cursor.getString(indexTrainsetType);
		list.add(trainsetType);
	} while (cursor.moveToNext());
}

8.SQLITE分页查询

//方法一:select * from table_name where id > 7 limit 10 offset 3;
limit 10 offset 3;//limit N表示取N个记录, offset P表示跳过P行.

//方法二:select * from table_name where id > 7 limit 3,10;
limit 3,10;//limit index,count表示跳过index行后,取出count个记录

9.Update or Replace

INSERT OR REPLACE INTO Employee (id, name, role) VALUES (1, 'John Foo', 'CEO');

BAD: This will insert or replace 2 of the columns... the NAME column will be set to NULL or the default value:

INSERT OR REPLACE INTO Employee (id, role) VALUES (1, 'code monkey');

GOOD: This will update 2 of the columns. When ID=1 exists, the NAME will be unaffected. When ID=1 does not exist, the name will be default (NULL).

INSERT OR REPLACE INTO Employee (id, role, name) VALUES ( 1, 'code monkey', (SELECT name FROM Employee WHERE id = 1) );

This will update 2 of the columns. When ID=1 exists, the ROLE will be unaffected. When ID=1 does not exist, the role will be set to 'Benchwarmer' instead of the default value.

INSERT OR REPLACE INTO Employee (id, name, role) VALUES ( 1, 'Susan Bar', COALESCE((SELECT role FROM Employee WHERE id = 1), 'Benchwarmer') );

10.Replace部分字符串

You can use the built in replace() function to perform a string replace in a query.

Other string manipulation functions (and more) are detailed in the SQLite core functions list

The following should point you in the right direction.

UPDATE table SET field = replace( field, 'C:\afolder\', 'C:\anewfolder\' ) WHERE field LIKE 'C:\afolder\%'


你可能感兴趣的:(Android_SQLite数据库详解)