Android数据存储之SQLite数据库存储

本文目录:

1. 创建数据库

2. 升级数据库

3. 添加数据

4. 更新数据

5. 删除数据

6. 查询数据


分割线


(本文原理内容来自《Android第一行代码(第二版)》)

介绍:
SQLite是一款轻量级的关系型数据库,它的运算速度非常快,占用源很少,通常只需要几百KB的内存就足够了,因而特别适合在移动设备上使用。
SQLite不仅支持标准的SQL语法,还遵循数据库的ACID事务,所以只要你以前使用过其他的关系型数据库,就可以很快地上手 SQLite。
SQLite比一般的数据库要简单得多,它甚至不用设置用户名和密码就可以使用。
Android正是把这个功能极为强大的数据库嵌入到了系统当中,使得本地持久化的功能有了一次质的飞跃。

1. 创建数据库

Android提供了一个SQLiteOpenHelper帮助类,用于对数据库进行创建和升级
SQLiteOpenHelper是一个抽象类,使用时需要创建自己的类去继承它。
SQLiteOpenHelper有两个抽象方法onCreate()onUpgrade(),必须在自己的类中重写这两个方法,然后分别在这两个方法中实现创建升级数据库的逻辑。

SQLiteOpenHelper中还有两个非常重要的实例方法: getReadableDatabase()getWritableDatabase()
这两个方法都可以创建或打开一个现有的数据库(如果数据库已存在则直接打开,否则创建一个新的数据库),并返回一个可对数据库进行读写操作的对象。
不同的是,当数据库不可写入的时候(如磁盘空间已满),
getReadableDatabase()方法返回的对象将以只读的方式去打开数据库
getWritableDatabase()方法则将出现异常

SQLiteOpenHelper中有两个构造方法可供重写,一般使用参数少的那个构造方法即可。
这个构造方法中接收4个参数:
第一个参数是 Context,必须要有它才能对数据库进行操作。
第二个参数是数据库名,创建数据库时使用的就是这里指定的名称。
第三个参数允许我们在查询数据的时候返回一个自定义的 Cursor,一般都是传入 null。
第四个参数表示当前数据库的版本号,可用于对数据库进行升级操作。

构建出SQLiteOpenHelper的实例之后,再调用它的 getReadableDatabase()getWritableDatabase()方法就能够创建数据库了,数据库文件会存放在/data/data/<package name>/databases/日录下。此时重写的 onCreate()方法也会得到执行,所以通常会在这里去处理一些创建表的逻辑。

Demo:

我们希望创建一个名为BookStore的数据库,并在这个库中创建一张Book表,表中有id、作者、价格、页数和书名。
(SQLite中integer代表整型,real表示浮点型,text表示文本类型,blob表示二进制类型)

  • 新建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)";

    private Context mContext;

    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 succeeded", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    }
}

这里将建表语句定义为字符串常量,并在onCreate()方法中调用了execSQL()方法执行建表语句。

  • activity_main.xml文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/create_database"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Create database"/>

</LinearLayout>

布局里只加入了一个按钮,用于创建数据库

  • MainActivity代码
public class MainActivity extends AppCompatActivity {

    private MyDatabaseHelper dbHelper;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        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) {
                dbHelper.getWritableDatabase();
            }
        });

    }
}

这里我们在onCreate()方法中构建了一个MyDatabaseHelper对象,并且通过构造函数的参数将数据库名指定为BookStore.db,版本号指定为1,然后在Create database按钮的点击事件里调用了getWritableDatabase()方法。这样当第一次点击Create database按钮时,就会检测到当前程序中并没有BookStore.db这个数据库,于是会创建该数据库并调用MyDatabaseHelper中的onCreate()方法,这样Book表也就得到了创建,然后会弹出一个Toast提示创建成功。再次点击Create database按钮时,会发现此时已经存在BookStore.db数据库了,因此不会再创建一次

create database.gif

