Android中SQLite运用详解-Android-优质IT资源分享社区

admin
管理员
管理员
  • UID1
  • 粉丝33
  • 关注4
  • 发帖数581
  • 社区居民
  • 忠实会员
  • 原创写手
阅读:260回复:0

  Android中SQLite运用详解

楼主#
更多 发布于:2016-05-20 19:04


如今的干流移动设备像Android、iPhone等都运用SQLite作为杂乱数据的存储引擎,在咱们为移动设备开发运用程序时,或许就要运用到SQLite来存储咱们很多的数据,所以咱们就需求掌握移动设备上的SQLite开发技巧。关于Android渠道来说,体系内置了丰厚的API来供开发人员操作SQLite,咱们能够轻松的完结对数据的存取。
下面就向咱们介绍一下SQLite常用的操作办法,为了便利,我将代码写在了Activity的onCreate中:
@Overrideprotected void onCreate(Bundle
savedInstanceState) {      super.onCreate(savedInstanceState);    
 //翻开或创立test.db数据库      SQLiteDatabase db = openOrCreateDatabase("test.db",
Context.MODE_PRIVATE, null);      db.execSQL("DROP TABLE IF EXISTS person");    
 //创立person表      db.execSQL("CREATE TABLE person (_id INTEGER PRIMARY KEY
AUTOINCREMENT, name VARCHAR, age SMALLINT)");      Person person = new Person();
     person.name = "john";      person.age = 30;      //刺进数据    
 db.execSQL("INSERT INTO person VALUES (NULL, ?, ?)", new Object[]{person.name,
person.age});      person.name = "david";      person.age = 33;    
 //ContentValues以键值对的办法寄存数据      ContentValues cv = new ContentValues();    
 cv.put("name", person.name);      cv.put("age", person.age);    
 //刺进ContentValues中的数据      db.insert("person", null, cv);      cv = new
