目录
一:介绍
二:SQLiteOpenHelper
2.1 SQLiteOpenHelper帮助类简介
2.2 通过MyDatabaseHelper实现类创建数据库
2.3 通过SQLiteOpenHelper中的onUpdate() 方法升级数据库
2.4 升级数据库的最佳方式
三:SQL数据库CURD
3.1 sql语句方式
3.2 SQLiteDatabase 的非sql方法实现CURD
3.2.1 添加数据
3.2.2 更新数据
3.2.3 删除数据
3.3.3 查询数据
SQLite是一款轻量级的关系型数据库,它运算速度快,占资源少,只要几百K的内存,适合在移动设备使用。
Android 提供了一个SQLiteOpenHelper 帮助类,借助这个类就可以非常简单的对数据库进行创建和升级。SQLiteOpenHelper 是一个抽象类,使用它需要创建一个帮助类去继承它。
SQLiteOpenHelper 中有两个抽象方法,分别是 onCreate() 和 onUpdate(),需要在帮助类里重写这两个方法,然后分别在这两个方法中去实现创建和升级数据库的逻辑。
SQLiteOpenHelper 中还有两个非常重要的实例方法,getReadableDatabase() 和 getWritableDatabase()。这两种方法都可以创建或打开一个现有的数据库(如果数据库已存在则直接打开,否则创建一个新的数据库),并返回一个可对数据库进行读写操作的对象。不同的是,当数据库不可写入的时候(如磁盘空间已满)getReadableDatabase() 方法返回的对象将以只读的方式去打开数据库,而getWritableDatabase() 方法将抛出异常。
SQLiteOpenHelper 的构造方法接收四个参数,第一个参数是 Context,必须要有Context对象才能对数据库进行操作。第二个参数是数据库名,创建数据库时使用的就是这里指定的名称。第三个参数允许在查询数据库的时候返回一个自定义的 Cursor,一般传入null。第四个参数表示当前数据库的版本号,可用于对数据库进行升级操作。
构建出 SQLiteOpenHelper 的实例之后,再调用它的 getReadableDatabase() 或 getWritableDatabase() 方法就能够创建数据库了,数据库文件会存放在 /data/data/<包名>/database/ 目录下。
新建 MyDatabaseHelper 类继承自 SQLiteOpenHelper:
public class MyDatabaseHelper extends SQLiteOpenHelper {
public static final String CREATE_BOOK = "CREATE TABLE book ("
+ "id integer PRIMARY KEY Autoincrement ,"
+ "author text ,"
+ "price real ,"
+ "pages integer,"
+ "name text )";
/**
* integer:整形
* real:浮点型
* text:文本类型
* blob:二进制类型
* PRIMARY KEY将id列设置为主键
* AutoIncrement关键字表示id列是自动增长的
*/
private Context myContent;
public MyDatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
myContent = context;
}
@Override
public void onCreate(SQLiteDatabase db) {
//创建数据库的同时创建Book表
db.execSQL(CREATE_BOOK);
//提示数据库创建成功
Toast.makeText(myContent, "数据库创建成功", Toast.LENGTH_SHORT).show();
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
在 MainActivity 中进行测试:
public class MainActivity extends AppCompatActivity {
private MyDatabaseHelper dbHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//构建一个 MyDatabaseHelper 对象,通过构造函数将数据库名指定为 BookStore.db
dbHelper = new MyDatabaseHelper(this,"BookStore.db",null,1);
Button createDatabase = (Button)findViewById(R.id.create_database);
createDatabase.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
/**
*调用getWritableDatabase() 方法
* 自动检测当前程序中 BookStore.db 这个数据库
* 如果不存在则创建该数据库并调用 onCreate() 方法
* 同时Book表也会被创建
*/
dbHelper.getWritableDatabase();
}
});
}
}
onUpdate() 方法是用于对数据库进行升级的,它在整个数据库的管理工作中担当着非常重要的作用。
修改 上述的MyDatabaseHelper 中的代码,如下所示:
@Override
public void onCreate(SQLiteDatabase db) {
//创建Book表和Category表
db.execSQL(CREATE_BOOK);
db.execSQL(CREATE_CATEGORY);
//提示数据库创建成功
Toast.makeText(myContent, "数据库创建成功", Toast.LENGTH_SHORT).show();
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
/**
* 如果发现数据库中已经存在 Book 表或 Category 表
* 就将这两张表删除掉,然后调用 onCreate() 方法重新创建
* 如果在创建表时发现表已经存在,就会直接报错
*/
db.execSQL("DROP TABLE IF EXISTS Book");
db.execSQL("DROP TABLE IF EXISTS Category");
onCreate(db);
}
接下来的问题就是如何让 onUpgrade() 方法能够得到执行了,还记得 SQLiteOpenHelper 的构造方法里接受的第四个参数吗?它表示当前数据库的版本号,之前我们传入的是1,现在只要传入一个比1大的数,就可以让 onUpgrade() 方法得到执行了,修改MainActivity 中的代码:
dbHelper = new MyDatabaseHelper(this,"BookStore.db",null,3);
粗暴的删除当前所有的表来达到更新的效果,对于用户来说是非常糟糕的,因为以前程序中存储的本地数据全部丢失了。其实只需进行一些合理的控制,就可以保证升级数据库的时候数据并不会丢失了。
每一个数据库版本都会对应一个版本号,当指定的数据库版本号大于当前数据库版本号的时候,就会进入到 onUpgrade()方法中去执行更新操作。这里需要为每一个版本号赋予它各自改变的内容,然后在 onUpgrade()方法中对当前数据库的版本进行判断,再执行相应的改变就可以了。
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
switch (oldVersion) {
case 1:
db.execSQL(CREATE_CATEGORY);
default:
}
}
注意,switch 中每一个 case 的最后都是没有使用 break 的,这是为了保证跨版本升级的时候,每一次的数据库修改都能被全部执行。使用这种方式来维护数据库的升级,不管版本怎样更新,都可以保证数据库的表结构是最新的,而且表中的数据也完全不会丢失。
添加数据的方法如下:
/**
* SQL插入语句:
* INSERT INTO Book(name,author,pages,price) VALUES
* ("The Da Vinci Code" ,"Dan Brown",454,16.96);
*/
db.execSQL("INSERT INTO Book(name,author,pages,price) VALUES(?,?,?,?",
new String[]{"The Lost Symbol", "Dan Brown", "510", "19.95"});
删除数据的方法如下:
/**
*SQL删除语句:
* DELETE FROM Book WHERE pages > 500;
*/
db.execSQL("DELETE FROM Book WHERE pages > ?",new String[]{"500"});
更新数据的方法如下:
/**
* SQL更新语句:
* UPDATE Book SET price = 10.99 WHERE name = "The Da Vinci Code" ;
*/
db.execSQL("UPDATE Book SET price = ? WHERE name = ?",
new String[]{"10.99", "The Da Vinci Code"});
查询数据的方法如下:
/**
* SQL查询语句:
* SELECT * FROM BOOK ;
*/
db.rawQuery("SELECT * FROM BOOK", null);
注意:除了查询数据的时候调用的是 SQLiteDatabase 的 rawQuery()方法,其他的操作都是调用的 execSQL()方法。
SQLiteDatabase db = dbHelper.getWritableDatabase()得到SQLiteDatabase对象。SQLiteDatabase 中提供了一个 insert() 方法,这个方法就是专门用于添加数据的。
insert() 方法接收三个参数,第一个参数是表名,我们希望向哪张表里添加数据,这里就传入该表的名字。第二个参数用于在未指定添加数据的情况下给某些可为空的列自动赋值NULL,一般用不到这个功能,直接传入 null 即可。第三个参数是一个 ContentValues 对象,它提供了一系列的 put() 方法重载,用于向 ContentValues 中添加数据,只需要将表中的每个列名及相对应的待添加数据传入即可。
public void onClick(View v) {
//获取 SQLiteDatabase 对象
SQLiteDatabase db = dbHelper.getWritableDatabase();
//使用ContentValues 对数据进行组装
ContentValues values = new ContentValues();
//开始组装第一条数据
values.put("name", "The Da Vinci Code");
values.put("author", "Dan Brown");
values.put("pages", 454);
values.put("price", 16.96);
//插入第一条数据
db.insert("Book", null, values);
values.clear();
//开始组装第二条数据
values.put("name", "The Lost Symbol");
values.put("author", "Dan Brown");
values.put("pages", 510);
values.put("price", 19.95);
//插入第二条数据
db.insert("Book", null, values);
}
这里只对Book表里其中四列的数据进行了组装,id并没有赋值。因为创建表的时候就将 id 列设置为自动增长了,它的值会在入库的时候自动生成,不需要手动赋值。
SQLiteDatabase 中提供了一个非常好用的 update() 方法用于对数据进行更新,这个方法接收四个参数,第一个参数和 insert()方法一样,也是表名,在这里指定去更新哪张表里的数据。第二个参数是 ContentValues 对象,把要更新的数据在这里组装进去。第三、第四个参数用于去约束更新某一行的数据,不指定的话默认就是更新所有行。
public void onClick(View v) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("price", 10.99);
//?是一个占位符,通过字符串数组为每个占位符指定相应的内容
db.update("Book", values, "name = ?", new String[]{"The Da Vinci Code"});
}
SQLiteDatabase 中提供了一个 delete()方法专门用于删除数据,这个方法接收三个参数,第一个参数仍然是表名,第二、第三个参数用于约束删除某一行或某几行的数据,不指定的话默认就是删除所有行。
public void onClick(View v) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
db.delete("Book", "pages > ?", new String[]{"500"});
}
SQLiteDatabase 中还提供了一个 query() 方法用于对数据进行查询。这个方法的参数非常复杂,最短的一个方法重载也需要传入七个参数。
query()方法参数及对应SQL:
table:指定查询的表名,对应 from table_name
columns:指定查询的列名,对应 select column1,column2 ...
selection:指定 where 的约束条件,where column = value
selectionArgs:为 where 中的占位符提供具体的值
groupBy:指定需要分组的列,group by column
having:对分组后的结果进一步约束,having column = value
orderBy:指定查询结果的排序方式,order by column
虽然 query()方法的参数非常多,但是不必每条查询语句都指定上所有的参数,多数情况下只需传入少数几个参数就可以完成查询操作了。
调用 query()方法后会返回一个 Cursor 对象,查询到的所有数据都将从这个对象中取出。
public void onClick(View v) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
//查询Book表中的所有数据
Cursor cursor = db.query("Book", null, null, null, null, null, null, null);
//遍历Curosr对象,取出数据并打印
while (cursor.moveToNext()) {
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("woider", "Book Name:" + name + " Author:"
+ author + " Pages:" + pages + " Price:" + price);
}
//关闭Cursor
cursor.close();
}
执行 query()方法之后会得到一个 Cursor对象,然后通过 Cursor 对象的 moveToNext()方法去遍历查询到的每一行数据。在这个循环中可以通过 Cursor 的 getColumnIndex() 方法获取到某一列在表中对应位置的索引,然后将这个索引传入到相应的取值方法中,就可以得到从数据库中读取到的数据了。最后别忘了调用 close()方法来关闭 Cursor。
注:文章自郭霖老师的第一行代码(推荐)进行回顾,略作整理,手打记录,以备遗忘翻看。