65.9K
CodeProject 正在变化。 阅读更多。
Home

在 Android 中使用 SQLite 数据库

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.66/5 (83投票s)

2010 年 10 月 19 日

CPOL

7分钟阅读

viewsIcon

1255288

downloadIcon

15831

关于在 Android 中使用 SQLite 的教程。

引言

Android 的默认数据库引擎是 Lite。SQLite 是一个轻量级的事务性数据库引擎,占用磁盘存储和内存空间少,因此它是创建 Android、iOS 等许多移动操作系统数据库的理想选择。

处理 SQLite 时需要考虑的事项

  1. SQLite 不维护数据类型完整性,您可以将某种数据类型的值放入另一种数据类型的列中(例如,在整数列中放入字符串,反之亦然)。
  2. SQLite 不维护引用完整性,没有 FOREIGN KEY 约束或 JOIN 语句。
  3. SQLite 完全支持 Unicode 是可选的,默认不安装。

在本教程中,我们将创建一个简单的数据库应用程序来存储员工数据。数据库包含

表格

  1. 员工
  2. 部门

视图

  1. ViewEmps:用于显示员工及其相关部门。

AndroidSQLite/sqlitedb1.jpg

创建 SQLite 数据库

默认情况下,Android 上的 SQLite 没有管理界面或应用程序来创建和管理数据库,因此我们将通过代码自行创建数据库。首先,我们将创建一个类来处理与数据库相关的所有操作,例如创建数据库、创建表、插入和删除记录等。第一步是创建一个继承自 SQLiteOpenHelper 类的类。此类提供了两个需要重写的方法来处理数据库

  1. onCreate(SQLiteDatabase db):数据库创建时调用,我们可以在这里创建表和列,创建视图或触发器。
  2. onUpgrade(SQLiteDatabse db, int oldVersion, int newVersion):当我们对数据库进行修改时调用,例如修改、删除、创建新表。

我们的类将具有以下成员

public class DatabaseHelper extends SQLiteOpenHelper {

static final String dbName="demoDB";
static final String employeeTable="Employees";
static final String colID="EmployeeID";
static final String colName="EmployeeName";
static final String colAge="Age";
static final String colDept="Dept";

static final String deptTable="Dept";
static final String colDeptID="DeptID";
static final String colDeptName="DeptName";

static final String viewEmps="ViewEmps";

构造函数

public DatabaseHelper(Context context) {
  super(context, dbName, null,33); 
  }

超类的构造函数具有以下参数

  • Context con:附加到数据库的上下文
  • dataBaseName:数据库的名称
  • CursorFactory:有时,我们可能会使用一个扩展 Cursor 类的类来实现一些额外的验证或对数据库运行的查询进行操作。在这种情况下,我们传递一个 CursorFactory 的实例来返回我们派生类的引用,以代替默认游标使用。在此示例中,我们将使用标准的 Cursor 接口检索结果,因此 CursorFactory 参数将为 null
  • Version:数据库模式的版本。构造函数使用指定的名称和版本创建一个新的空白数据库。

创建数据库

第一个需要重写的超类方法是 onCreate(SQLiteDatabase db)

public void onCreate(SQLiteDatabase db) {
  // TODO Auto-generated method stub
  
  db.execSQL("CREATE TABLE "+deptTable+" ("+colDeptID+ " INTEGER PRIMARY KEY , "+
    colDeptName+ " TEXT)");
  
  db.execSQL("CREATE TABLE "+employeeTable+" 
    ("+colID+" INTEGER PRIMARY KEY AUTOINCREMENT, "+
        colName+" TEXT, "+colAge+" Integer, "+colDept+" 
    INTEGER NOT NULL ,FOREIGN KEY ("+colDept+") REFERENCES 
    "+deptTable+" ("+colDeptID+"));");
  
  
  db.execSQL("CREATE TRIGGER fk_empdept_deptid " +
    " BEFORE INSERT "+
    " ON "+employeeTable+
    
    " FOR EACH ROW BEGIN"+
    " SELECT CASE WHEN ((SELECT "+colDeptID+" FROM "+deptTable+" 
    WHERE "+colDeptID+"=new."+colDept+" ) IS NULL)"+
    " THEN RAISE (ABORT,'Foreign Key Violation') END;"+
    "  END;");
  
  db.execSQL("CREATE VIEW "+viewEmps+
    " AS SELECT "+employeeTable+"."+colID+" AS _id,"+
    " "+employeeTable+"."+colName+","+
    " "+employeeTable+"."+colAge+","+
    " "+deptTable+"."+colDeptName+""+
    " FROM "+employeeTable+" JOIN "+deptTable+
    " ON "+employeeTable+"."+colDept+" ="+deptTable+"."+colDeptID
    );
  //Inserts pre-defined departments
  InsertDepts(db);  
 }