ContentValues();      cv.put("age", 35);      //更新数据      db.update("person",
cv, "name = ?", new String[]{"john"});      Cursor c = db.rawQuery("SELECT *
FROM person WHERE age >= ?", new String[]{"33"});      while (c.moveToNext())
{           int _id = c.getInt(c.getColumnIndex("_id"));           String name =
c.getString(c.getColumnIndex("name"));           int age =
c.getInt(c.getColumnIndex("age"));           Log.i("db", "_id=>" + _id + ",
name=>" + name + ", age=>" + age);      }      c.close();      //删去数据    
 db.delete("person", "age < ?", new String[]{"35"});      //封闭当时数据库    
 db.close();      //删去test.db数据库      //      deleteDatabase("test.db");}
在履行完上面的代码后,体系就会在/data/data/[PACKAGE_NAME]/databases目录下生成一个“test.db”的数据库文件,如图:

上面的代码中根本上包括了大多数的数据库操作;关于增加、更新和删去来说,咱们都能够运用
db.executeSQL(String sql);db.executeSQL(String
sql, Object[] bindArgs);//sql句子中运用占位符,然后第二个参数是实践的参数集
除了一致的办法以外,他们还有各自的操作办法:
db.insert(String table, String nullColumnHack,
ContentValues values);db.update(String table, Contentvalues values, String
whereClause, String whereArgs);db.delete(String table, String whereClause,
String whereArgs);
以上三个办法的榜首个参数都是表明要操作的表名;insert中的第二个参数表明假如刺进的数据每一列都为空的话,需求指定此行中某一列的称号,体系将此列设置为NULL,不至于呈现过错;insert中的第三个参数是ContentValues类型的变量,是键值对构成的Map,key代表列名,value代表该列要刺进的值;update的第二个参数也很相似,只不过它是更新该字段key为最新的value值,第三个参数whereClause表明WHERE表达式,比方“age
> ? and age < ?”等,最终的whereArgs参数是占位符的实践参数值;delete办法的参数也是相同。
下面来说说查询操作。查询操作相关于上面的几种操作要杂乱些,由于咱们经常要面对着各式各样的查询条件,所以体系也考虑到这种杂乱性,为咱们供给了较为丰厚的查询办法:
db.rawQuery(String sql, String[]
selectionArgs);db.query(String table, String[] columns, String selection,
String[] selectionArgs, String groupBy, String having, String
orderBy);db.query(String table, String[] columns, String selection, String[]
selectionArgs, String groupBy, String having, String orderBy, String
limit);db.query(String distinct, String table, String[] columns, String
selection, String[] selectionArgs, String groupBy, String having, String
orderBy, String limit);
上面几种都是常用的查询办法,榜首种最为简单,将一切的SQL句子都组织到一个字符串中,运用占位符替代实践参数,selectionArgs即是占位符实践参数集;下面的几种参数都很相似,columns表明要查询的列一切称号集,selection表明WHERE以后的条件句子,能够运用占位符,groupBy指定分组的列名,having指定分组条件,合作groupBy运用,orderBy指定排序的列名,limit指定分页参数,distinct能够指定“true”或“false”表明要不要过滤重复值。需求留意的是,selection、groupBy、having、orderBy、limit这几个参数中不包括“WHERE”、“GROUP
BY”、“HAVING”、“ORDER BY”、“LIMIT”等SQL关键词。
最终,他们同时回来一个Cursor目标,代表数据集的游标,有点相似于JavaSE中的ResultSet。
下面是Cursor目标的常用办法:
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);   //回来当时行指定列的值
在上面的代码示例中,现已用到了这几个常用办法中的一些,关于更多的信息,咱们能够参阅官方文档中的阐明。
最终当咱们完结了对数据库的操作后,记住调用SQLiteDatabase的close()办法开释数据库衔接,不然简单呈现SQLiteException。
上面即是SQLite的根本运用,但在实践开发中,为了能够十分好的办理和保护数据库,咱们会封装一个承继自SQLiteOpenHelper类的数据库操作类,然后以这个类为根底,再封装咱们的业务逻辑办法。
下面,咱们就以一个实例来解说详细的用法,咱们新建一个名为db的项目,结构如下:

其间DBHelper承继了SQLiteOpenHelper,作为保护和办理数据库的基类,DBManager是建立在DBHelper之上,封装了常用的业务办法,Person是咱们的person表对应的JavaBean,MainActivity即是咱们显现的界面。
下面咱们先来看一下DBHelper:
package com.scott.db;import
android.content.Context;import android.database.sqlite.SQLiteDatabase;import
android.database.sqlite.SQLiteOpenHelper;public class DBHelper extends
SQLiteOpenHelper {     private static final String DATABASE_NAME = "test.db";  
  private static final int DATABASE_VERSION = 1;     public DBHelper(Context
context) {          //CursorFactory设置为null,运用默认值          super(context,
DATABASE_NAME, null, DATABASE_VERSION);     }     //数据库首次被创立时onCreate会被调用    
@Override     public void onCreate(SQLiteDatabase db) {        
 db.execSQL("CREATE TABLE IF NOT EXISTS person" +  "(_id INTEGER PRIMARY KEY
AUTOINCREMENT, name VARCHAR, age INTEGER, info TEXT)");     }    
//假如DATABASE_VERSION值被改为2,体系发现现有数据库版别不相同,即会调用onUpgrade     @Override     public
void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {        
 db.execSQL("ALTER TABLE person ADD COLUMN other STRING");     }}
正如上面所述,数据库首次创立时onCreate办法会被调用,咱们能够履行创立表的句子,当体系发现版别改变以后,会调用onUpgrade办法,咱们能够履行修正表结构等句子。
为了便利咱们面向目标的运用数据,咱们建一个Person类,对应person表中的字段,如下:
package com.scott.db;public class Person {public
int _id;public String name;public int age;public String info;public Person()
{}public Person(String name, int age, String info) {     this.name = name;    
this.age = age;     this.info = info;}}
然后,咱们需求一个DBManager,来封装咱们一切的业务办法,代码如下:
package com.scott.db;import
java.util.ArrayList;import java.util.List;import
android.content.ContentValues;import android.content.Context;import
android.database.Cursor;import android.database.sqlite.SQLiteDatabase;public
class DBManager {      private DBHelper helper;      private SQLiteDatabase db;
     public DBManager(Context context) {            helper = new
DBHelper(context);          
 //由于getWritableDatabase内部调用了mContext.openOrCreateDatabase(mName, 0, mFactory);
           //所以要保证context已初始化,咱们能够把实例化DBManager的进程放在Activity的onCreate里          
 db = helper.getWritableDatabase();      }      /**        * add persons      
 * @param persons        */      public void add(Listpersons) {        
  db.beginTransaction();  //开端业务           try {                 for (Person
person : persons) {                        db.execSQL("INSERT INTO person
VALUES(null, ?, ?, ?)",                                        new
Object[]{person.name, person.age, person.info});                 }              
  db.setTransactionSuccessful();  //设置业务成功完结           } finally {              
  db.endTransaction();    //完毕业务           }      }      /**        * update
person's age        * @param person        */      public void updateAge(Person
person) {           ContentValues cv = new ContentValues();          
cv.put("age", person.age);           db.update("person", cv, "name = ?", new
String[]{person.name});      }      /**        * delete old person        *
@param person        */      public void deleteOldPerson(Person person) {      
     db.delete("person", "age >= ?", new
String[]{String.valueOf(person.age)});      }      /**        * query all
persons, return list        * @return List*/      public
Listquery() {           ArrayListpersons = new
ArrayList();           Cursor c = queryTheCursor();           while
(c.moveToNext()) {                 Person person = new Person();                
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);    
     }          c.close();          return persons;     }     /**       * query
all persons, return cursor       * @return  Cursor       */     public Cursor
queryTheCursor() {          Cursor c = db.rawQuery("SELECT * FROM person",
null);          return c;     }     /**       * close database       */    
public void closeDB() {          db.close();    
}}
咱们在DBManager结构办法中实例化DBHelper并获取一个SQLiteDatabase目标,作为全部运用的数据库实例;在增加多个Person信息时,咱们选用了业务处理,保证数据完整性;最终咱们供给了一个closeDB办法,开释数据库资本,这一个进程在咱们全部运用封闭时履行,这个环节简单被忘记,所以朋友们要留意。
咱们获取数据库实例时运用了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()获取实例,当然这个时分你获取的实例只能读不能写了。
最终,让咱们看一下怎么运用这些数据操作办法来显现数据,下面是MainActivity.java的布局文件和代码:

package com.scott.db;import
java.util.ArrayList;import java.util.HashMap;import java.util.List;import
java.util.Map;import android.app.Activity;import android.database.Cursor;import
android.database.CursorWrapper;import android.os.Bundle;import
android.view.View;import android.widget.ListView;import
android.widget.SimpleAdapter;import android.widget.SimpleCursorAdapter;public
class MainActivity extends Activity {     private DBManager mgr;     private
ListView listView;     @Override     public void onCreate(Bundle
savedInstanceState) {          super.onCreate(savedInstanceState);        
 setContentView(R.layout.main);          listView = (ListView)
findViewById(R.id.listView);          //初始化DBManager          mgr = new
DBManager(this);     }     @Override     protected void onDestroy() {        
 super.onDestroy();          //运用的最终一个Activity封闭时应开释DB          mgr.closeDB();  
  }     public void add(View view) {          ArrayListpersons = new
ArrayList();          Person person1 = new Person("Ella", 22, "lively
girl");          Person person2 = new Person("Jenny", 22, "beautiful girl");    
     Person person3 = new Person("Jessica", 23, "sexy girl");          Person
person4 = new Person("Kelly", 23, "hot baby");          Person person5 = new
Person("Jane", 25, "a pretty woman");          persons.add(person1);        
 persons.add(person2);          persons.add(person3);        
 persons.add(person4);          persons.add(person5);          mgr.add(persons);
   }    public void update(View view) {         Person person = new Person();  
      person.name = "Jane";         person.age = 30;        
mgr.updateAge(person);    }    public void delete(View view) {         Person
person = new Person();         person.age = 30;        
mgr.deleteOldPerson(person);    }    public void query(View view) {        
Listpersons = mgr.query();         ArrayList<Map> list = new ArrayList<Map>();         for
(Person person : persons) {               HashMapmap = new
HashMap();               map.put("name", person.name);      
        map.put("info", person.age + " years old, " + person.info);            
  list.add(map);         }         SimpleAdapter adapter = new
SimpleAdapter(this, list, android.R.layout.simple_list_item_2,                  
        new String[]{"name", "info"}, new int[]{android.R.id.text1,
android.R.id.text2});         listView.setAdapter(adapter);   }    public void
queryTheCursor(View view) {          Cursor c = mgr.queryTheCursor();        
 startManagingCursor(c); //托付给activity依据自己的生命周期去办理Cursor的生命周期        
 CursorWrapper cursorWrapper = new CursorWrapper(c) {                  @Override
                 public String getString(int columnIndex) {                    
   //将简介前加上年纪                        if
(getColumnName(columnIndex).equals("info")) {                              int
age = getInt(getColumnIndex("age"));                              return age + "
years old, " + super.getString(columnIndex);                        }          
             return super.getString(columnIndex);                  }          };
         //保证查询成果中有"_id"列          SimpleCursorAdapter adapter = new
SimpleCursorAdapter(this, android.R.layout.simple_list_item_2,                  
     cursorWrapper, new String[]{"name", "info"}, new int[]{android.R.id.text1,
android.R.id.text2});          ListView listView = (ListView)
findViewById(R.id.listView);          listView.setAdapter(adapter);    
 }}
这儿需求留意的是SimpleCursorAdapter的运用,当咱们运用这个适配器时,咱们必须先得到一个Cursor目标,这儿面有几个问题:怎么办理Cursor的生命周期,假如包装Cursor,Cursor成果集都需求留意啥。
假如手动去办理Cursor的话会十分的麻烦,还有必定的风险,处理不妥的话运转时期就会呈现反常,幸亏Activity为咱们供给了startManagingCursor(Cursor
cursor)办法,它会依据Activity的生命周期去办理当时的Cursor目标,下面是该办法的阐明:
/** * This method allows the activity to take care
of managing the given * {@link Cursor}'s lifecycle for you based on the
activity's lifecycle. * That is, when the activity is stopped it will
automatically call * {@link Cursor#deactivate} on the given Cursor, and when it
is later restarted * it will call {@link Cursor#requery} for you.  When the
activity is * destroyed, all managed Cursors will be closed automatically. * *
@param c The Cursor to be managed. * * @see #managedQuery(android.net.Uri ,
String[], 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就会翻脸不认人,为何必定要这么呢?由于这源于SQLite的标准,主键以“_id”为标准。解决办法有三:榜首,建表时依据标准去做;第二,查询时用别号,例如: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);     }};
假如企图从CursorWrapper里获取“_id”对应的列索引,咱们就回来查询成果里“id”对应的列索引即可。
最终咱们来看一下成果怎么:











优质IT资源分享社区为你提供此文。
本站有大量优质android教程视频,资料等资源,包含android基础教程,高级进阶教程等等,教程视频资源涵盖传智播客,极客学院,达内,北大青鸟,猎豹网校等等IT职业培训机构的培训教学视频,价值巨大。欢迎点击下方链接查看。

android教程视频
优质IT资源分享社区(www.itziyuan.top)
一个免费,自由,开放,共享,平等,互助的优质IT资源分享网站。
专注免费分享各大IT培训机构最新培训教学视频,为你的IT学习助力!

!!!回帖受限制请看点击这里!!!
!!!资源失效请在此版块发帖说明!!!

[PS:按 CTRL+D收藏本站网址~]

——“优质IT资源分享社区”管理员专用签名~

本版相似帖子

游客