SQLite介绍

来源:互联网 发布:windows cmd ls 编辑:程序博客网 时间:2024/05/19 02:17

1.SQLite 简介

SQLite是一种流行的关系数据库管理系统(Relational Database Managerment System RDBMS.

(请发邮件到 freeget.one@gmail.com  获得最新翻强软件。)

2.SQLite数据类型

SQLiteSQLite3支持 NULLINTEGERREAL浮点数字TEXT(字符串文本)BLOB(二进制对象)数据类型。

虽然它支持的类型虽然只有五种,但实际上sqlite3也接受varchar(n)char(n)decimal(p,s) 等数据类型,只不过在运算或保存时会转成对应的五种数据类型。 SQLite最大的特点是你可以保存任何类型的数据到任何字段中,无论这列声明的数据类型是什么。例如可以在Integer字段中存放字符串或者在布尔型字段中存放浮点数,或者在字符型字段中存放日期型值。 但有一种情况例外:定义为INTEGER PRIMARY KEY的字段只能存储64位整数, 当向这种字段中保存除整数以外的数据时,将会产生错误。 另外, SQLite 在解析CREATE TABLE 语句时,会忽略 CREATE TABLE 语句中跟在字段名后面的数据类型信息,如下面语句会忽略 name字段的类型信息:

CREATE TABLE person (personid integer primary key autoincrement, name varchar(20))

 

SQLite可以解析大部分标准SQL语句如:

查询语句:select * from 表名 where 条件子句 group by 分组字句 having ... order by 排序子句

如:select * from person

        select * from person order by id desc

        select name from person group by name having count(*)>1

分页SQLmysql类似,下面SQL语句获取5条记录,跳过前面3条记录

select * from Account limit 5 offset 3 或者 select * from Account limit 3,5

插入语句:insert into 表名(字段列表) values(值列表)如: insert into person(name, age) values(‘传智’,3)

更新语句:update 表名 set 字段名= where 条件子句。如:update person set name=‘传智‘ where id=10

删除语句:delete from 表名 where 条件子句。如:delete from person  where id=10

 

3、SQLiteOpenHelper类(public abstract class SQLiteOpenHelper

a、用来实现创建、打开和升级数据库的最佳实践模式。通过实现SQLiteOpenHelper,可以隐藏那些用于决定一个数据库在被打开之前是否需要被创建或者升级的逻辑。

abstract void  onCreate(SQLiteDatabase db) :该函数第一次创建数据库的时候执行,实际上是在第一次得到SQLiteDatabase对象的时候才调用这个方法。

abstract void  onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) :Called when the database needs to be upgraded. The implementation should use this method to drop tables, add tables, or do anything else it needs to upgrade to the new schema version.数据库的版本发生变化时会被调用

b.调用SQLiteOpenHelpergetWritableDatabase()或者getReadableDatabase()方法获取用于操作数据库的SQLiteDatabase实例。

synchronized SQLiteDatabase  getReadableDatabase() 先以读写方式打开数据库,如果数据库的磁盘空间满了,就会打开失败,当打开失败后会继续尝试以只读方式打开数据库。

synchronized SQLiteDatabase  getWritableDatabase() 以读写方式打开数据库,一旦数据库的磁盘空间满了,数据库就只能读而不能写,倘若使用的是getWritableDatabase() 方法就会出错。

c、由于磁盘空间或者权限问题,对getWritableDatabase() 的调用可能会失败,所以较好的做法是提供一个对getReadableDatabase方法的回退(fallback)

dbHelper = new myDbHelper(context, DATABASE_NAME, null, DATABASE_VERSION);

 

SQLiteDatabase db;

try {

  db = dbHelper.getWritableDatabase();

}

catch (SQLiteException ex){

  db = dbHelper.getReadableDatabase();

}

d.onUpgrade()方法在数据库版本每次发生变化时都会把用户手机上的数据库表删除,然后再重新创建。一般在实际项目中是不能这样做的,正确的做法是在更新数据库表结构时,还要考虑用户存放于数据库中的数据不会丢失。

public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

               db.execSQL("DROP TABLE IF EXISTS person");

               onCreate(db);

}

