Android SQLiteDatabase

Android - SQLite数据库

程序中使用数据库,通常是:

  • 设计表结构
    • 需要哪些表
    • 表之间的关系
    • 有哪些字段
  • 创建数据库
    • 数据库名称
    • 存储路径
    • 文件大小
  • 创建表
    • 字段相关内容
  • 业务相关 (根据不同的需求,可选)
    • 视图
    • 存储过程
    • 函数

那下面来看下我们Android中数据库的操作,更多内容请看:SQLite文档

设计表结构 (Contract)

程序中需要哪些表,有哪些列(字段,属性),表之间的关系…

以前写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那一列作为主键。

以上就是定义表结构,其实就是定义几张不同的表,它有哪些列。最后看下我们上述实现的表的图示。

表结构图

创建数据库 (SQLiteOpenHelper)

表的结构定义完成,那我们是不是按照契约创建数据库,创建表了?答案是肯定的。

创建数据库,必然少不了操作数据库的类,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中该添加哪些内容吧。

数据库操作

  • 添加创建和删除的表的SQL语句
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;

      ......
   }
  • 再看onCreate()和onUpdate()方法中怎么写
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;
    }

}
  • C_添加数据 (create)
    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参数就行了。

  • R_查询数据(retrieve)
    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。

  • U_更新数据(udpate)
    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);
    }
  • D_删除数据(delete)
    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 ,期待你的到来

你可能感兴趣的:(Android开发相关)