1.前言
最近有个刚接触开发的朋友在使用SQLite进行数据存储时出现各种问题,所以特意写了个DEMO发给他用于参考,以规避掉那些容易发生的突发状况。以下先附出代码部分,然后标出并解释对方在阅读Demo时产生的疑问。
2.代码部分
1.DBHelper类
package com.example.sqlitetest.db;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import com.example.sqlitetest.db.constant.MyDBConstant;
/**
* @ClassName:DBHelper
* @Description:TODO<数据库帮助类--用于数据库的创建and版本管理>
* @author:zihao
* @date:2014年04月18日 上午11:00:11
* @version:v1.0 构建一个基本的框架
*/
public class DBHelper extends SQLiteOpenHelper {
/** 数据库名称 **/
private static final String DB_NAME = "test.db";
/** 数据库版本号 **/
private static final int DB_VERSION = 1001;
/**
* 构造方法
*
* @param context
* // 上下文对象
*/
public DBHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);// 调用父类方法进行数据库的创建、打开或管理
// TODO Auto-generated constructor stub
}
/**
* Called when the database is created for the first time. This is where the
* creation of tables and the initial population of the tables should
* happen. 首次创建数据库时调用.
*/
@Override
public void onCreate(SQLiteDatabase db) {
// TODO Auto-generated method stub
// 进行数据库表的创建
createTable(db);
}
/**
* 当数据库发生更新操作时触发--即当前版本号DB_VERSION比原版本号大时
*/
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO Auto-generated method stub
// 用于处理版本更新(数据库表结构发生变更时,修改DB_VERSION时触发更新)
// 在更新处理中,包含两种处理方式:1.保留原始数据(1.取出数据,删除旧表;2.创建新表;3.插入数据);2.不保留原始数据(1.删除旧表;2.创建新表)--这里默认用第二种.
// 这里默认用第二种处理方式
// 1.删除旧表
deleteAllTable(db);
// 2.调用onCreate创建新表
onCreate(db);
}
/**
* TODO<创建表格>
*
* @return void
*/
private void createTable(SQLiteDatabase db) {
// 所有表格的创建都在这里进行
db.execSQL(MyDBConstant.CREATE_TB_USER_SQL);
}
/**
* TODO<删除所有表格>
*
* @return void
*/
private void deleteAllTable(SQLiteDatabase db) {
// 所有表格的删除都在这里进行
db.execSQL("DROP TABLE IF EXISTS " + MyDBConstant.TB_NAME_USERINFO);
}
}
2.DBManager类
package com.example.sqlitetest.db;
import java.util.concurrent.atomic.AtomicInteger;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;
/**
* @ClassName:DBManager
* @Description:TODO<数据库管理类>
* @author:zihao
* @date:2014年04月18日 上午11:02:23
* @version:v1.0 构建一个基本的框架
*/
public class DBManager {
private static final String TAG = DBManager.class.getSimpleName();
private AtomicInteger mOpenCounter = new AtomicInteger();// 用于操作计数
private static DBManager mInstance;
private static DBHelper mDbHelper;
private SQLiteDatabase mDatabase;
/**
* TODO<获取一个数据库管理实例>
*
* @return DBManager
*/
public static synchronized DBManager getInstance(Context context) {
if (mInstance == null) {
mInstance = new DBManager();
mDbHelper = new DBHelper(context);
}
return mInstance;
}
/**
* TODO<获得一个读写数据库操作对象>
*
* @return SQLiteDatabase
*/
public synchronized SQLiteDatabase getWriteDatabase() {
if (mOpenCounter.incrementAndGet() == 1) {// 每次获取该实例mOpenCounter都通过自增来进行标记
mDatabase = mDbHelper.getWritableDatabase();
}
return mDatabase;
}
/**
* TODO<获取一个只写的数据库操作对象>
*
* @return SQLiteDatabase
*/
public synchronized SQLiteDatabase getReadDatabase() {
if (mOpenCounter.incrementAndGet() == 1) {// 每次获取该实例mOpenCounter都通过自增来进行标记
mDatabase = mDbHelper.getReadableDatabase();
}
return mDatabase;
}
/**
* TODO<关闭数据库>
*
* @return SQLiteDatabase
*/
public synchronized void closeDatabase() {
if (mOpenCounter.decrementAndGet() == 0) {// 自减判断计数值是否为0,如果不为0,表明其它位置还在引用该实例,不能关闭数据库
mDatabase.close();
}
}
}
3.MyDBConstant类
package com.example.sqlitetest.db.constant;
/**
* @ClassName:MyDBConstant
* @Description:TODO<数据库常量工具类--方便管理、维护>
* @author:zihao
* @date:2015年12月22日 上午11:23:52
* @version:v1.0
*/
public class MyDBConstant {
/** 用户信息表 **/
public static final String TB_NAME_USERINFO = "userInfo";
/**
* @ClassName:TB_USER
* @Description:TODO<用户表>
* @author:zihao
* @date:2015年12月22日 上午11:26:56
* @version:v1.0
*/
public class TB_USER {
/** 用户id **/
public static final String USER_ID = "user_id";
/** 用户名 **/
public static final String USER_NAME = "user_name";
/** 密码 **/
public static final String PASS_WORD = "pass_word";
}
/** 新建会议主题信息表sql语句 **/
public static final String CREATE_TB_USER_SQL = "CREATE TABLE IF NOT EXISTS "
+ TB_NAME_USERINFO
+ "("
+ TB_USER.USER_ID
+ " VARCHAR(100) PRIMARY KEY NOT NULL,"
+ TB_USER.USER_NAME
+ " VARCHAR(40)," + TB_USER.PASS_WORD + " VARCHAR(40))";
}
3.疑问
1.为什么要使用线程锁?
限制并发线程访问同一个对象中的这个synchronized(this)同步代码块时,同一时间内只能有一个线程得到执行,另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
2.是否有必要使用AtomicInteger计数?
假设我们在ThreadA、ThreadB中对DBManager中的getWriteDatabase()方法进行操作,那么我们获取到的是相同的数据库连接对象,那么在不加AtomicInteger计数的情况下,我们调用closeDatabase()会直接执行数据库连接关闭功能,那么在ThreadB中再次使用该对象的时候就会抛出以下异常:
java.lang.IllegalStateException: attempt to re-open an already-closed object: SQLiteDatabase(尝试重新打开一个已经关闭的对象).
所以使用AtomicInteger来进行计数,在每获取一次数据库连接时自增统计,在调用数据库连接关闭方法时,通过自减判断是否还有引用,如果在别处已无引用,则关闭该连接。
建议阅读:
多线程下访问数据库
JAVA多线程与并发学习总结声明:如本文有不正之处,还望各位不吝赐教,多谢^_^