4.SQLiteDatabase类(public class SQLiteDatabase extends SQLiteClosable

该类封装了一些操作数据库的API,使用该类可以完成对数据进行添加(Create)、查询(Retrieve)、更新(Update)和删除(Delete)操作(这些操作简称为CRUD)。

void  execSQL(String sql) 

void  execSQL(String sql, Object[] bindArgs)

Cursor  rawQuery(String sql, String[] selectionArgs)

Cursor  rawQueryWithFactory(SQLiteDatabase.CursorFactory cursorFactory, String sql, String[] selectionArgs, String editTable)

execSQL()方法的使用例子:

SQLiteDatabase db = ....;

db.execSQL("insert into person(name, age) values('传智播客', 4)");

db.close();

执行上面SQL语句会往person表中添加进一条记录,在实际应用中, 语句中的传智播客这些参数值应该由用户输入界面提供,如果把用户输入的内容原样组拼到上面的insert语句, 当用户输入的内容含有单引号时,组拼出来的SQL语句就会存在语法错误。要解决这个问题需要对单引号进行转义,也就是把单引号转换成两个单引号。有些时候用户往往还会输入像“ & ”这些特殊SQL符号,为保证组拼好的SQL语句语法正确,必须对SQL语句中的这些特殊SQL符号都进行转义,显然,对每条SQL语句都做这样的处理工作是比较烦琐的。 SQLiteDatabase类提供了一个重载后的execSQL(String sql, Object[] bindArgs)方法,使用这个方法可以解决前面提到的问题,因为这个方法支持使用占位符参数(?)。使用例子如下:

SQLiteDatabase db = ....;

db.execSQL("insert into person(name, age) values(?,?)", new Object[]{"传智播客", 4});

db.close();

execSQL(String sql, Object[] bindArgs)方法的第一个参数为SQL语句,第二个参数为SQL语句中占位符参数的值,参数值在数组中的顺序要和占位符的位置对应。

 

Android提供:

Cursor query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit)

Query the given table, returning a Cursor over the result set.

Cursor query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy)

Query the given table, returning a Cursor over the result set.

Cursor query(boolean distinct, String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit)

Query the given URL, returning a Cursor over the result set.

1、一个可选的布尔值,用来说明返回的值是否只包含唯一的值

2、要查询的表的名称

3、一个投影,它作为一个字符数组,列出了只包含在结果集中的类

4、一个where子句定义了要返回的行,可以在其中包含“?”通配符,它将会被存储在选择参数中的值替换。

5、一个选择参数字符串的数组,它将会替换where子句中的“?”。

6.一个group by子句,用来定义返回的行的分组方式。

7.一个having过滤器,如果包含了一个group by子句,则这个过滤器会定义要包含哪些行的分组。

8.一个字符串,用来描述要返回的行的顺序。

9.一个可选的字符串,用来定义对返回的行数的限制。

