SQLite是一款轻量级的关系型数据库,它的运算速度非常快,占用资
源很少, 通常只需要几百KB的内存就足够了,因而特别适合在移动设备上使用。SQLite不仅支持标准的 SQL 语法,还遵循了数据库的ACID 事务。所以只要你以前使用过其他的关系型数据库, 就可以很快地上手SQLite。而SQLite又比一般的数据库要简单得多,它甚至不用设置用户名和密码就可以使用。Android 正是把这个功能极为强大的数据库嵌人到了系统当中,使得本地持久化的功能有了一 次质的飞跃。
1、创建数据库
Android为了让我们能够更方便的管理数据库,专门提供了一个SQLiteOpenHelper帮助类,借助这个类就可以非常简单的对数据库进行创建和升级。
首先SQLiteOpenHelper是一个抽象类,这意味着我们要想使用的话,就需要创建自己的帮助类去实现它。SQLiteOpenHelper中有两个抽象方法,分别是onCreate()和onUpgrade(),我们得在自己的类里重写这两个方法,然后分别在这两个方法中去实现创建、升级数据库的逻辑。
SQLiteOpenHelper还有两个非常重要的实例方法,getReadableDatabase()和getWritableDatabase()。这两个方法都可以创建或者打开一个现有的数据库,并范湖一个可对数据库进行读写操作的对象。不同的是,当数据库不可写入的时候,getReadableDatabase()方法返回的对象将以只读的方式去打开,getWritableDatabase()将会出现异常。
还是通过例子来体会SQLiteOpenHelper的用法,我们希望创建一个名为BookStore.db的数据库,然后在数据库中建一张Book表,表中有主键id、作者、价格、书名等等信息。下面新建一个MyDatabaseHelper类,并继承自SQLiteOpenHelper:
public class MyDatabaseHelper extends SQLiteOpenHelper{
private Context mContext;
public static final String CREATE_BOOK = "create table Book ("
+ "id integer primary key autoincrement, "
+ "author text, "
+ "price real, "
+ "pages integer, "
+ "name text )";
public MyDatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
mContext = context;
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_BOOK);
Toast.makeText(mContext, "create success", Toast.LENGTH_SHORT).show();
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
我们把建表的语句定义成了字符串常量值,然后在onCreate()方法中又调用了SQLiteOpenHelper的execSQL()方法去执行这条建表语句,并弹一个创建成功的Toast提示。
在SQLiteOpenHelper中有两个构造方法可供重写,一般使用是本例中的构造方法,这个方法接收4个参数。第一个参数是Context;第二个参数是数据库的名字;第三个参数允许我们在查询数据的时候返回一个自定义的cursor,一般传入null;第四个参数是表示当前数据库的版本号,可用于对数据库进行升级操作。
接下来创建一个SQLiteActivity,并在布局中添加一个Button
public class SQLiteActivity extends BaseActivity {
private MyDatabaseHelper dbHelper;
private Button btn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sqlite);
setTitle("SQLite");
dbHelper = new MyDatabaseHelper(this, "BookStore.db", null, 1);
btn = findViewById(R.id.btn_sql_create);
// 添加数据
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SQLiteDatabase db = dbHelper.getReadableDatabase();
}
});
}
}
我们在onCreate()方法中,构建了一个MyDatabaseHelper对象,并通过构造函数的参数将数据名指定为BookStore.db,版本号为1。然后在点击事件中,调用了SQLiteDatabase的getReadableDatabase()方法来打开数据库。由于第一次点击时,当前应用中并没有BookStore.db这个数据库,于是就会创建该数据库并调用MyDatabaseHelper的onCreate()方法,这样Book表也会被创建,然后弹出一个创建成功的Toast提示。当我们再次点击按钮时,因为已经存在数据了,所以并不会再次创建,也不会弹Toast提示了。
重新运行程序,点击按钮创建数据,然后到本地查看可以发现数据库被成功的创建了:
2、升级数据库
之前在我们的MyDatabaseHelper类中还空着一个onUpgrade()方法没有内容,这个方法实际上就是用来对数据库进行升级的。我们的数据中现有一张Book表,接下来添加一张新的Category表用于记录图书的分类。
public class MyDatabaseHelper extends SQLiteOpenHelper{
private Context mContext;
public static final String CREATE_BOOK = "create table Book ("
+ "id integer primary key autoincrement, "
+ "author text, "
+ "price real, "
+ "pages integer, "
+ "name text )";
public static final String CREATE_CATEGORY = "create table Category ("
+ "id integer primary key autoincrement, "
+ "category_name text, "
+ "category_code integer)";
...
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_BOOK);
db.execSQL(CREATE_CATEGORY);
Toast.makeText(mContext, "create success", Toast.LENGTH_SHORT).show();
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("drop table if exists Book");
db.execSQL("drop table if exists Category");
onCreate(db);
}
}
在onUpgrade()方法中,我们执行了两条drop语句,如果在数据库中发现Book或者Category表就删掉,然后调用onCreate重建。这里这么做只是为了演示onUpgrade(),(因为如果已经存在了Book表,那么onCreate将不会被执行)不要在别的地方这么应用!!!。另外我们之前在构造方法中传的数据库版本是1,现在改成2,然后重新运行程序,点击创建数据库按钮:
用RE管理器打开BookStore.db文件,可以看到在原来的Book之外,我们成功新添加了一个Category表进来。
3、添加数据
我们长听说这么一句,数据库玩的就是增删改查。其实我们可以对数据进行的操作无非就这四种,CRUD,即Create、Retrieve、Update、Delete。
SQLiteDatabase中提供了一个insert()方法,用于添加数据。它接收3个参数,第一个参数是表名,第二个参数用于在未指定添加数据的情况下给某些可为空列自动赋值null,一般用不到话直接传null即可。第三个参数是一个ContentValues对象,它提供了一系列的put方法重载,用于向ContentValues对象中添加数据。继续修改代码:
public class SQLiteActivity extends BaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sqlite);
...
// 添加数据
add_data.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SQLiteDatabase db = dbHelper.getReadableDatabase();
ContentValues values = new ContentValues();
values.put("name", "The perfect Code");
values.put("author", "John Hao");
values.put("pages", 1802);
values.put("price", 79.99);
db.insert("Book", null, values);
values.clear();
values.put("name", "Travel in Code world2");
values.put("author", "John Hao");
values.put("pages", 300);
values.put("price", 26.73);
db.insert("Book", null, values);
}
});
}
}
新添加add_data按钮,并添加响应事件。首先通过getReadableDatabase()获得SQLiteDatabase对象,然后使用ContentValues对要添加的数据进行组装,接着调用insert()方法将数据添加到表中。重新运行程序
我们可以看到Book表中添加了两条新的数据。
4、更新数据
SQLiteDatabase中提供了非常好用update()方法用于对数据进行更新。该方法接收4个参数,第一个参数是表名,第二参数是ContentValues对象,第三、四个参数用于约束更新某一行或者某几行中的数据,不指定的话默认更新所有的行。继续修改代码:
public class SQLiteActivity extends BaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sqlite);
...
// 更新数据
// update,第一个参数表的名字,第二参数是更新的数据,第三、四个参数用于约束,例子中的name = ?相当于SQL中的where部分,表示为要更新name=?的行
update_data.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("price", 254.23);
db.update("Book", values, "name = ?", new String[]{"Travel in Code world"});
}
});
}
}
现在,我的书Travel in Code world2卖的太火了,已经供不应求,因此我决定要提价了,由原来的26.73提价到254.23。重新运行运行下程序:
用RE打开数据库,查看Book表,发现价格已经更新了。
5、删除数据
类似的,SQLiteDatabase提供了一个delete()方法用于删除数据。该方法接收3个参数,第一个参数仍然为表名,第二、三个参数又是用于约束删除某一行或某几行的数据,不指定的话默认删除所有行。继续修改代码,添加一个delete_data按钮:
public class SQLiteActivity extends BaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sqlite);
...
// 删除数据
delete_data.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
db.delete("Book", "pages > ?", new String[]{"500"});
}
});
}
}
这里我们把Book表中,超过500页的书的数据都删除掉。重新运行下程序,点击删除按钮:
通过RE管理器,我们可以看到,那本长达1802页的The perfect Code被我们干掉了。
6、查询数据
把查询放到最后,是因为它是“增删改查”中最为复杂的部分。SQL查询涉及的内容如此之多,以至于需要专门的书来进行系统的学习。这里只介绍最简单的一些应用。
类似的,SQLiteDatabase提供了一个query()方法用于对数据进行查询。这个方法的参数非常复杂,最短的一个方法重载也需要7个参数。引用一张图来进行参考。
虽然query()方法的参数非常多,但是因为我们不必为每条查询语句都指定所有的参数,多数情况是只需要传少数几个参数就可以完成查询操作了。调用query()后,会返回一个Cursor对象,查询到的所有数据都将从这个对象中取出。添加一个查询按钮:
public class SQLiteActivity extends BaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sqlite);
...
// query查询
query_data.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
Cursor cursor = db.query("Book",null,null,null,null,null,null);
if (cursor.moveToFirst()) {
do {
String name = cursor.getString(cursor.getColumnIndex("name"));
String author = cursor.getString(cursor.getColumnIndex("author"));
int pages = cursor.getInt(cursor.getColumnIndex("pages"));
double price = cursor.getDouble(cursor.getColumnIndex("price"));
Log.d("SQLiteActivity", "name: " + name);
Log.d("SQLiteActivity", "author: " + author);
Log.d("SQLiteActivity", "pages: " + pages);
Log.d("SQLiteActivity", "price: " + price);
} while (cursor.moveToNext());
}
cursor.close();
}
});
}
}
首先通过getWritableDatabase()获取到SQLiteDatabase对象,然后通过调用query()方法得到Cursor对象,这里只传入了表名,表示我们查询Book表内的所有数据。接着我们调用moveToFirst()方法,将数据的指针移动到第一行的位置,然后进入循环中,去遍历查到的每一行数据。在循环中,可以通过Cursor的getColumnIndex()方法获取到某一列对应的位置索引,然后将这个索引传入到相应的取值方法中,就可以读取到数据了。最后我们通过Log的方式,将取出的数据打印出来:
可以看到,我们把Book表内唯一的一条数据成功的读取出来了。
我们这里只是学习了最简单的用法,更多高级的用法还需要我们在后面的学习中慢慢摸索。