程序中使用数据库,通常是:
那下面来看下我们Android中数据库的操作,更多内容请看:SQLite文档
程序中需要哪些表,有哪些列(字段,属性),表之间的关系…
以前写Java程序时是通过写JavaBean类,在bean包下创建很多个实体类,在Android中,Google建议我们使用 Contract(契约类)
契约类 :是用于定义 URI、表格和列名称的常数的容器
项目经验很少,并不是很懂写契约类,和那些实体类相比有什么大大的优势,只是在结合ContentProvider时,契约类更能派上用场,好吧,留待以后慢慢体会,接着写…
我们简单的写一个学生信息(Student)的增删改查(CRUD)好了
看下契约类长什么样子
~~~
// 定义学生管理系统的契约类
public final class StudentContract {
// 私有化构造器,避免创建对象,因为我们不用该类对象
private StudentContract() {}
// 定义Student表内容的内部类
public static class StudentEntry implements BaseColumns {
public static final String TABLE_NAME = "student";
public static final String COLUMN_STU_NAME = "stuName";
public static final String COLUMN_STU_AGE = "stuAge";
}
}
~~~
这里我们只使用了一张表,如果有多张表,那么就在契约类中添加定义表内容的内部类,就像StudentEntry类一样。
看到我们的 StudentEntry 类实现了 BaseColumns 接口,BaseColumns接口中只有两个字段,一个是 _ID ,另一个是 _COUNT,也就是说StudentEntry设计的表有5列。其中_ID那一列作为主键。
以上就是定义表结构,其实就是定义几张不同的表,它有哪些列。最后看下我们上述实现的表的图示。
表的结构定义完成,那我们是不是按照契约创建数据库,创建表了?答案是肯定的。
创建数据库,必然少不了操作数据库的类,Android中给我们提供了SQLiteDatabase 类来进行数据库的相关操作,我们要创建数据库,来看下API,有没有创建数据库的方法。
嘿,找到了 create() ,看下方法的文档说明,咦,当数据库关闭时,数据库内容就被销毁了???(关于create方法,里面有一个In-Memory Database的概念,感兴趣的同学可以去了解下),HAi,这方法不行啊。
接着看API….,哈哈,不看了
好吧,不卖关子了,其实所有的创建数据库的方法(create()、openDatabase(重载)、openOrCreateDatabase(重载)),最终都会执行openDatabase(String path,CursorFactory factory, int flags,DatabaseErrorHandler errorHandler)
看下它是怎么创建
public static SQLiteDatabase openDatabase(String path, CursorFactory factory, int flags,DatabaseErrorHandler errorHandler) {
SQLiteDatabase db = new SQLiteDatabase(path, flags, factory, errorHandler);
db.open();
return db;
}
嘿呀,流了一大把汗终于把数据库new出来了,哎,下面db.open(),打开数据库?是的,我们的数据库并不是new出来就能增删改查的,它要创建并且打开后才能使用它进行CRUD操作的。
数据库创建
path : 数据库创建了,保存的路径,这个路径包含数据库的名称吧,假如我们没有给数据库起名字?是不是很嗨?后面我们使用SQLiteOpenHelper时,会有这个参数,假如传递null,即不给它起名字,那么它便会创建一个”In-Memory Database” ,这里是 In-Memory Databases文档
flags : 这里会有打开的方式(只读 or 读写),假如我们以只读的方法打开了,那么我们只能查询,怎么增删改呢?那就以全以读写的方式打开啊,那我们的数据库部分内容只有有权限的人进行增删改呢,那就让它以只读方法打开啊。嘿,就一个打开就这么麻烦的吗?
数据库更新
业务需求的变更,数据库的表结构可能也要有相应的更改,那数据库的更新该怎么做呢?向上升级,向下降级
升级或者降级,需要备份当前数据库后删除,然后导入新的数据库中,删除和导入的过程中出现了问题怎么办?还要使用数据库事务,开启事务,删除,导入,关闭事务。
据上所述,数据库的创建,和版本更新是一个比较复杂的事情,因此Google给我们提供了一个便于数据库创建和版本更新的辅助工具类 SQLiteOpenHelper ,查看文档,我们会发现它不仅在创建和版本更新上有优势,在和ContentProvider结合使用时还有它别的优势。
其实在初学时,我们并不会有那么复杂的业务,就一个简单增删改查的方法调用,直接一个openOrCreateDatabase()就可以了啊,因此SQLiteOpenHelper的使用不是必须的,但是,它又是必须的,因为我们不会一直是初学者。养成良好的编程风格。
SQLiteOpenHelper是一个辅助类,用于方便我们数据库的创建和更新,还有一个,数据库的创建通常会保存在SDCard中,因此我们自己也可以写一个SQLiteOpenHelper,其中封装一些操作SDCard路径的内容等等类似的SQLiteHelper。
上面说了一些关于我对为何使用SQLiteOpenHelper的理解。下面来看下SQLiteOpenHelper的使用吧。
public class DbHelper extends SQLiteOpenHelper {
private static DbHelper sInstance;
//单例设计模式,确保程序中只有一个Database实例对象,避免Leak错误
public static synchronized DbHelper getInstance(Context context) {
if (sInstance == null) {
sInstance = new DbHelper(context.getApplicationContext());
}
return sInstance;
}
private static final String DATABASE_NAME = "student.db";
private static final int DATABASE_VERSION = 1;
// 私有化DbHelper类构造器,只通过getInstance方法获取DbHelper实例
private DbHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
// 创建数据库
@Override
public void onCreate(SQLiteDatabase db) {
}
// 更新数据库
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
嗯,大概的样子有了,结合我们的实例,学生信息管理开看下,具体的Helper中该添加哪些内容吧。
public class DbHelper extends SQLiteOpenHelper {
......
// 创建student表的sql语句
private static final String SQL_CREATE_TABLE_STUDENT =
"CREATE TABLE " + StudentContract.StudentEntry.TABLE_NAME + " (" +
StudentContract.StudentEntry._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
StudentContract.StudentEntry.COLUMN_STU_NAME + " TEXT NOT NULL, " +
StudentContract.StudentEntry.COLUMN_STU_AGE + " INTEGER" +
");";
// 删除student表的sql语句
private static final String SQL_DELETE_TABLE_STUDENT =
"DROP TABLE IF EXISTS " + StudentContract.StudentEntry.TABLE_NAME;
......
}
public class DbHelper extends SQLiteOpenHelper {
......
// 创建数据库
@Override
public void onCreate(SQLiteDatabase db) {
// 执行创建语句
db.execSQL(SQL_CREATE_TABLE_STUDENT);
}
// 更新数据库
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
//执行删除语句
db.execSQL(SQL_DELETE_TABLE_STUDENT);
//重新创建表
onCreate(db);
}
}
这里只是给出了简单的Helper怎么写,那么具体的我们可以参考Google的代码,这里有个Google I/O 2016官方APP的源码,我们可以去看看大厂是如何写Helper的:ScheduleDatabase类 ,参考并学习它是如何创建和实现版本更新的。
哈,创建数据库写了太多内容了,下面简单写下SQLiteDatabase的C(create)、R(retrieve)、U(update)、D(delete)数据操作的方法好了(因为,网上一大把关于SQLiteDatabase的CRUD操作的文章)。
为了功能分离,我单独创建了一个StudentDb类来是先数据的增删改查
public class StudentDb {
// Database由DbHelper来创建,因此需要添加依赖
private DbHelper mDbHelper;
public StudentDb(DbHelper dbHelper) {
mDbHelper = dbHelper;
}
//添加
public long insert(){
return 0;
}
//删除
public void delete(){
}
//修改
public int update(){
return 0;
}
//查找
public Cursor select(String name){
return null;
}
}
public long insert(String name, int age) {
// 获取数据库对象
SQLiteDatabase db = mDbHelper.getWritableDatabase();
// 用ContentValues装载要保存的数据
ContentValues values = new ContentValues();
values.put(StudentContract.StudentEntry.COLUMN_STU_NAME, name);
values.put(StudentContract.StudentEntry.COLUMN_STU_AGE, age);
// 插入数据
return db.insert(StudentContract.StudentEntry.TABLE_NAME, null, values);
}
这里是参数用String name ,int age,当字段很多时呢?我们可以在别处封装数据,传递ContentValues参数就行了。
public Cursor select(String name) {
// 获取数据库对象
SQLiteDatabase db = mDbHelper.getReadableDatabase();
// 要查询的列
String[] projection = {
StudentContract.StudentEntry._ID,
StudentContract.StudentEntry.COLUMN_STU_NAME,
StudentContract.StudentEntry.COLUMN_STU_AGE};
// 查询条件
String selection = StudentContract.StudentEntry.COLUMN_STU_NAME + " = ?";
String[] selectionArgs = {name};
// 排序条件
String sortOrder =
StudentContract.StudentEntry.COLUMN_STU_AGE + " DESC";
return db.query(
StudentContract.StudentEntry.TABLE_NAME, // 要查询的表名称
projection, // 要查询的列
selection, // 查询条件列
selectionArgs, // 查询条件值
null, // 不使用group by
null, // 不使用having
sortOrder // 分组条件
);
}
要查询的列,如果全都查询的话,直接输入null,没有查询条件的话也是输入null。
public int update(String oldName, String newName) {
// 获取数据库对象
SQLiteDatabase db = mDbHelper.getReadableDatabase();
// 新数据
ContentValues values = new ContentValues();
values.put(StudentContract.StudentEntry.COLUMN_STU_NAME, newName);
// 更新记录的过滤条件
String selection = StudentContract.StudentEntry.COLUMN_STU_NAME + " LIKE ?";
String[] selectionArgs = {oldName};
//更新
return db.update(
StudentContract.StudentEntry.TABLE_NAME,
values,
selection,
selectionArgs);
}
public int delete(String name) {
// 获取数据库对象
SQLiteDatabase db = mDbHelper.getWritableDatabase();
// 删除记录的过滤条件
String selection = StudentContract.StudentEntry.COLUMN_STU_NAME + " LIKE ?";
String[] selectionArgs = {name};
// 删除
return db.delete(
StudentContract.StudentEntry.TABLE_NAME,
selection,
selectionArgs);
}
由上可以看出
当进行查找,数据库中数据不发生变化时,使用helper的getReadableDatabase()方法
当进行增删改,数据发生变化时,我们使用helper的getWritableDatabase()方法
使用已经封装好了的query()方法似乎参数太多,不太好用,再加上爱装逼的我们,使用SQL脚本多好呐。哈哈,好吧,开个玩笑,Google为我们不仅仅提供这种便捷的方法,还提供了一种通用的execSQL()方法,另外还有单门执行select语句的rawQuery()方法。呐,具体用哪个看个人的喜好还有哪个更方便操作了。
至此,关于Android SQLite数据库的内容就结束了,感谢您的阅读。
如果你对我的文章感兴趣,欢迎关注我的微信公众号: oogh ,期待你的到来