5.Cursor接口(public interface Cursor

是一个游标接口,相当于指向底层数据中结果集的指针,游标是在数据库查询的结果集中对位置(行)的控制方式。

abstract boolean moveToFirst() :把游标移动到查询结果集的第一行。

abstract boolean  moveToNext() :把游标移动到下一行

abstract boolean  moveToPrevious() :把游标移动到前一行

abstract int getCount()  :返回结果集的行数

abstract boolean moveToPosition(int position) :移动到指定行的游标

abstract int getPosition()  :返回当前的游标的位置

abstract boolean isFirst() :判断是否是第一条记录

abstract boolean  isLast()  :判断是否是最后一条记录

Cursor中提取结果

int GOLD_HOARDED_COLUMN = 2;

Cursor myGold = myDatabase.query("GoldHoards", null, null, null, null, null, null);

float totalHoard = 0f;

// Make sure there is at least one row.

if (myGold.moveToFirst()) {

  // Iterate over each cursor.

  do {

    float hoard = myGold.getFloat(GOLD_HOARDED_COLUMN);

    totalHoard += hoard;

  } while(myGold.moveToNext());

}

 

6.Android数据库时需要注意

A、文件(例如位图、或者音频文件)通常是不存储在数据库文件中的。

B、强烈建议所有表都应该包含一个自动增加的键域,从而为每一行提供唯一的索引。

7、第一次调用getWritableDatabase()getReadableDatabase()方法后SQLiteOpenHelper会缓存当前的SQLiteDatabase实例,SQLiteDatabase实例正常情况下会维持数据库的打开状态,所以在你不再需要SQLiteDatabase实例时,请及时调用close()方法释放资源。一旦SQLiteDatabase实例被缓存,多次调用getWritableDatabase()getReadableDatabase()方法得到的都是同一实例。

8使用SQLiteDatabasebeginTransaction()方法可以开启一个事务,程序执行到endTransaction() 方法时会检查事务的标志是否为成功,如果为成功则提交事务,否则回滚事务。当应用需要提交事务,必须在程序执行到endTransaction()方法之前使用setTransactionSuccessful() 方法设置事务的标志为成功,如果不调用setTransactionSuccessful() 方法,默认会回滚事务。使用例子如下:
 SQLiteDatabase db = ....;

db.beginTransaction();//开始事务

try {

    db.execSQL("insert into person(name, age) values(?,?)", new Object[]{"传智播客", 4});

    db.execSQL("update person set name=? where personid=?", new Object[]{"传智", 1});

    db.setTransactionSuccessful();//调用此方法会在执行到endTransaction() 时提交当前事务,如果不调用此方法会回滚事务

} finally {

    db.endTransaction();//由事务的标志决定是提交事务,还是回滚事务

}

db.close();

上面两条SQL语句在同一个事务中执行。

9、标准数据库adapter类的实现代码:

import android.content.Context;

import android.database.*;

import android.database.sqlite.*;

import android.database.sqlite.SQLiteDatabase.CursorFactory;

import android.util.Log;

 

public class MyDBAdapter {

  private static final String DATABASE_NAME = "myDatabase.db";

  private static final String DATABASE_TABLE = "mainTable";

  private static final int DATABASE_VERSION = 1;

 

  // The index (key) column name for use in where clauses.

Where子句中使用的索引(键)列的名称

  public static final String KEY_ID="_id";

 

  // The name and column index of each column in your database. 

数据库每个列的名称和索引

public static final String KEY_NAME="name";

  public static final int NAME_COLUMN = 1;

  // TODO: Create public field for each column in your table.

 

  // SQL Statement to create a new database.

  private static final String DATABASE_CREATE = "create table " +

    DATABASE_TABLE + " (" + KEY_ID +

    " integer primary key autoincrement, " +

    KEY_NAME + " text not null);";

 

  // Variable to hold the database instance

  private SQLiteDatabase db;

  // Context of the application using the database.

  private final Context context;

  // Database open/upgrade helper

  private myDbHelper dbHelper;

 

  public MyDBAdapter(Context _context) {

    context = _context;

    dbHelper = new myDbHelper(context, DATABASE_NAME, null, DATABASE_VERSION);

  }

 

  public MyDBAdapter open() throws SQLException {

    db = dbHelper.getWritableDatabase();

    return this;

  }

 

  public void close() {

      db.close();

  }

 

  public int insertEntry(MyObject _myObject) {

    // TODO: Create a new ContentValues to represent my row

    // and insert it into the database.

    return index;

  }

 

  public boolean removeEntry(long _rowIndex) {

    return db.delete(DATABASE_TABLE, KEY_ID + "=" + _rowIndex, null) > 0;

  }

 

  public Cursor getAllEntries () {

    return db.query(DATABASE_TABLE, new String[] {KEY_ID, KEY_NAME},

                    null, null, null, null, null);

  }

 

  public MyObject getEntry(long _rowIndex) {

    // TODO: Return a cursor to a row from the database and

    // use the values to populate an instance of MyObject

    return objectInstance;

  }

 

  public boolean updateEntry(long _rowIndex, MyObject _myObject) {

    // TODO: Create a new ContentValues based on the new object

    // and use it to update a row in the database.

    return true;

  }

 

  private static class myDbHelper extends SQLiteOpenHelper {

 

    public myDbHelper(Context context, String name,

                      CursorFactory factory, int version) {

      super(context, name, factory, version);

    }

 

    // Called when no database exists in disk and the helper class needs

    // to create a new one.

    @Override

    public void onCreate(SQLiteDatabase _db) {

      _db.execSQL(DATABASE_CREATE);

    }

 

    // Called when there is a database version mismatch meaning that the version

    // of the database on disk needs to be upgraded to the current version.

    @Override

    public void onUpgrade(SQLiteDatabase _db, int _oldVersion, int _newVersion) {

      // Log the version upgrade.

      Log.w("TaskDBAdapter", "Upgrading from version " +

                             _oldVersion + " to " +

                             _newVersion + ", which will destroy all old data");

       

      // Upgrade the existing database to conform to the new version. Multiple

      // previous versions can be handled by comparing _oldVersion and _newVersion

      // values.

 

      // The simplest case is to drop the old table and create a new one.

      _db.execSQL("DROP TABLE IF EXISTS " + DATABASE_TABLE);

      // Create a new one.

      onCreate(_db);

    }

  }

}

 

10、使用ContentProvider共享数据

当应用继承ContentProvider类,并重写该类用于提供数据和存储数据的方法,就可以向其他应用共享其数据。虽然使用其他方法也可以对外共享数据,但数据访问方式会因数据存储的方式而不同,如:采用文件方式对外共享数据,需要进行文件操作读写数据;采用sharedpreferences共享数据,需要使用sharedpreferences API读写数据。而使用ContentProvider共享数据的好处是统一了数据访问方式

当应用需要通过ContentProvider对外共享数据时,第一步需要继承ContentProvider并重写下面方法:

public class PersonContentProvider extends ContentProvider{

   public boolean onCreate()

   public Uri insert(Uri uri, ContentValues values)

   public int delete(Uri uri, String selection, String[] selectionArgs)

   public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)

   public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)

   public String getType(Uri uri)

}