该方法创建带有列的表、一个视图和一个触发器。数据库创建时会调用此方法。因此,我们创建表并指定列。当数据库在磁盘上不存在时调用此方法,它在设备上仅执行一次,当应用程序首次在该设备上运行时。

升级数据库

有时,我们想通过更改模式、添加新表或更改列数据类型来升级数据库。这可以通过重写 onUpdate(SQLiteDatabase db,int old Version,int newVerison) 方法来完成

public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
  // TODO Auto-generated method stub
  
  db.execSQL("DROP TABLE IF EXISTS "+employeeTable);
  db.execSQL("DROP TABLE IF EXISTS "+deptTable);
  
  db.execSQL("DROP TRIGGER IF EXISTS dept_id_trigger");
  db.execSQL("DROP TRIGGER IF EXISTS dept_id_trigger22");
  db.execSQL("DROP TRIGGER IF EXISTS fk_empdept_deptid");
  db.execSQL("DROP VIEW IF EXISTS "+viewEmps);
  onCreate(db);
 }

当构造函数中指定的版本号发生变化时,会调用此方法。

当您想向数据库追加更改时,必须更改类构造函数中的版本号。

因此,当您将构造函数的版本号指定为 2

public DatabaseHelper(Context context) {
  super(context, dbName, null,2);
  
  // TODO Auto-generated constructor stub
 }

而不是 1

super(context, dbName, null,2);

应用程序会识别您要升级数据库,并且会调用 onUpgrade 方法。此方法的典型实现是删除表然后用额外的修改重新创建它们。

管理外键约束

我们之前提到 SQLite 3 默认不支持外键约束,但是我们可以通过使用 TRIGGERS 来强制执行此类约束:我们将创建一个触发器,以确保在插入新 Employee 时,其 Dept 值存在于原始 Dept 表中。创建此类触发器的 SQL 语句如下

CREATE TRIGGER fk_empdept_deptid Before INSERT ON Employees 
FOR EACH ROW BEGIN
    SELECT CASE WHEN ((SELECT DeptID FROM Dept WHERE DeptID =new.Dept ) IS NULL)
    THEN RAISE (ABORT,'Foreign Key Violation') END;
    END

onCreate 方法中,我们像这样创建了这个触发器

db.execSQL("CREATE TRIGGER fk_empdept_deptid " +
    " BEFORE INSERT "+
    " ON "+employeeTable+
    
    " FOR EACH ROW BEGIN"+
    " SELECT CASE WHEN ((SELECT "+colDeptID+" FROM "+deptTable+" _
    WHERE "+colDeptID+"=new."+colDept+" ) IS NULL)"+
    " THEN RAISE (ABORT,'Foreign Key Violation') END;"+
    "  END;");

 更新 

您可以通过重写 onOpen 方法来启用数据库中的外键,从而强制执行引用完整性,如下所示

@Override
public void onOpen(SQLiteDatabase db) {
		super.onOpen(db);
		if (!db.isReadOnly()) {
// Enable foreign key constraints
db.execSQL("PRAGMA foreign_keys=ON;");
}
	} 

执行 SQL 语句

现在我们开始执行基本的 SQL 语句。您可以使用 db.execSQL(String statement) 方法执行任何 非查询 SQL 语句,无论是 insertdeleteupdate 还是其他任何语句,就像我们在创建数据库表时那样

db.execSQL("CREATE TABLE "+deptTable+" ("+colDeptID+ " INTEGER PRIMARY KEY , "+
    colDeptName+ " TEXT)");

插入记录

我们使用以下代码将记录插入数据库,例如将记录插入 Dept

SQLiteDatabase db=this.getWritableDatabase();
 ContentValues cv=new ContentValues();
   cv.put(colDeptID, 1);
   cv.put(colDeptName, "Sales");
   db.insert(deptTable, colDeptID, cv);

   cv.put(colDeptID, 2);
   cv.put(colDeptName, "IT");
   db.insert(deptTable, colDeptID, cv);
                     db.close();

请注意,我们需要调用 this.getWritableDatabase() 来打开与数据库的 **读/写** 连接。ContentValues.put 有两个参数:列名 和要插入的 。另外,在执行语句后关闭数据库是一个好习惯。

更新值

要执行 update 语句,我们有两种方法

  1. 执行 db.execSQL
  2. 执行 db.update 方法