此时BookStore.db数据库和Book表已经创建成功了,因为当你再次点击Create database按钮时,不会有Toast弹出。

2. 升级数据库

现在已经有了一个Book表用于存放书的各种详细数据,如果想再添加一张Category表用于记录图书的分类,怎么办呢?

  • 在MyDatabaseHelper中添加语句:
图片.png

此时运行程序,Category表并没有创建成功,因为之前我们的BookStore.db数据库已经存在了,不管怎样点击Create database按钮,MyDatabaseHelper中的onCreate()方法都不会执行,也就无法创建新表了。

解决这个问题就要用到升级数据库功能

  • 在MyDatabaseHelper的onUpgrade()函数中加入以下语句
图片.png
 @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()方法得到执行,修改SQLiteOpenHelper构造方法的第四个参数,之前设置的1,现在传入一个大于1的值,表示我们对数据库进行升级了
public class MainActivity extends AppCompatActivity {

    private MyDatabaseHelper dbHelper;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        dbHelper = new MyDatabaseHelper(this, "BookStore.db", null, 2);

        //创建数据
        Button createDatabase = (Button) findViewById(R.id.create_database);
        createDatabase.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                dbHelper.getWritableDatabase();
            }
        });

    }
}

重新运行程序,Category表就创建成功了。

3. 添加数据

SQLiteDatabase中提供了一个insert()方法,用于添加数据。
它接受3个参数
第一个参数:表名
第二个参数:用于在未指定添加数据的情况下给某些可为空的列自动赋值NULL,我们一般直接传入null
第三个参数:一个ContentValues对象,它提供了一系列put()方法重载,用于向ContentValues中添加数据,只需将表中的每个列名以及对应的数据传入即可。

Demo:

  • activity_main.xml中加入Add data按钮控件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <Button
        android:id="@+id/create_database"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Create database"/>

    <Button
        android:id="@+id/add_data"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Add data"/>

</LinearLayout>
  • 更改MainActivity代码,onCreate()中加入代码
public class MainActivity extends AppCompatActivity {

    private MyDatabaseHelper dbHelper;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        dbHelper = new MyDatabaseHelper(this, "BookStore.db", null, 2);

        //创建数据
        Button createDatabase = (Button) findViewById(R.id.create_database);
        createDatabase.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                dbHelper.getWritableDatabase();
            }
        });
        
        //添加数据
       Button addData = (Button) findViewById(R.id.add_data);
       addData.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                SQLiteDatabase db = dbHelper.getWritableDatabase();
                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); // 插入第二条数据
            }
        });
    }
}

在添加数据按钮的点击事件里面,我们先获取到了SQLiteDatabase对象,然后使用ContentValues来对要添加的数据进行组装。

这里只对Book表里其中四列的数据进行了组装,id那一列没并没给它赋值。这是因为在前面创建表的时候,我们就将id列设置为自增长了,它的值会在入库的时候自动生成,所以不需要手动给它赋值了。

接下来调用了insert()方法将数据添加到表当中,注意这里我们实际上添加了两条数据,上述代码中使用 Contentvalues分别组装了两次不同的内容,并调用了两次 insert()方法。

重新运行程序,点击ADD DATA 按钮便可在表中添加数据了

4. 更新数据

关于更新数据,SQLiteDatabase中提供了一个update()方法,用于对数据更新。
该方法接受4个参数
第一个参数:表名
第二个参数:Contentvalues对象,在这里把要更新的数据组装进去
第三个参数第四个参数:用于约束更新某一行或某几行的数据(默认更新所有行)

Demo:

  • activity_main.xml中加入update data按钮控件
    <Button
        android:id="@+id/update_data"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Update data"/>
  • 更改MainActivity代码,onCreate()中加入代码
        //更新数据
        Button updateData = (Button) findViewById(R.id.update_data);
        updateData.setOnClickListener(new View.OnClickListener() {
            @Override
            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" });
            }
        });