B第二步需要在AndroidManifest.xml使用<provider>对该ContentProvider进行配置,为了能让其他应用找到该ContentProvider  ContentProvider 采用了authorities主机名/域名对它进行唯一标识,你可以把 ContentProvider看作是一个网站想想,网站也是提供数据者authorities 就是他的域名:

<manifest .... >

    <application android:icon="@drawable/icon" android:label="@string/app_name">

        <provider android:name=".PersonContentProvider" android:authorities="cn.itcast.provider.personprovider"/>

    </application>

</manifest>

注意:一旦应用继承了ContentProvider类,后面我们就会把这个应用称为ContentProvider内容提供者

 

11、Uri介绍(public abstract class Uri

Uri代表了要操作的数据Uri主要包含了两部分信息:

1需要操作的ContentProvider

2ContentProvider中的什么数据进行操作

一个Uri由以下几部分组成:

 

 

 

 

 

 


ContentProvider内容提供者scheme已经由Android所规定, scheme为:content://

主机名(或叫Authority)用于唯一标识这个ContentProvider,外部调用者可以根据这个标识来找到它。

路径(path)可以用来表示我们要操作的数据,路径的构建应根据业务而定如下:

要操作person表中id10的记录,可以构建这样的路径:/person/10

要操作person表中id10的记录的name字段, person/10/name

要操作person表中的所有记录,可以构建这样的路径:/person

要操作xxx表中的记录,可以构建这样的路径:/xxx

当然要操作的数据不一定来自数据库,也可以是文件等他存储方式,如下:

要操作xml文件中person节点下的name节点,可以构建这样的路径:/person/name

如果要把一个字符串转换成Uri,可以使用Uri类中的parse()方法,如下:

Uri uri = Uri.parse("content://cn.itcast.provider.personprovider/person")

12、UriMatcher类使用介绍(public class UriMatcher

因为Uri代表了要操作的数据,所以我们很经常需要解析Uri并从Uri中获取数据,Android系统提供了两个用于操作Uri的工具类:

A、public class UriMatcher

B、public class ContentUris

首先第一步把你需要匹配Uri路径全部给注册上,如下:

//常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码

UriMatcher  sMatcher = new UriMatcher(UriMatcher.NO_MATCH);

//如果match()方法匹配content://cn.itcast.provider.personprovider/person路径,返回匹配码为1

sMatcher.addURI(“cn.itcast.provider.personprovider”, “person”, 1);

//添加需要匹配uri,如果匹配就会返回匹配码

//如果match()方法匹配content://cn.itcast.provider.personprovider/person/230路径,返回匹配码为2

sMatcher.addURI(“cn.itcast.provider.personprovider”, “person/#”, 2);//#号为通配符

switch (sMatcher.match(Uri.parse("content://cn.itcast.provider.personprovider/person/10"))) {

   case 1

    break;

   case 2

    break;

   default://不匹配

    break;

}

注册完需要匹配的Uri后,就可以使用sMatcher.match(uri)方法对输入的Uri进行匹配,如果匹配就返回匹配码,匹配码是调用addURI()方法传入的第三个参数,假设匹配content://cn.itcast.provider.personprovider/person路径,返回的匹配码为1

13ContentUris类使用介绍

ContentUris类用于获取Uri路径后面的ID部分,它有两个比较实用的方法:

withAppendedId(uri, id)用于为路径加上ID部分:

Uri uri = Uri.parse("content://cn.itcast.provider.personprovider/person")

Uri resultUri = ContentUris.withAppendedId(uri, 10);

//生成后的Uri为:content://cn.itcast.provider.personprovider/person/10

 

parseId(uri)方法用于从路径中获取ID部分:

Uri uri = Uri.parse("content://cn.itcast.provider.personprovider/person/10")

long personid = ContentUris.parseId(uri);//获取的结果为:10

14、public abstract class ContentProvider

ContentProvider类主要方法的作用:

public boolean onCreate()

该方法在ContentProvider创建后就会被调用, Android在系统启动时就会创建ContentProvider 

public Uri insert(Uri uri, ContentValues values)

该方法用于供外部应用往ContentProvider添加数据。

public int delete(Uri uri, String selection, String[] selectionArgs)

该方法用于供外部应用从ContentProvider删除数据。

public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)

该方法用于供外部应用更新ContentProvider中的数据。

public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)

该方法用于供外部应用从ContentProvider中获取数据。

public String getType(Uri uri)

该方法用于返回当前Url所代表数据的MIME类型。如果操作的数据属于集合类型,那么MIME类型字符串应该以vnd.android.cursor.dir/开头,例如:要得到所有person记录的Uricontent://cn.itcast.provider.personprovider/person,那么返回的MIME类型字符串应该为:“vnd.android.cursor.dir/person”。如果要操作的数据属于单一数据,那么MIME类型字符串应该以vnd.android.cursor.item/开头,例如:得到id10person记录,Uricontent://cn.itcast.provider.personprovider/person/10,那么返回的MIME类型字符串应该为“vnd.android.cursor.item/person”

15使用ContentResolver操作ContentProvider中的数据

当外部应用需要对ContentProvider中的数据进行添加、删除、修改和查询操作时,可以使用ContentResolver 类来完成,要获取ContentResolver 对象,可以使用Activity提供的getContentResolver()方法。 ContentResolver 类提供了与ContentProvider类相同签名的四个方法:

public Uri insert(Uri uri, ContentValues values)

该方法用于往ContentProvider添加数据。

public int delete(Uri uri, String selection, String[] selectionArgs)

该方法用于从ContentProvider删除数据。

public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)

