SQLite数据库精炼详解

版权声明:因为个人水平有限,文章中可能会出现错误,如果你觉得有描述不当、代码错误等内容或者有更好的实现方式,欢迎在评论区告诉我,即刻回复!最后,欢迎关注博主!谢谢 https://blog.csdn.net/weixin_41101173/article/details/83715018

一、前期基础知识储备

Android本地化存储三种方式:
①文件存储,存储简单二进制数据和文本数据;
②SharedPreference存储,键值对存储,存储数据类型多样,存储方式单一;
③SQLite数据库存储,Android内置数据库,存储复杂关系型数据,质的飞跃。
在这里插入图片描述
SQLite正式发布于2000年,现在Android内置的是SQLite3.0。占用几百KB资源,但运算速度飞快,非常适合在移动设备上使用。SQLite数据库使用时会涉及到三个类:
①SQLiteOpenHelper:Android系统提供的操作SQLite数据库的方式。工具类 抽象类,我们通过继承该类,然后重写数据库创建以及更新的方法,我们还可以通过该类的对象获得数据库实例或者关闭数据库。
②SQliteDatabase:平常数据库操作方式使用的类,数据库访问类——我们可以通过该类的对象来直接使用操作数据库的方式对数据库做增删改查的操作。(见用于,熟悉数据库语法的大牛)
③Cursot:游标,有点类似于JDBC里的resultset,结果集!可以简单理解为指向数据库中某一记录的指针。使用可以同平常数据库一样,也可以使用Android系统提供的操作方式。
本篇博客,主要分析Android系统内置的操作数据库的方式,所以打交道的类只有两个:SQLiteOpenHelper和Cursot。

二、上代码,具体分析

1)使用SQLiteOpenHelper创建数据库

①SQLiteOpenHelper 是一个抽象类,如果需要使用它的话,就需要创建一个帮助类去继承它;
②抽象父类SQLiteOpenHelper 中有两个抽象方法:onCreate()onUpdate(),需要在子类里重写这两个方法,然后分别在这两个方法中去实现创建和升级数据库的逻辑;
新建 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) {

    }
}

③SQLiteOpenHelper 中另外两个非常重要的实例方法,getReadableDatabase() (查)和 getWritableDatabase()(增删改)。这两种方法都可以创建或打开一个现有的数据库(如果数据库已存在则直接打开,否则创建一个新的数据库),并返回一个可对数据库进行增删改查操作的对象;
④子类需要重写父类SQLiteOpenHelper 的两个构造方法,第二个构造方法接收四个参数,第一个参数是 Context,必须要有Context对象才能对数据库进行操作。第二个参数是数据库名,创建数据库时使用的就是这里指定的名称。第三个参数允许在查询数据库的时候返回一个自定义的 Cursor,一般传入null。第四个参数表示当前数据库的版本号,可用于对数据库进行升级操作。

构建出 SQLiteOpenHelper 的实例之后,再调用它的 getReadableDatabase() 或 getWritableDatabase() 方法就能够创建数据库了,数据库文件会存放在 /data/data/<包名>/database/ 目录下。利用Android Studio右下角工具可直接查看。

在 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();
            }
        });
    }
}

点击按钮 BookStore.db 数据库和 Book 表就已经创建成功了,再次点击不会再有Toast弹出。

2)使用SQLiteOpenHelper升级数据库

onUpdate() 方法是用于对数据库进行升级的,它在整个数据库的管理工作中担当着非常重要的作用。
目前 BookStore.db 中已经有一张 Book 表用于存放书的各种详细数据,接下来再添加一张 Category 表用于记录书籍的分类。将建表语句添加到 MyDatabaseHelper 中,代码如下:

    public static final String CREATE_CATEGORY = "CREATE TABLE category ("
            + "id integer PRIMARY KEY Autoincrement , "
            + "category_name text , "
            + "category_code integer )";

并在 onCreate()方法中添加:db.execSQL( CREATE_CATEGORY); 这条语句,运行程序,并不会弹出创建成功的提示。因为此时 BookStore.db 数据库已经存在了,之后不论怎样点击创建按钮,MyDatabaseHelper 中的 onCreate() 方法都不会再次执行,因此新添加的表也就无法得到创建了。
只需要巧妙的运用 SQLiteOpenHelper 的升级功能就可以很轻松的解决这个问题。
修改 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,2);

这里将数据库版本号指定为2,表示我们对数据库进行升级了。重新运行程序,并点击创建数据库按钮,这时就会再次弹出创建成功的提示。
升级数据库的最佳方式:
粗暴的删除当前所有的表来达到更新的效果,对于用户来说是非常糟糕的,因为以前程序中存储的本地数据全部丢失了。其实只需进行一些合理的控制,就可以保证升级数据库的时候数据并不会丢失了。

    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        switch (oldVersion) {
            case 1:
                db.execSQL(CREATE_CATEGORY);
            default:
        }
    }

每一个数据库版本都会对应一个版本号,当指定的数据库版本号大于当前数据库版本号的时候,就会进入到 onUpgrade()方法中去执行更新操作。这里需要为每一个版本号赋予它各自改变的内容,然后在 onUpgrade()方法中对当前数据库的版本进行判断,再执行相应的改变就可以了。
注意,switch 中每一个 case 的最后都是没有使用 break 的,这是为了保证跨版本升级的时候,每一次的数据库修改都能被全部执行。使用这种方式来维护数据库的升级,不管版本怎样更新,都可以保证数据库的表结构是最新的,而且表中的数据也完全不会丢失。