这里在更新数据按钮的点击事件里面构建了一个 ContentValues对象,并且只给它指定了一组数据,说明我们只是想把价格这一列的数据更新成10.99。然后调用了SQLiteDatabase的update()方法去执行具体的更新操作,可以看到,这里使用了第三、第四个参数来指定具体更新哪几行。第三个参数对应的是SQL语句的 where部分,表示更新所有name等于?的行,而?是一个占位符,可以通过第四个参数提供的一个字符串数组为第三个参数中的每个占位符指定相应的内容。因此上述代码想表达的意图是将名字是 The Da Vinci Code的这本书的价格改成10.99。

5. 删除数据

SQLiteDatabase中提供了一个delete()方法,用于删除数据。
该方法接受3个参数
第一个参数:表名
第二个参数第三个参数:用于约束删除某一行或某几行的数据(默认删除所有行)

Demo:

  • activity_main.xml中加入delete data按钮控件
     <Button
        android:id="@+id/delete_data"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Delete data" />
  • 更改MainActivity代码,onCreate()中加入代码
        //删除数据
        Button deleteButton = (Button) findViewById(R.id.delete_data);
        deleteButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                SQLiteDatabase db = dbHelper.getWritableDatabase();
                db.delete("Book", "pages > ?", new String[] { "500" });
            }
        });

程序指定删除那些页数超过500页的书

6. 查询数据

SQLiteDatabase中提供了一个query()方法,用于对数据进行查询。
该方法参数比较复杂,最短的一个重载方法也需要7个参数
第一个参数:表名
第二个参数:指定查询的列名(默认所有列)
第三个参数:和第四个参数:用于约束查询某一行或某几行的数据(默认查询所有行)
第五个参数:指定需要group by的列,不指定则不进行group by操作
第六个参数:对group by之后的数据进一步过滤
第七个参数:指定查询结果的排序方式

Demo:

  • activity_main.xml中加入query data按钮控件
    <Button
        android:id="@+id/query_data"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Query data" />
  • 更改MainActivity代码,onCreate()中加入代码
        //查询数据
        Button queryButton = (Button) findViewById(R.id.query_data);
        queryButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                SQLiteDatabase db = dbHelper.getWritableDatabase();
                // 查询Book表中所有的数据
                Cursor cursor = db.query("Book", null, null, null, null, null, null);
                if (cursor.moveToFirst()) {
                    do {
                        // 遍历Cursor对象,取出数据并打印
                        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("MainActivity", "book name is " + name);
                        Log.d("MainActivity", "book author is " + author);
                        Log.d("MainActivity", "book pages is " + pages);
                        Log.d("MainActivity", "book price is " + price);
                    } while (cursor.moveToNext());
                }
                cursor.close();
            }
        });

我们首先在查询按钮的点击事件里面调用了SQLiteDatabase的query()方法查询数据。这里的query()方法非常简单,只是使用了第一个参数指明去查询Book表,后面的参数全部为null,表示希望查询这张表中的所有数据。虽然这张表中目前只剩下一条数据了。査询完之后就得到了一个Cursor对象,接着我们调用它的 moveToFirst()方法将数据的指针移动到第一行的位置,然后进入了一个循环当中,去遍历查询到的每一行数据。在这个循环中可以通过Cursor的getColumnIndex()方法获取到某一列在表中对应的位置索引,然后将这个索引传入到相应的取值方法中,就可以得到从数据库中读取到的数据了。接着我们使用Log将数据打印出来。最后别忘了调用close()方法关闭Cursor。

重新运行程序,可以看到logcat打印的内容如下:

图片.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,646评论 4 366
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,979评论 1 301
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,391评论 0 250
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,356评论 0 215
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,740评论 3 293
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,836评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,022评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,764评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,487评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,728评论 2 252
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,200评论 1 263
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,548评论 3 260
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,217评论 3 241
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,134评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,921评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,919评论 2 283
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,766评论 2 274