该方法用于更新ContentProvider中的数据。

public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)

该方法用于从ContentProvider中获取数据。

 

这些方法的第一个参数为Uri,代表要操作的是哪个ContentProvider和对其中的什么数据进行操作,假设给定的是:Uri.parse(“content://cn.itcast.provider.personprovider/person/10”),那么将会对主机名为cn.itcast.provider.personproviderContentProvider进行操作,操作的数据为person表中id10的记录。

使用ContentResolverContentProvider中的数据进行添加、删除、修改和查询操作:

ContentResolver resolver =  getContentResolver();

Uri uri = Uri.parse("content://cn.itcast.provider.personprovider/person");

//添加一条记录

ContentValues values = new ContentValues();

values.put("name", "itcast");

values.put("age", 25);

resolver.insert(uri, values);          

//获取person表中所有记录

Cursor cursor = resolver.query(uri, null, null, null, "personid desc");

while(cursor.moveToNext()){

       Log.i("ContentTest", "personid="+ cursor.getInt(0)+ ",name="+ cursor.getString(1));

}

//id1的记录的name字段值更改新为liming

ContentValues updateValues = new ContentValues();

updateValues.put("name", "liming");

Uri updateIdUri = ContentUris.withAppendedId(uri, 2);

resolver.update(updateIdUri, updateValues, null, null);

//删除id2的记录

Uri deleteIdUri = ContentUris.withAppendedId(uri, 2);

resolver.delete(deleteIdUri, null, null);

 

16Android平台给我们提供了一个SharedPreferences类,它是一个轻量级的存储类,特别适合用于保存软件配置参数。使用SharedPreferences保存数据,其背后是用xml文件存放数据,文件存放在/data/data/<package name>/shared_prefs目录下:

SharedPreferences sharedPreferences = getSharedPreferences("itcast", Context.MODE_PRIVATE);

Editor editor = sharedPreferences.edit();//获取编辑器

editor.putString("name", "传智播客");

editor.putInt("age", 4);

editor.commit();//提交修改

 

生成的itcast.xml文件内容如下:

<?xml version='1.0' encoding='utf-8' standalone='yes' ?>

<map>

<string name="name">传智播客</string>

<int name="age" value="4" />

</map>

 

 

因为SharedPreferences背后是使用xml文件保存数据,getSharedPreferences(name,mode)方法的第一个参数用于指定该文件的名称,名称不用带后缀,后缀会由Android自动加上。方法的第二个参数指定文件的操作模式,共有四种操作模式,这四种模式前面介绍使用文件方式保存数据时已经讲解过。如果希望SharedPreferences背后使用的xml文件能被其他应用读和写,可以指定Context.MODE_WORLD_READABLEContext.MODE_WORLD_WRITEABLE权限。

另外Activity还提供了另一个getPreferences(mode)方法操作SharedPreferences,这个方法默认使用当前类不带包名的类名作为文件的名称。

访问SharedPreferences中的数据代码如下:

SharedPreferences sharedPreferences = getSharedPreferences("itcast", Context.MODE_PRIVATE);

//getString()第二个参数为缺省值,如果preference中不存在该key,将返回缺省值

String name = sharedPreferences.getString("name", "");

int age = sharedPreferences.getInt("age", 1);

 

如果访问其他应用中的Preference前提条件是:该preference创建时指定了Context.MODE_WORLD_READABLE或者Context.MODE_WORLD_WRITEABLE权限。如:有个<package name>cn.itcast.action的应用使用下面语句创建了preference

getSharedPreferences("itcast", Context.MODE_WORLD_READABLE);

其他应用要访问上面应用的preference首先需要创建上面应用的Context,然后通过Context 访问preference ,访问preference时会在应用所在包下的shared_prefs目录找到preference 

Context otherAppsContext = createPackageContext("cn.itcast.action", Context.CONTEXT_IGNORE_SECURITY);

SharedPreferences sharedPreferences = otherAppsContext.getSharedPreferences("itcast", Context.MODE_WORLD_READABLE);

String name = sharedPreferences.getString("name", "");

int age = sharedPreferences.getInt("age", 0);

 

如果不通过创建Context访问其他应用的preference,可以以读取xml文件方式直接访问其他应用preference对应的xml文件,如:

File xmlFile = new File(“/data/data/<package name>/shared_prefs/itcast.xml”);//<package name>应替换成应用的包名。

原创粉丝点击