public int UpdateEmp(Employee emp)
  {
   SQLiteDatabase db=this.getWritableDatabase();
   ContentValues cv=new ContentValues();
   cv.put(colName, emp.getName());
   cv.put(colAge, emp.getAge());
   cv.put(colDept, emp.getDept());
   return db.update(employeeTable, cv, colID+"=?", 
    new String []{String.valueOf(emp.getID())});   
  }

update 方法具有以下参数

  1. String Table:要更新值的表
  2. ContentValues cv:包含新值的 content values 对象
  3. String where clause:用于指定要更新哪个记录的 WHERE 子句
  4. String[] argsWHERE 子句的参数

删除行

update 类似,要 execute 一个 delete 语句,我们有两种方法

  1. 执行 db.execSQL
  2. 执行 db.delete 方法
public void DeleteEmp(Employee emp)
  {
   SQLiteDatabase db=this.getWritableDatabase();
   db.delete(employeeTable,colID+"=?", new String [] {String.valueOf(emp.getID())});
   db.close();
  }

delete 方法与 update 方法具有相同的参数。

执行查询

要执行查询,有两种方法

  1. 执行 db.rawQuery 方法
  2. 执行 db.query 方法

执行原始查询以检索所有部门

Cursor getAllDepts()
  {
   SQLiteDatabase db=this.getReadableDatabase();
   Cursor cur=db.rawQuery("SELECT "+colDeptID+" as _id, 
	"+colDeptName+" from "+deptTable,new String [] {});
   
   return cur;
  }

rawQuery 方法有两个参数

  1. String queryselect 语句
  2. String[] selection args:如果 select 语句中包含 WHERE 子句,则为参数

注释

  1. 查询结果返回在 Cursor 对象中。
  2. select 语句中,如果表的主键列(id 列)的名称不是 _id,则您必须使用别名,形式为 SELECT [列名] as _id,因为 Cursor 对象总是期望主键列的名称为 _id,否则会抛出异常。

执行查询的另一种方法是使用 db.query 方法。从视图中选择某个部门的所有员工的查询如下所示

public Cursor getEmpByDept(String Dept)
  {
   SQLiteDatabase db=this.getReadableDatabase();
   String [] columns=new String[]{"_id",colName,colAge,colDeptName};
   Cursor c=db.query(viewEmps, columns, colDeptName+"=?", 
	new String[]{Dept}, null, null, null);
   return c;
  }

db.query 具有以下参数

  1. String Table Name:运行查询的表名
  2. String [ ] columns:查询的投影,即要检索的列
  3. String WHERE clause:where 子句,如果没有则传递 null
  4. String [ ] selection args:WHERE 子句的参数
  5. String Group by:指定 group by 子句的字符串
  6. String Having:指定 HAVING 子句的字符串
  7. String Order By by:指定 Order By 子句的字符串

管理游标

查询的结果集返回在 Cursor 对象中。有一些常用的方法您将与游标一起使用

  1. boolean moveToNext():将游标在结果集中移动一条记录,如果移到结果集的最后一行之后,则返回 false
  2. boolean moveToFirst():将游标移动到结果集的第一行,如果结果集为空,则返回 false
  3. boolean moveToPosition(int position):将游标移动到结果集中的某个行索引,如果位置无法到达,则返回 false
  4. boolean moveToPrevious():将游标移动到结果集的前一行,如果游标已移至第一行之前,则返回 false
  5. boolean moveToLast():将游标移动到结果集的最后一行,如果结果集为空,则返回 false

还有一些有用的方法可以检查游标的位置:boolean isAfterLast()isBeforeFirstisFirstisLastisNull(columnIndex)。另外,如果您有一个只有一行结果集并且需要检索特定列的值,可以这样做

public int GetDeptID(String Dept)
  {
   SQLiteDatabase db=this.getReadableDatabase();
   Cursor c=db.query(deptTable, new String[]{colDeptID+" as _id",colDeptName},
	colDeptName+"=?", new String[]{Dept}, null, null, null);
   //Cursor c=db.rawQuery("SELECT "+colDeptID+" as _id FROM "+deptTable+" 
   //WHERE "+colDeptName+"=?", new String []{Dept});
   c.moveToFirst();
   return c.getInt(c.getColumnIndex("_id"));  
  }

我们有 Cursor.getColumnIndex(String ColumnName) 来获取列的索引。然后,要获取特定列的值,我们有 Cursor.getInt(int ColumnIndex) 方法。

还有 getShortgetStringgetDoublegetBlob 方法用于将值作为字节数组返回。在使用完游标后关闭它是一个好习惯。

在此 下载一个关于 Android 数据库使用示例的演示应用程序。

我的博客 http://android-pro.blogspot.com 上有更多教程。

© . All rights reserved.