3)使用SQLiteOpenHelper升级数据库

注:这里使用的Android内置的一系列辅助方法,即使用SQLiteOpenHelper操作数据库,并不是使用SQliteDatabase直接操作数据表。
①添加数据-insert()
SQLiteDatabase 中提供了一个 insert() 方法,用于添加数据。
insert() 方法接收三个参数,第一个参数是表名,我们希望向哪张表里添加数据,这里就传入该表的名字。第二个参数用于在未指定添加数据的情况下给某些可为空的列自动赋值NULL,一般用不到这个功能,直接传入 null 即可。第三个参数是一个 ContentValues 对象,它提供了一系列的 put() 方法重载,用于向 ContentValues 中添加数据,只需要将表中的每个列名及相对应的待添加数据传入即可。
MainActivity中代码如下:

            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 列设置为自动增长了,它的值会在入库的时候自动生成,不需要手动赋值。
②更新数据-update()
SQLiteDatabase 中提供了一个非常好用的 update() 方法用于对数据进行更新,这个方法接收四个参数,第一个参数和 insert()方法一样,也是表名,在这里指定去更新哪张表里的数据。第二个参数是 ContentValues 对象,把要更新的数据在这里组装进去。第三、第四个参数用于去约束更新某一行的数据,不指定的话默认就是更新所有行。
MainActivity中代码如下:

            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"});
            }

③删除数据-delete()
SQLiteDatabase 中提供了一个 delete()方法专门用于删除数据,这个方法接收三个参数,第一个参数仍然是表名,第二、第三个参数用于约束删除某一行或某几行的数据,不指定的话默认就是删除所有行。
MainActivity中代码如下:

            public void onClick(View v) {
                SQLiteDatabase db = dbHelper.getWritableDatabase();
                db.delete("Book", "pages > ?", new String[]{"500"});
            }

④查询数据-query(),会使用到第三个类Cursor游标类
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 对象,查询到的所有数据都将从这个对象中取出。
MainActivity中代码如下:

            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。(不关闭Cursor会造成内存泄漏)
游标Cursor的相关操作:
遍历游标:

        while(c.moveToNext()){
              int id=c.getInt(c.getColumnName(列名ID));
              String name=c.getString(c.getColumnIndex(字段名NAME));
        }

常用方法:

(1)move(int offset);将指针上下移动offset个记录。若offset值为正数则向下移动,若为负数则向上移动。
(2)boolean moveToFirst();指针移至结果集的第一个记录。若移动成功则返回true,否则返回false。
(3)boolean moveToLast();指针移至结果集的最后一个记录。若移动成功则返回true,否则返回false。
(4)boolean moveToNext();指针向后移动一条记录,若移动成功则返回true,否则返回false。
(5)boolean moveToPrevious();指针向前移动一条记录,若移动成功则返回true,否则返回false。
(6)isLast();若指针在Cursor结果集的最后一条记录,则返回true,否则返回false。
(7)isAfterLast();若指针在Cursor结果集最后一条记录之后,则返回true,否则返回false。
(8)isFirst();若指针在Cursor结果集的第一条记录,则返回true,否则返回false。
(9)isBeforeFirst();若指针在Cursor结果集的第一条记录之前,则返回true,否则返回false。
(10)isClose();Cursot对象是否关闭,若关闭则返回true,否则返回false。
(11)int getColumnIndex(String columnName);获得指定列的索引值。
(12)String getString(int columnIndex);按列的索引获取字符串类型的数据。

--------------------------------------------------SQLiteOpenHelper分割线----------------------------------------------------

4)SQLiteDatabase数据库相关操作

上面的数据库操作都是利用SQLiteOpenHelper进行的,有兴趣的读者也可尝试下直接使用SQLite自带的数据库语言进行数据库的操作。
SQLiteDatabase数据库类:提供了针对数据库的增 删 改 查的操作。
注:除了Cursor游标对象、SQLiteDatabase数据库对象也需要关闭。
SQLiteDatabase db=openOrCreateDatabase(“students.db”,MODE_PRIVATE,null);如果数据库不存在则执行创建操作,存在则执行打开操作。
db.execSQL(“create table if not exists student (_id integer primary key autoincrement,name varchar(20),age integer)”);创建数据表,如果存在则不创建。
db.execSQL(“insert into student(_id,name,age)values(null,?,?)”,new Object[]{“张立冬”,25});插入数据;
db.execSQL(“delete from student where name=‘张立冬’”);删除数据;
db.execSQL(“update student set name=?,age=?”,new Object[]{“小明”,18});修改数据;

    Cursor c=db.rawQuery("select * from student",null);查询数据——返回游标(结果集);
    while(c.moveToNext()){将游标移到下一行,存在数据返回true,不存在数据返回false;
         int id=c.getInt(c.getColumnIndex("_id"));获得对应列名中该行的整型数据;
         String name=c.getString(c.getColumnIndex("name"));获得对应列名中该行的字符串数据;
         int age=c.getInt(c.getColumnIndex("age"));获得对应列名中该行的整型数据
}

注:增删改使用的是execSQL()方法,查询用的是rawQuery()方法。

猜你喜欢

转载自blog.csdn.net/weixin_41101173/article/details/83715018