公众号推荐
微信公众号搜"智元新知" 关注 微信扫一扫可直接关注哦!
SQLite的基本信息和使用过程
上次我向大家介绍了sql ite的基本信息和使用过程,相信朋友们对sql ite已经有所了解了,那今天呢,我就和大家分享 一下在Android中如何使用sql ite。
现在的主流移动设备像Android、iPhone等都使用sql ite作为复杂数据的存储引擎,在我们为移动设备开发应用程序时,也许就要使用到sql ite来存储我们大量的数据,所以我们就需要掌握移动设备上的sql ite开发技巧。对于Android平台来说,系统内置了丰富的API来供开发人员操作sql ite,我们可以轻松的完成对数据的存取。
下面就向大家介绍一下sql ite常用的操作方法 ,为了方便,我将代码 写在了Activity的onCreate中:
@Override
protected void onCreate(BundlesavedInstanceState){
super .onCreate(savedInstanceState);
sql iteDatabasedb=openor CreateDatabase("test.db" ,Context.MODE_PRIVATE, null );
db.execsql ("DROPTABLEIFEXISTSperson" );//如果数据库 中存在address_book表,就把它从数据库 中drop掉。
db.execsql ("CREATETABLEperson(_idINTEGERPRIMARYKEYAUTOINCREMENT,nameVARCHAR,ageSMALLINT)" );
Personperson=new Person();
person.name="john" ;
person.age=30 ;
//插入数据
db.execsql ("INSERTINTOpersonVALUES(NULL,?,?)" ,153); background-color:inherit; font-weight:bold">new Object[]{person.name,person.age});
person.name="david" ;
person.age=33 ;
//ContentValues以键值对的形式存放数据
ContentValuescv=new ContentValues();
cv.put("name" ,person.name);
cv.put("age" ,person.age);
//插入ContentValues中的数据
db.insert("person" ,153); background-color:inherit; font-weight:bold">null ,cv);
cv= cv.put("age" , 35 );
//更新数据
db.update("person" ,cv, "name=?" ,153); background-color:inherit; font-weight:bold">new String[]{ "john" });
Cursorc=db.rawQuery("SELECT*FROMpersonWHEREage>=?" ,153); background-color:inherit; font-weight:bold">new String[]{ "33" });
while(c.moveto Next()){
int_id=c.getInt(c.getColumnIndex( "_id" ));
Stringname=c.getString(c.getColumnIndex("name" ));
intage=c.getInt(c.getColumnIndex( "age" ));
Log.i("db" , "_id=>" +_id+ ",name=>" +name+ ",age=>" +age);
}
c.close();
//删除 数据
db.delete("person" , "age<?" ,153); background-color:inherit; font-weight:bold">new String[]{ "35" });
//关闭 当前数据库
db.close();
//删除 test.db数据库
//deleteDatabase("test.db");
}
在执行完上面的代码 后,系统就会在/data/data/[PACKAGE_NAME]/databases目录下生成 一个 “test.db”的数据库 文件 ,如图:
上面的代码 中基本上囊括了大部分的数据库 操作;对于添加 、更新和删除 来说,我们都可以使用
db.executesql (Stringsql );
db.executesql (Stringsql ,Object[]bindArgs);
除了统一的形式之外,他们还有各自的操作方法 :
db.insert(Stringtable,StringnullColumnHack,ContentValuesvalues);
db.update(Stringtable,Contentvaluesvalues,StringwhereClause,StringwhereArgs);
db.delete(Stringtable,StringwhereArgs);
以上三个方法 的第一个 参数都是表示要操作的表名;insert中的第二个参数表示如果插入的数据每一列都为空的话,需要指定此行中某一列的名称 ,系统将此列设置为NULL,不至于出现错误 ;insert中的第三个参数是ContentValues类型的变量,是键值对组成的Map,key代表列名,value代表该列要插入的值;update的第二个参数也很类似,只不过它是更新该字段key为最新的value值,第三个参数whereClause表示WHERE表达式,比如“age > ? and age < ?”等,最后的whereArgs参数是占位符的实际参数值;delete方法 的参数也是一样。
下面来说说查询 操作。查询 操作相对于上面的几种操作要复杂些,因为我们经常要面对着各种各样的查询 条件,所以系统也考虑到这种复杂性,为我们提供了较为丰富的查询 形式:
db.rawQuery(Stringsql ,String[]selectionArgs);
db.query(Stringtable,String[]columns,Stringselection,String[]selectionArgs,StringgroupBy,Stringhaving,StringorderBy);
db.query(Stringtable,StringorderBy,Stringlimit);
db.query(Stringdis tinct,Stringtable,Stringlimit);
上面几种都是常用的查询 方法 ,第一种最为简单,将所有的sql 语句都组织到一个 字符串中,使用占位符代替实际参数,selectionArgs就是占位符实际参数集;下面的几种参数都很类似,columns表示要查询 的列所有名称 集,selection表示WHERE之后的条件语句,可以使用占位符,groupBy指定分组的列名,having指定分组条件,配合groupBy使用,orderBy指定排序的列名,limit指定分页 参数,dis tinct可以指定“true”或“false”表示要不要过滤重复值。需要注意的是,selection、groupBy、having、orderBy、limit这几个参数中不包括 “WHERE”、“GROUP BY”、“HAVING”、“ORDER BY”、“LIMIT”等sql 关键字。
最后,他们同时返回一个 Cursor对象,代表数据集的游标,有点类似于JavaSE中的ResultSet。
下面是Cursor对象的常用方法 :
c.move( int offset);
c.moveto Firs t();
c.moveto Last();
c.moveto Position(int position);
c.moveto PrevIoU s();
c.moveto Next();
c.isFirs t();
c.isLast();
c.isBeforeFirs t();
c.isAfterLast();
c.isNull(int columnIndex);
c.isClosed();
c.getCount();
c.getPosition();
c.getColumnIndex(StringcolumnName);
c.getString(//返回当前行指定列的值
在上面的代码 示例中,已经用到了这几个常用方法 中的一些,关于更多的信息,大家可以参考官方文档中的说明。
最后当我们完成了对数据库 的操作后,记得调用 sql iteDatabase的close()方法 释放数据库 连接,否则容易出现sql iteException。
上面就是sql ite的基本应用,但在实际开发中,为了能够更好的管理和维护数据库 ,我们会封装一个 继承自sql iteOpenHelper类的数据库 操作类,然后以这个类为基础,再封装我们的业务逻辑方法 。
下面,我们就以一个 实例来讲解具体的用法 ,我们新建一个 名为db的项目,结构如下:
其中DBHelper继承了sql iteOpenHelper,作为维护和管理数据库 的基类,DBManager是建立在DBHelper之上,封装了常用的业务方法 ,Person是我们的person表对应的JavaBean,MainActivity就是我们显示 的界面。
下面我们先来看一下DBHelper:
packagecom.scott .db;
importandroid.content.Context;
importandroid.database.sql ite.sql iteDatabase;
importandroid.database.sql ite.sql iteOpenHelper;
publicclass DBHelper extends sql iteOpenHelper{
privatestatic final StringDATABASE_NAME= "test.db" ;
finalint DATABASE_VERSION= 1 ;
publicDBHelper(Contextcontext){
//CursorFactory设置为null,使用默 认值
super(context,DATABASE_NAME,DATABASE_VERSION);
}
//数据库 第一次被创建时onCreate会被调用
@Override
voidonCreate(sql iteDatabasedb){
db.execsql ("CREATETABLEIFNOTEXISTSperson" +
"(_idINTEGERPRIMARYKEYAUTOINCREMENT,ageINTEGER,infoTEXT)" );
//如果DATABASE_VERSION值被改为2,系统发现现有数据库 版本不同,即会调用 onUpgrade
void onUpgrade(sql iteDatabasedb,153); background-color:inherit; font-weight:bold">int oldVersion,153); background-color:inherit; font-weight:bold">int newVersion){
db.execsql ("ALTERTABLEpersonADDCOLUMNotherSTRING" );
}
正如上面所述,数据库 第一次创建时onCreate方法 会被调用 ,我们可以执行创建表的语句,当系统发现版本变化之后,会调用 onUpgrade方法 ,我们可以执行修改 表结构等语句。
为了方便我们面向对象的使用数据,我们建一个 Person类,对应person表中的字段,如下:
classPerson{
int_id;
publicStringname;
intage;
publicStringinfo;
publicPerson(){
publicPerson(Stringname,153); background-color:inherit; font-weight:bold">int age,Stringinfo){
this.name=name;
this.age=age;
this.info=info;
}
然后,我们需要一个 DBManager,来封装我们所有的业务方法 ,代码 如下:
importjava.util.ArrayList;
importjava.util.List;
importandroid.content.ContentValues;
importandroid.database.Cursor;
importandroid.database.sql ite.sql iteDatabase;
classDBManager{
privateDBHelperhelper;
privatesql iteDatabasedb;
publicDBManager(Contextcontext){
helper=new DBHelper(context);
//因为getWritableDatabase内部调用 了mContext.openor CreateDatabase(mName,mFactory);
//所以要确保context已初始化,我们可以把实例化DBManager的步骤放在Activity的onCreate里
db=helper.getWritableDatabase();
/**
*addpersons
*@para mpersons
*/
voidadd(List<Person>persons){
db.beginTransaction();
try{
for(Personperson:persons){
db.execsql ("INSERTINTOpersonVALUES(null,person.age,person.info});
db.setTransactionSuccessful();
}finally {
db.endTransaction();
*updateperson'sage
*@para mperson
voidupdateAge(Personperson){
db.update("person" ,153); background-color:inherit; font-weight:bold">new String[]{person.name});
*deleteoldperson
voiddeleteOldPerson(Personperson){
db.delete("person" , "age>=?" ,153); background-color:inherit; font-weight:bold">new String[]{String.valueOf(person.age)});
*queryallpersons,returnlist
*@returnList<Person>
publicList<Person>query(){
ArrayList<Person>persons=new ArrayList<Person>();
Cursorc=queryTheCursor();
person._id=c.getInt(c.getColumnIndex("_id" ));
person.name=c.getString(c.getColumnIndex("name" ));
person.age=c.getInt(c.getColumnIndex("age" ));
person.info=c.getString(c.getColumnIndex("info" ));
persons.add(person);
returnpersons;
/**
*@returnCursor
*/
publicCursorqueryTheCursor(){
Cursorc=db.rawQuery("SELECT*FROMperson" ,153); background-color:inherit; font-weight:bold">return c;
*closedatabase
voidcloseDB(){
db.close();
}
我们在DBManager构造方法 中实例化DBHelper并获取 一个 sql iteDatabase对象,作为整个应用的数据库 实例;在添加 多个Person信息时,我们采用了事务处理,确保数据完整性;最后我们提供了一个 closeDB方法 ,释放数据库 资源,这一个 步骤在我们整个应用关闭 时执行,这个环节容易被忘记,所以朋友们要注意。
我们获取 数据库 实例时使用了getWritableDatabase()方法 ,也许朋友们会有疑问,在getWritableDatabase()和getReadableDatabase()中,你为什么选择前者作为整个应用的数据库 实例呢?在这里 我想和大家着重分析一下这一点。
我们来看一下sql iteOpenHelper中的getReadableDatabase()方法 :
synchronizedsql iteDatabasegetReadableDatabase(){
if(mDatabase!= null &&mDatabase.isopen() ){
//如果发现mDatabase不为空并且已经打开则直接返回
returnmDatabase;
if(mIsInitializing){
//如果正在初始化则抛出异常
thrownew Illegal StateException( "getReadableDatabasecalledrecursively" );
//开始实例化数据库 mDatabase
//注意这里是调用 了getWritableDatabase()方法
returngetWritableDatabase();
catch(sql iteExceptione){
if(mName== null )
throwe;
Log.e(TAG,"Could n'topen" +mName+ "forwriting(willtryread-only):" ,e);
//如果无法以可读写模式打开数据库 则以只读方式打开
sql iteDatabasedb=null ;
mIsInitializing=true ;
Stringpath=mContext.getDatabasePath(mName).getPath();
//以只读方式打开数据库
db=sql iteDatabase.openDatabase(path,mFactory,sql iteDatabase.OPEN_READONLY);
if(db.getVersion()!=mNewVersion){
newsql iteException("Can'tupgraderead-onlydatabasefromversion" +db.getVersion()+ "to"
+mNewVersion+":" +path);
onop en(db);
Log.w(TAG,"Opened" +mName+ "inread-onlymode" );
mDatabase=db;
returnmDatabase;
}finally {
false;
if(db!= null &&db!=mDatabase)
}
在getReadableDatabase()方法 中,首先判断是否已存在数据库 实例并且是打开状态,如果是,则直接返回该实例,否则试图获取 一个 可读写模式的数据库 实例,如果遇到磁盘空间已满等情况获取 失败的话,再以只读模式打开数据库 ,获取 数据库 实例并返回,然后为mDatabase赋值为最新打开的数据库 实例。既然有可能调用 到getWritableDatabase()方法 ,我们就要看一下了:
synchronizedsql iteDatabasegetWritableDatabase(){
null&&mDatabase.isopen() &&!mDatabase.isReadOnly()){
//如果mDatabase不为空已打开并且不是只读模式则返回该实例
newIllegal StateException( "getWritableDatabasecalledrecursively" );
//Ifwehavearead-onlydatabaSEO pen,someoneCould beusingit
//(thoughthey shouldn't),whichwouldcausealocktobeheldon
//thefile,andourattemptstoopenthedatabaseread-writewould
//failwaitingforthefilelock.Topreventthat,weacquirethe
//lockontheread-onlydatabase,whichshutsoutotherusers.
booleansuccess= sql iteDatabasedb=null ;
//如果mDatabase不为空则加锁阻止其他的操作
mDatabase.lock();
null){
db=sql iteDatabase.create(null );
else{
//打开或创建数据库
db=mContext.openor CreateDatabase(mName,0 ,mFactory);
//获取 数据库 版本(如果刚创建的数据库 ,版本为0)
intversion=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);
//设置事务成功
success=return db;
mIsInitializing=false ;
if(success){
//打开成功
null){
//如果mDatabase有值则先关闭
mDatabase.close();
catch(Exceptione){
mDatabase.unlock();
mDatabase=db;
//打开失败的情况:解锁、关闭
mDatabase.unlock();
}
大家可以看到,几个关键步骤是,首先判断mDatabase如果不为空已打开并不是只读模式则直接返回,否则如果mDatabase不为空则加锁,然后开始打开或创建数据库 ,比较版本,根据版本号来调用 相应的方法 ,为数据库 设置新版本号,最后释放旧的不为空的mDatabase并解锁,把新打开的数据库 实例赋予mDatabase,并返回最新实例。
看完上面的过程之后,大家或许就清楚了许多,如果不是在遇到磁盘空间已满等情况,getReadableDatabase()一般都会返回和getWritableDatabase()一样的数据库 实例,所以我们在DBManager构造方法 中使用getWritableDatabase()获取 整个应用所使用的数据库 实例是可行的。当然如果你真的担心这种情况会发生,那么你可以先用getWritableDatabase()获取 数据实例,如果遇到异常,再试图用getReadableDatabase()获取 实例,当然这个时候你获取 的实例只能读不能写了。
最后,让我们看一下如何使用这些数据操作方法 来显示 数据,下面是MainActivity.java的布局文件 和代码 :
<? xml version = "1.0" encoding = "utf-8" ?>
< LinearLayout xmlns:android = "http://schemas.android.com/apk/res/android"
android:orientation = "vertical"
android:layout_width = "fill_parent"
android:layout_height = "fill_parent" >
Button
android:layout_width = "fill_parent"
android:layout_height = "wrap_content"
android:text = "add"
android:onClick = "add" />
Button
android:layout_height = "wrap_content"
android:text = "update"
android:onClick = "update" />
android:text = "delete"
android:onClick = "delete" android:text = "query"
android:onClick = "query" android:text = "queryTheCursor"
android:onClick = "queryTheCursor" ListView
android:id = "@+id/listView"
android:layout_height = "wrap_content" </ LinearLayout >
importjava.util.HashMap;
importjava.util.List;
importjava.util.Map;
importandroid.app.Activity;
importandroid.database.Cursor;
importandroid.database.CursorWrapper;
importandroid.os.Bundle;
importandroid.view.View;
importandroid.widget.ListView;
importandroid.widget.SimpleAdapter;
importandroid.widget.SimpleCursorAdapter;
classMainActivity extends Activity{
privateDBManagermgr;
privateListViewlistView;
setContentView(R.layout.main);
listView=(ListView)findViewById(R.id.listView);
//初始化DBManager
mgr=new DBManager( this );
voidonDestroy(){
super.onDestroy();
//应用的最后一个 Activity关闭 时应释放DB
mgr.closeDB();
voidadd(Viewview){
ArrayList<Person>persons=new ArrayList<Person>();
Personperson1=new Person( "Ella" ,0); background-color:inherit">22 , "livelygirl" );
Personperson2=new Person( "Jenny" , "beautifulgi rl" );
Personperson3=new Person( "Jessica" ,0); background-color:inherit">23 , "sexygirl" );
Personperson4=new Person( "Kelly" , "hotbaby" );
Personperson5=new Person( "Jane" ,0); background-color:inherit">25 , "aprettywoman" );
persons.add(person1);
persons.add(person2);
persons.add(person3);
persons.add(person4);
persons.add(person5);
mgr.add(persons);
voidupdate(Viewview){
person.name="Jane" ;
mgr.updateAge(person);
voiddelete(Viewview){
Personperson=new Person();
mgr.deleteOldPerson(person);
voidquery(Viewview){
List<Person>persons=mgr.query();
ArrayList<Map<String,String>>list=new ArrayList<Map<String,String>>();
HashMap<String,String>map=new HashMap<String,String>();
map.put("name" ,person.name);
map.put("info" ,person.age+ "yearsold," +person.info);
list.add(map);
SimpleAdaptera dapter=new SimpleAdapter( this ,list,android.R.layout.simple_list_item_2,
newString[]{ "name" , "info" },153); background-color:inherit; font-weight:bold">new int []{android.R.id.text1,android.R.id.text2});
listView.setAdapter(adapter);
voidqueryTheCursor(Viewview){
Cursorc=mgr.queryTheCursor();
startManagingCursor(c);
CursorWrappercursorWrapper=new CursorWrapper(c){
publicStringgetString( int columnIndex){
//将简介前加上 年龄
if(getColumnName(columnIndex).equals( "info" )){
intage=getInt(getColumnIndex( "age" ));
returnage+ "yearsold," + super .getString(columnIndex);
return };
//确保查询 结果中有"_id"列
SimpleCursorAdaptera dapter=new SimpleCursorAdapter( cursorWrapper, ListViewlistView=(ListView)findViewById(R.id.listView);
listView.setAdapter(adapter);
}
这里需要注意的是SimpleCursorAdapter的应用,当我们使用这个适配器时,我们必须先得到一个 Cursor对象,这里面有几个问题:如何管理Cursor的生命周期,如果包装Cursor,Cursor结果集都需要注意什么。
如果手动去管理Cursor的话会非常的麻烦,还有一定的风险,处理不当的话运行期间就会出现异常,幸好Activity为我们提供了startManagingCursor(Cursor cursor)方法 ,它会根据Activity的生命周期去管理当前的Cursor对象,下面是该方法 的说明:
*Thismethodallowstheactivitytotakecareofmanagingthegiven
*{@linkCursor}'slifecycleforyoubasedontheactivity'slifecycle.
*Thatis,whentheactivityisst oppeditwill automaticallycall
*{@linkCursor#deactivate}onthegivenCursor,andwhenitislaterrestarted
*itwill call{@linkCursor#requery}foryou.Whentheactivityis
*destroyed,allmanagedCursorswillbeclosedautomatically.
*
*@para mcTheCursortobemanaged.
*@see#managedQuery(android.net.Uri,String[],String,String)
*@see#stopManagingCursor
*/
文中提到,startManagingCursor方法 会根据Activity的生命周期去管理当前的Cursor对象的生命周期,就是说当Activity停止时他会自动 调用 Cursor的deactivate方法 ,禁用游标,当Activity重新回到屏幕时它会调用 Cursor的requery方法 再次查询 ,当Activity摧毁时,被管理的Cursor都会自动 关闭 释放。
如何包装Cursor:我们会使用到CursorWrapper对象去包装我们的Cursor对象,实现我们需要的数据转换工作,这个CursorWrapper实际上是实现了Cursor接口。我们查询 获取 到的Cursor其实是Cursor的引用,而系统实际返回给我们的必然是Cursor接口的一个 实现类的对象实例,我们用CursorWrapper包装这个实例,然后再使用SimpleCursorAdapter将结果显示 到列表上。
Cursor结果集需要注意些什么:一个 最需要注意的是,在我们的结果集中必须要包含一个 “_id”的列,否则SimpleCursorAdapter就会翻脸不认人,为什么一定要这样呢?因为这源于sql ite的规范,主键以“_id”为标准。解决 办法有三:第一,建表时根据规范去做;第二,查询 时用别名,例如:SELECT id AS _id FROM person;第三,在CursorWrapper里做文章 :
CursorWrappercursorWrapper= int getColumnIndexOrThrow(StringcolumnName) throws Illegal ArgumentException{
if(columnName.equals( "_id" )){
super.getColumnIndex( "id" );
super.getColumnIndexOrThrow(columnName);
};
如果试图从CursorWrapper里获取 “_id”对应的列索引,我们就返回查询 结果里“id”对应的列索引即可。
最后我们来看一下结果如何:
原文地址:https://www.jb51.cc/sqlite/199972.html
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。