您的位置:首页 > 移动开发 > Android开发

Android数据存储(七) SQLite使用注意和SQL语句

2012-08-27 21:22 741 查看
1、数据类型的问题

SQLite内部只支持NULL、INTEGER、REAL(浮点数)、TEXT(文本)和BLOB(大二进制对象)这5种数据类型,但实际上SQLite完全可以接受varchar(n)、char(n)、decimal(p,s)等数据类型,只不过SQLite会在原酸或保存时将他们转换为上面5种数据类型中相应的类型。

除此之外,SQLite还有一个特点:它允许把各种类型的数据保存到任何类型字段中,开发者可以不用关心声明该字段所使用的数据类型。例如程序可以把字符串类型的值存入到INTEGER类型的字段中,也可以把数值数据类型的值存入到布尔类型的字段中。。。。但是有一中情况例外:定义为INTEGER PRIMARY KEY的字段只能存储64位整数,当向这种字段保存除整数意外的其他类型的数据时,SQLite会产生错误。

并且,可以向字符串字段中插入任意长度的字符串,即使声明varchar(20)加以限制。

2、数据库何时关闭问题

我们使用SQLiteOpenHelper获取一个SQLiteDatabase数据库对象,然后就可以去使用这个数据库了。那么这个数据库何时关闭呢。一般我们不需要操作一次数据库,就去关闭它,因为这样很耗资源。毕竟这不是像J2EE上面的数据库,J2EE服务器上的数据库会根据访问的用户生成许多数据库连接,所以需要关闭。但是在Android手机上,只有我们当前的应用程序再回有一个数据库的连接,当然不需要用一次关闭一次。

所以,我们只需要完成了所有的数据库操作,或者退出程序的时候,选择一个恰当的实际关闭。

3、SQLiteDatabase对象的获取问题。

我们获取数据库实例时使用了getWritableDatabase()方法,也许朋友们会有疑问,在getWritableDatabase()和getReadableDatabase()中,你为什么选择前者作为整个应用的数据库实例呢?在这里我想和大家着重分析一下这一点。

我们来看一下SQLiteOpenHelper中的getReadableDatabase()方法:

public synchronized SQLiteDatabase getReadableDatabase() {
if (mDatabase != null && mDatabase.isOpen()) {
// 如果发现mDatabase不为空并且已经打开则直接返回
return mDatabase;
}

if (mIsInitializing) {
// 如果正在初始化则抛出异常
throw new IllegalStateException("getReadableDatabase called recursively");
}

// 开始实例化数据库mDatabase

try {
// 注意这里是调用了getWritableDatabase()方法
return getWritableDatabase();
} catch (SQLiteException e) {
if (mName == null)
throw e; // Can't open a temp database read-only!
Log.e(TAG, "Couldn't open " + mName + " for writing (will try read-only):", 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();
}
}


在getReadableDatabase()方法中,首先判断是否已存在数据库实例并且是打开状态,如果是,则直接返回该实例,否则试图获取一个可读写模式的数据库实例,如果遇到磁盘空间已满等情况获取失败的话,再以只读模式打开数据库,获取数据库实例并返回,然后为mDatabase赋值为最新打开的数据库实例。既然有可能调用到getWritableDatabase()方法,我们就要看一下了:

public synchronized SQLiteDatabase getWritableDatabase() {
if (mDatabase != null && mDatabase.isOpen() && !mDatabase.isReadOnly()) {
// 如果mDatabase不为空已打开并且不是只读模式 则返回该实例
return mDatabase;
}

if (mIsInitializing) {
throw new IllegalStateException("getWritableDatabase called recursively");
}

// If we have a read-only database open, someone could be using it
// (though they shouldn't), which would cause a lock to be held on
// the file, and our attempts to open the database read-write would
// fail waiting for the file lock. To prevent that, we acquire the
// lock on the read-only database, which shuts out other users.

boolean success = false;
SQLiteDatabase db = null;
// 如果mDatabase不为空则加锁 阻止其他的操作
if (mDatabase != null)
mDatabase.lock();
try {
mIsInitializing = true;
if (mName == null) {
db = SQLiteDatabase.create(null);
} else {
// 打开或创建数据库
db = mContext.openOrCreateDatabase(mName, 0, mFactory);
}
// 获取数据库版本(如果刚创建的数据库,版本为0)
int version = db.getVersion();
// 比较版本(我们代码中的版本mNewVersion为1)
if (version != mNewVersion) {
db.beginTransaction();// 开始事务
try {
if (version == 0) {
// 执行我们的onCreate方法
onCreate(db);
} else {
// 如果我们应用升级了mNewVersion为2,而原版本为1则执行onUpgrade方法
onUpgrade(db, version, mNewVersion);
}
db.setVersion(mNewVersion);// 设置最新版本
db.setTransactionSuccessful();// 设置事务成功
} finally {
db.endTransaction();// 结束事务
}
}

onOpen(db);
success = true;
return db;// 返回可读写模式的数据库实例
} finally {
mIsInitializing = false;
if (success) {
// 打开成功
if (mDatabase != null) {
// 如果mDatabase有值则先关闭
try {
mDatabase.close();
} catch (Exception e) {
}
mDatabase.unlock();// 解锁
}
mDatabase = db;// 赋值给mDatabase
} else {
// 打开失败的情况:解锁、关闭
if (mDatabase != null)
mDatabase.unlock();
if (db != null)
db.close();
}
}
}


大家可以看到,几个关键步骤是,首先判断mDatabase如果不为空已打开并不是只读模式则直接返回,否则如果mDatabase不为空则加锁,然后开始打开或创建数据库,比较版本,根据版本号来调用相应的方法,为数据库设置新版本号,最后释放旧的不为空的mDatabase并解锁,把新打开的数据库实例赋予mDatabase,并返回最新实例。
看完上面的过程之后,大家或许就清楚了许多,如果不是在遇到磁盘空间已满等情况,getReadableDatabase()一般都会返回和getWritableDatabase()一样的数据库实例,所以我们在DBManager构造方法中使用getWritableDatabase()获取整个应用所使用的数据库实例是可行的。当然如果你真的担心这种情况会发生,那么你可以先用getWritableDatabase()获取数据实例,如果遇到异常,再试图用getReadableDatabase()获取实例,当然这个时候你获取的实例只能读不能写了。

并且,同一个数据库只会返回一个SQLiteDatabase数据库实例。

4、在Android中对数据库操作语句和方法

SQLite中sql语句和MySql非常类似,完全可以去参照MySql的sql语句。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息