在Android实际开发中,我们的项目基本上都会用到数据库,SQLite是Android内置的一个很小的关系型数据库,我们在Android的系统架构里面就会发现,在应用程序框架层中就有一个SQLite,如下图:
SQLite简介
SQLite数据库是一种轻量型的数据库,适用于这种嵌入式的程序中。SQLite数据库文件与Excel有些类似,我们可以根据Excel来理解SQLite的数据库文件,在SQLite中,每个文件表现为一个数据库(database)中可以包含多个数据表(table),其中每张表的存储格式也是按行与列的网格形式存在的,每张表需要先定义有哪些列,以及每一列中存储的数据类型。对于SQLit数据库我们同样可以进行各种数据库操作,包括创建数据表以及数据的增、删、改、查等。
这里有个需要注意的是,SQLite对于数据存储的约束不是严格的,也就是说我们可以在Integer类型的列中添加VARCHAR类型的数据,当然,我们并不建议这样做。
SQLite数据库查看工具
SQLite数据库并不是可视化的,当我们新建数据库后,我们可以在data/data/应用程序包名/databases目录下找到我们的数据库文件,将其导出来,通过可视化工具来查看,有许多工具可以查看,例如:
SQLiteSpy
SQLite Export,专业版为收费的,个人版为免费的
Navicat(用于管理MySQL数据库服务器,部分版本可查看SQLite)
具体使用哪种工具就看个人的喜好了,我这里也不多说这些工具了。
数据库的使用
我们可以直接在Activity中的onCreate方法中调用openOrCreateDatabase()方法创建数据库,并打开数据库,如果数据库已经存在,则直接打开,openOrCreateDatabase()方法的返回值是SQLiteDatabase类型的数据,用于执行各种数据表及数据的操作。
创建数据表:
// 创建数据库,名为myTestDatabase.db
SQLiteDatabase db;
db = openOrCreateDatabase("myTestDatabase.db",MODE_PRIVATE,null);
这时我们运行模拟器,完成之后就会在DDMS的知道目录下找到该数据库文件。
创建数据表:
String sql = "CREATE TABLE [students] ("
+ "[_id] INTEGER PRIMARY KEY AUTOINCREMENT,"
+ "[_name] VARCHAR(50) NUIQUE NOT NULL,"
+ "[_age] INT NOT NULL DEFAULT 16"
+ ")";
db.execSQL(sql);
这样我们就在该数据库文件下新建了一张名为”students”的数据表,这里字段名和列名加上方括号是为了防止和关键字发生冲突。
在新建好数据表之后我们就可以使用SQL语法去对数据库进行添加数据、增、删、改、查了。
添加数据——语法:INSERT INTO 数据表名称 (字段名称列表) VALUES (值列表);
增——long insert(String table, String nullColumnHack, ContentValues values);
删——int delete(String table, String whereClause, String[] whereArgs);
改——int update(String table, ContentValues values, String whereClause, String[] whereArgs);
查——Cursor query(String table, String[] columns, String selection, String[] selectionArgs,String groupBy, String having, String orderBy);
这里需要提一下Cursor这个类,Cursor是在Android中查询了SQLite后的查询结果的数据类型,其本质类似于网格的形式存储数(其实就是充当一个游标的作用),当需要获取数据时,Cursor每次获取一整条记录,并可以通过get系列方法获取数据,关于Cursor需要了解的方法有:move系列方法、get系列方法、is系列方法;
关于Cursor的遍历:
for(c.moveToFirst(); !c.isAfterLast(); c.moveToNext()) {
... ;
... ;
}
但是,我们并不建议这样去做,为什么呢?因为这样的话我们就会在每一次启动程序的时候,都会去建一个数据表,当然这是不可能的,已经存在了就会报错了。
扩展:SQLiteOpenHelper的使用方法:
SQLiteOpenHelper是一个辅助类来管理数据库的创建和版本。 可以通过继承这个类,实现它的一些方法来对数据库进行一些操作。具体的使用方法是我们需要自定义类来继承SQLiteOpenHelper这个类,并实现其抽象方法,由于该类没有无参的构造方法,所以需要实现父类的带参数的构造方法,代码如下:
public class DBOpenHelper extends SQLiteOpenHelper {
/**
* @param context 上下文对象
* @param name 数据库名称
* @param factory 数据库工厂
* @param version 数据库版本
*/
public DBOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
}
@Override
public void onCreate(SQLiteDatabase db) {
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
需要注意,当第一次调用getReadableDatabase()或者getWritableDatabase()方法来初始化时,onCreate和onUpgrade方法可能被执行,这两个方法可能会消耗较长的执行时间,所以并不推荐在主线程中调动该方法。
我们可以直接在调用父类的构造方法的时候,就将我们要指定创建的数据库创建好,之后我们可以将创建数据表的语句写在onCreate方法中,这样我们就可以保证在每次启动应用程序的时候不会多次建表,代码如下:
public class DBOpenHelper extends SQLiteOpenHelper {
/**
* 由于我们本例只需要新建一个数据库,所以在调用父类的构造方法的时候,
* 我们就指定将数据库建好
* @param context
*/
public DBOpenHelper(Context context) {
super(context, "myTestDatabase.db", null, 1);
}
@Override
public void onCreate(SQLiteDatabase db) {
// 创建数据表
String sql = "CREATE TABLE [students] ("
+ "[_id] INTEGER PRIMARY KEY AUTOINCREMENT,"
+ "[_name] VARCHAR(50) NUIQUE NOT NULL,"
+ "[_age] INT NOT NULL DEFAULT 16"
+ ")";
db.execSQL(sql);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
这时关于数据库的类我们就写好了,接下来需要在Activity中初始化,在Activity的onCreate方法中:
// 初始化数据库
DBOpenHelper helper = new DBOpenHelper(this) ;
但是,这时我们运行程序时,却发现指定目录下并没有新建出相应的数据库文件,我们可以通过getReadableDatabase()或者getWritableDatabase()方法来获取SQLiteDatabase对象,这两个方法的区别在于当尝试获取一个可执行写操作的数据库访问对时两个方法是等效的,但是,当出现意外例如磁盘空间已满,getReadableDatabase()则获取只读的数据库访问对象。
// 初始化数据库
DBOpenHelper helper = new DBOpenHelper(this) ;
// 获取SQLiteDatabase对象
helper.getReadableDatabase() ;
接下来就是我们需要调用的增删改查一系列方法了,直接上代码,相信大家都能看懂的,代码里也会有相应的注释内容:
public class MainActivity extends AppCompatActivity {
private DBOpenHelper helper ;
private SQLiteDatabase db ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 初始化数据库
helper = new DBOpenHelper(this) ;
// 获取SQLiteDatabase对象
db = helper.getReadableDatabase() ;
// 增
doInsert();
// 删
doDelete();
// 改
doUpdate();
// 查
doQueryAll();
doQueryByName("Bob");
}
/**
* 增加数据记录
*/
private void doInsert() {
String table = "students";
String nullColumnHack = null;
ContentValues values = new ContentValues();
values.put("_name", "Bob");
values.put("_age", 20);
long i = db.insert(table, nullColumnHack, values);
if(i == -1) {
Toast.makeText(this,"错误:插入数据失败!",Toast.LENGTH_LONG).show();
} else {
Toast.makeText(this,"插入数据成功! ID=" + i,Toast.LENGTH_LONG).show();
}
}
/**
* 删除记录
*/
private void doDelete() {
long i = db.delete("students", "_id=?", new String[] { 3 + "" }) ;
if(i == -1) {
Toast.makeText(this,"错误:删除数据失败!",Toast.LENGTH_LONG).show();
} else {
Toast.makeText(this,"删除数据成功", Toast.LENGTH_LONG).show();
}
}
/**
* 修改内容
*/
private void doUpdate() {
ContentValues values = new ContentValues();
values.put("_age", 30);
long i = db.update("students", values, "_name=?", new String[] { "Bob" }) ;
if(i == -1) {
Toast.makeText(this,"错误:修改数据失败!",Toast.LENGTH_LONG).show();
} else {
Toast.makeText(this,"修改数据成功", Toast.LENGTH_LONG).show();
}
}
/**
* 查询所有记录
*/
private void doQueryAll() {
Cursor c = db.query("students", new String[]{"_id", "_name", "_age"}, null, null, null, null, "_id desc");
long id ;
String name ;
int age ;
for(c.moveToFirst(); !c.isAfterLast(); c.moveToNext()) {
id = c.getLong(c.getColumnIndex("_id")) ;
name = c.getString(c.getColumnIndex("_name")) ;
age = c.getInt(c.getColumnIndex("_age")) ;
Log.d("TAG", "_id" + id + "_name" + name + "_age" + age) ;
}
c.close();
}
/**
* 根据姓名查询记录
* @param studentName
*/
private void doQueryByName(String studentName) {
Cursor c = db.query("students", null, "_name=?", new String[] { studentName }, null, null, null);
if(c.moveToFirst()) {
long id;
String name;
int age;
id = c.getLong(c.getColumnIndex("_id"));
name = c.getString(c.getColumnIndex("_name"));
age = c.getInt(c.getColumnIndex("_age"));
Toast.makeText(this, "学生记录:_id=" + id + "_name=" + name + "_age=" + age, Toast.LENGTH_LONG).show();
} else {
Toast.makeText(this, "没有相关学生记录", Toast.LENGTH_LONG).show();
}
}
}
最后还是把相关的操作再写出来吧,这样大家会看的更明白一些,代码里面有些参数我直接就是写成了所需的内容,所以为了方便大家掌握就将增删改查的方法在下面说一下:
增
long insert(String table, String nullColumnHack,ContentValues values)
// 参数:
String table // 表名
String nullColumnHack // 当第3个参数为null或无效时,避免SQ语句错误时需要,如果第3个参数确保无误,则该参数没有意义
ContentValues values // 由Map实现的数据类型,通过key表现数据表中的字段,通过value表现各字段对应的值
// 返回值:最新增加的记录的ID,如果发生错误,则返回-1
删
int delete(String table, String whereClause, String[]whereArgs)
// 参数:
String table //表名
String whereClause //查询条件,即where子句,例如id=?
String[] whereArgs //查询条件对应的值列表,例如new String[] { id + "" }
//返回值:受影响的行数,即删除了多少条记录
改
int update(String table, ContentValues values, StringwhereClause, String[] whereArgs)
//参数:
String table //表名
ContentValues values // 由Map实现的数据类型,通过key表现数据表中的字段,通过value表现各字段对应的值
String whereClause //修改条件,即where子句,例如id=?
String[] whereArgs //修改条件对应的值列表,例如new String[] { id + "" }
//返回值:受影响的行数,即删除了多少条记录
查
Cursor query(String table, String[] columns, String selection,String[] selectionArgs,String groupBy, String having,String orderBy)
//参数:
String table //表名
String[] columns //需要查询的字段的列表
String selection //查询条件,即where子句
String[] selectionArgs //查询条件对应的值列表
String groupBy //分组查询的group by子句
String having //分组查询的having子句
String orderBy //排序,格式例如id asc表示按照id字段顺序排列,或例如name desc表示按照name字段倒序排列,也可以例如name desc, id asc,即表示主要按照name排序,当name值相同时,再按照id排序
有了以上的介绍相信学起来就很容易了吧!