一、 Sqlite的基本操作—-增删改查

1、创建或打开数据库

SQLiteDatabase db = openOrCreateDatabase(DATABASE_NAME, MODE_PRIVATE, null);

2、创建或删除table

db.execSQL("DROP TABLE IF EXISTS " + Student.TABLE_NAME);
		db.execSQL("CREATE TABLE " + Student.TABLE_NAME 
		+ "(" 
		+ Student.ID + " INTEGER PRIMARY KEY 	AUTOINCREMENT, "
		+ Student.NAME + " VARCHAR, "
		+ Student.SCORE + " VARCHAR "
		+ ");");

3、插入数据

if(db.delete(Student.TABLE_NAME, Student.NAME + "=?", new String[]{"wgc"}) == 0) {
	Log.v("base_operation", "删除失败!");
} else {
	Log.v("base_operation", "删除成功!");
}

4、修改数据

values.put(Student.NAME, "wgc2");
values.put(Student.SCORE, "99");
int rows = db.update(Student.TABLE_NAME, values, Student.NAME + "=?", new String[]{"wgc"});

5、查询数据

Cursor cursor = db.rawQuery(
        "select * from " 
        + Student.TABLE_NAME
        + " where " + Student.NAME + "=?" 
        , new String[] {"wgc2"});
Student student = new Student();
while(cursor.moveToNext()){
    student.id = cursor.getString(cursor.getColumnIndex(Student.ID));
    student.name = cursor.getString(cursor.getColumnIndex(Student.NAME));
    student.score = cursor.getString(cursor.getColumnIndex(Student.SCORE));
    Log.v("base_operation", student.toString());
}

二、Sqlite的事务属性

应用程序初始化时如果单独使用for+Insert的方式插入大量的数据,会导致应用响应慢。因为SQLite的数据库本质上是一个磁盘上的文件,所以一些的数据库操作其实就是对文件的操作。而频繁的文件操作是一个很耗时的过程,极大地影响了数据库的存储速度。

sqlite插入数据的时候默认一条语句就是一个事务,有多少条数据就有多少次磁盘操作。初始化1000条数据,也就要1000次磁盘的操作,将会重复的打开关闭数据库1000次,所以速度回满,而且还不能保证所有的数据都能同时插入。

解决方法是使用事务处理。
(1)db.beginTransaction();//手动设置开始事务

(2)db.setTransactionSuccessful();//设置事务处理成功,不设置会自动回滚不提交,在setTransactionSuccessful和endTransaction之间不进行任何数据库操作

(3) db.endTransaction();//处理完成

这样SQLite将把全部要执行的SQL语句先缓存到内存当中,然后等到commit的时候一次性写入到数据库。这样数据库文件只被打开关闭了一次,效率就大大提高。

1、未使用事务属性,插入1000条数据

long startMillis = System.currentTimeMillis();
for(int i = 0; i < 1000; i++) {
	ContentValues val = new ContentValues();
	val.put(Student.NAME, "wgc"+i);
	val.put(Student.SCORE, "99");
	db.insert(Student.TABLE_NAME, null, values);
}
Log.v("transaction", "未开始事务属性之前总共耗时:" + (System.currentTimeMillis() - startMillis) + "");

2、使用事务属性,插入1000天数据

db.beginTransaction();
startMillis = System.currentTimeMillis();
for(int i = 0; i < 1000; i++) {
	ContentValues val = new ContentValues();
	val.put(Student.NAME, "wgc"+i);
	val.put(Student.SCORE, "99");
	db.insert(Student.TABLE_NAME, null, val);
}
db.setTransactionSuccessful();
db.endTransaction();
Log.v("transaction", "使用事务属性之后总共耗时:" + (System.currentTimeMillis() - startMillis) + "");

下面是一个对比结果,可以明显看出,使用事务属性后,效率大大提高了。

三、Sqlite的多线程操作问题

我们可以得知SQLite是文件级别的锁:多个县城可以同时读,但是同时只能只有一个线程写。如果多线程同时读写(这里指不同的线程使用的是不同的SQLiteDatabase实例),后面就会遇到android.database.sqlite.SQLiteException:database is locked这样的异常。

解决办法是保持一个sqlite连接,保持单个SQLiteDatabase实例。同时对所有数据库操作方法添加synchroned关键字。

1、 下面看一个多线程同时进行写操作的后果:

class WriteThread extends Thread{
	@Override
	public void run() {
		SQLiteDatabase db = openOrCreateDatabase("demo.db", MODE_PRIVATE, null);
		db.beginTransaction();
		for(int i = 0; i < 8000; i++) {
			ContentValues val = new ContentValues();
			val.put(Student.NAME, "wgc"+i);
			val.put(Student.SCORE, "99");
			db.insert(Student.TABLE_NAME, null, val);
		}
		db.setTransactionSuccessful();
		db.endTransaction();
		Log.v("multiThread2", "Thread ID:"+getId());
	}
}
…………………..
//开启5个线程执行写操作
for(int i = 0; i < 5; i++) {
	new WriteThread().start();
}

运行程序,会见到程序奔溃,并报错,错误提示如下:

2、 下面看一个多线程同时进行读的后果:

class ReadThread extends Thread {
	@Override
	public void run() {
		SQLiteDatabase db = openOrCreateDatabase("demo.db", MODE_PRIVATE,
				null);
		for (int i = 0; i < 8000; i++) {
			StringBuilder builder = new StringBuilder();
			builder.append("select * from ");
			builder.append(Student.TABLE_NAME);
			builder.append(" where " + Student.NAME + "=?");

			Cursor cursor = db.rawQuery(builder.toString(),
					new String[] { "wgc2" });
			Student student = new Student();
			if (cursor.moveToNext()) {
				student.id = cursor.getString(cursor
						.getColumnIndex(Student.ID));
				student.name = cursor.getString(cursor
						.getColumnIndex(Student.NAME));
				student.score = cursor.getString(cursor
						.getColumnIndex(Student.SCORE));
			}
			Log.v("multiThread",
					"Thread ID:" + getId() + "===" + student.toString());
		}
	}
}
…………………..
//开启5个线程执行读操作
for(int i = 0; i < 5; i++) {
	new ReadThread().start();
}

程序运行后没有任何异常,也没有报错。

下面给出解决多线程同时写的解决方法:
(1) 首先是使用单例模式,始终保持一个连接,使用一个SQLiteDatabase实例。

public class SQLiteDatabaseManager {
	
	private static SQLiteDatabase db = null;
 
	
	public static SQLiteDatabase getInstance(Context context, String name, int mode) {
		if(db == null) {
			db = context.openOrCreateDatabase(name, mode, null);
		} 
		return db;
	}

}

通过SQLiteDatabaseManager的getInstance方法就能保证始终使用同一个SQLiteDatabase实例。给write函数添加synchronized关键字能保证该方法统一时刻不会被同时调用。

然后我们队对第一次多线程写操作的代码做一些修改:

private synchronized void write() {
	SQLiteDatabase db = SQLiteDatabaseManager.getInstance(
			getApplicationContext(), "demo.db", MODE_PRIVATE);

	db.beginTransaction();
	for (int i = 0; i < 8000; i++) {
		ContentValues val = new ContentValues();
		val.put(Student.NAME, "wgc" + i);
		val.put(Student.SCORE, "99");
		db.insert(Student.TABLE_NAME, null, val);
	}
	db.setTransactionSuccessful();
	db.endTransaction();
}


	class WriteThread extends Thread {
	@Override
	public void run() {
		write();
	}
}

private synchronized void read() {
	SQLiteDatabase db = SQLiteDatabaseManager.getInstance(
			getApplicationContext(), "demo.db", MODE_PRIVATE);

	for (int i = 0; i < 8000; i++) {
		StringBuilder builder = new StringBuilder();
		builder.append("select * from ");
		builder.append(Student.TABLE_NAME);
		builder.append(" where " + Student.NAME + "=?");

		Cursor cursor = db.rawQuery(builder.toString(),
				new String[] { "wgc2" });
		Student student = new Student();
		if (cursor.moveToNext()) {
			student.id = cursor
					.getString(cursor.getColumnIndex(Student.ID));
			student.name = cursor.getString(cursor
					.getColumnIndex(Student.NAME));
			student.score = cursor.getString(cursor
					.getColumnIndex(Student.SCORE));
		}
	}
}

class ReadThread extends Thread {
	@Override
	public void run() {
		read();
	}
}

我们再次进行多线程的读写操作:

// 开启5个线程执行读操作
for (int i = 0; i < 5; i++) {
	new ReadThread().start();
}

// 开启5个线程执行写操作
for (int i = 0; i < 5; i++) {
	new WriteThread().start();
}

这次程序就没有异常,也没有报错了。

源码下载地址:
http://download.csdn.net/detail/wen294299195/9308083

打赏

发表评论

电子邮件地址不会被公开。