48. 【Android教程】数据库:SQLite 的使用

简介: 48. 【Android教程】数据库:SQLite 的使用

今天来学习另一种比较专业的存储方式——数据库,在 Android 中引入了一个轻量级的数据库框架:SQLite。如果你对数据库非常熟悉,那它可以全面支持数据的 SQL 语言,同时也提供了 Java 接口方便不太熟悉数据库的 Android 工程师使用。

1. SQLite 是什么

SQLite 是一个开源的轻量级关系数据库管理系统,简称RDMS(Relational Database Management System)。Android 通过 SQLIte 来进行数据库增删改查等相关操作,如果想进一步了解 SQLite,可以参考网上教程的数据库相关课程,这里主要围绕 Android 中的使用方法来展开。

Android 已经预置了 SQLite DateBase,所以我们不需要做任何的依赖配置,跟前面几种存储方式一样,存储在 SQLite 的数据也是 App 的私有数据,也就是只有自己的 App 能够访问,这样能够保证数据安全。

2. 数据库辅助类

为了帮助我们快速使用数据库,Android 系统封装了一个辅助类——SQLite Helper,可以通过辅助类完成数据库的更新、升级等操作,SQLite Helper 使用示例如下:

public class DbHandler extends SQLiteOpenHelper {
    private static final int DB_VERSION = 1;
    private static final String DB_NAME = "usersdb";
    private static final String TABLE_Users = "userdetails";
    private static final String KEY_ID = "id";
    private static final String KEY_NAME = "name";
    private static final String KEY_LOC = "location";
    private static final String KEY_DESG = "designation";
    public DbHandler(Context context){
        super(context,DB_NAME, null, DB_VERSION);
    }
    @Override
    public void onCreate(SQLiteDatabase db) {
       // 创建表格
        String CREATE_TABLE = "CREATE TABLE " + TABLE_Users + "("
                + KEY_ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + KEY_NAME + " TEXT,"
                + KEY_LOC + " TEXT,"
                + KEY_DESG + " TEXT"+ ")";
        db.execSQL(CREATE_TABLE);
    }
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        // 删除表格
        db.execSQL("DROP TABLE IF EXISTS " + TABLE_Users);
        // 重建
        onCreate(db);
    }
}

在代码中我们创建了一个名叫“ usersdb”的数据库,在“ usersdb”中创建了一张名为“ userdetails”的表格,里面包含姓名、地址位置、描述等用户信息。细心的读者可能会注意到,代码中存在两个生命周期回调方法:


onCreate(SQLiteDatabase db):

当数据库被创建的时候回调,此方法在整个 App 运行期间只会被回调一次,在数据库创建好之后就不会再回调,通常会把数据库中的表格创建代码写在onCreate()中。


onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion):

当数据库需要升级时调用,通常发生在 App 覆盖升级的时候。当我们需要修改数据库,比如删除字段、增加表格、修改字段等等操作的时候,需要将对应数据库的版本号 +1,此时在 App 升级之后发现版本号有变化,便会回调此方法,我们可以在onUpgrade()方法中做数据的适配及搬迁。

3. 使用 SQL 操作数据库

前面提到过,我们完全可以通过传统的 SQL 语言来操作数据库。

3.1 SQL 语言执行方法

Android 为我们提供了以下方法来执行 SQL:

  • **execSQL(SQL,Object[]):**执行带占位符的SQL语句,用于修改数据库内容
  • **rawQuery(SQL,Object[]):**执行带占位符的SQL查询语句,返回查询结构的游标指针——Cursor

3.2 数据库查询指针

Cursor 相当于一个数据库指针,指向查询的结果,我们可以通过移动 Cursor 来获取想要的数据,Cursor支持以下方法:

  • move(offset):
  • 向上或者向下移动,参数是移动的行数,正数表示向下,负数向上
  • moveToFirst():
  • 移动到第一行,移动成功返回 true,否则为 false
  • moveToLast():
  • 移动到最后一行,成功返回true,否则为 flase
  • moveToNext():
  • 移动到下一行,成功返回true,否则为 false
  • moveToPrevious():
  • 移动到前一条数据
  • getCount():
  • 获得总得数据条数
  • isFirst():
  • 判断当前是否是第一条记录
  • isLast():
  • 判断是否是最后一条记录
  • moveToPosition(int):
  • 直接移动到指定行

4. 使用 Android 接口操作数据库

如果你对数据库并不是很熟悉或者不想用 SQL 来操作数据库,同样可以采用纯 Java 接口来操作。在 Android 中数据库的内容用 ContentValues 表示,所以我们的增删改查其实都是对某个 ContentValues 进行操作。

4.1 插入数据

我们通过insert()方法进行插入:

// 采用写模式获取数据库对象
SQLiteDatabase db = this.getWritableDatabase();
// 创建一个数据表,key 就是表格每一列的列名
ContentValues cValues = new ContentValues();
cValues.put(KEY_NAME, name);
cValues.put(KEY_LOC, location);
cValues.put(KEY_DESG, designation);
// 添加一行数据,返回这一行的主 key
long newRowId = db.insert(TABLE_Users,null, cValues);

4.2 查询数据

查询数据是通过query()方法实现的:

// 以写模式获取数据库对象
SQLiteDatabase db = this.getWritableDatabase();
// 从TABLE_Users中查询指定userid的数据的名称、位置、描述信息
Cursor cursor = db.query(TABLE_Users, new String[]{KEY_NAME, KEY_LOC, KEY_DESG}, KEY_ID+ "=?",new String[]{String.valueOf(userid)},null, null, null, null);

4.3 更新数据

更新数据使用update()方法:

// 以写的模式获取数据库对象
SQLiteDatabase db = this.getWritableDatabase();
ContentValues cVals = new ContentValues();
cVals.put(KEY_LOC, location);
cVals.put(KEY_DESG, designation);
int count = db.update(TABLE_Users, cVals, KEY_ID+" = ?",new String[]{String.valueOf(id)});

4.4 删除数据

删除数据使用delete()方法

SQLiteDatabase db = this.getWritableDatabase();
db.delete(TABLE_Users, KEY_ID+" = ?",new String[]{String.valueOf(userid)});

以上就是 SQLite 的 CURD(insert、update、delete、query)操作对应的几个系统 API。

5. SQLite 使用示例

数据库通常用于存储批量的 Key-Value,比如通讯录里、短信、用户信息等。本节就借用上一节的例子,在 SharePreferenced 一节中我们实现了一个登陆框,这一节我们稍作修改,做一个用户信息登记表。首页类似登录框一样提供一个用户信息等级的页面,然后登记完我们通过 ListView 展示所有用户信息。为了方便大家理解,我们来统计一下王者荣耀英雄的名称、主打位置以及特征描述。



5.1 英雄登记页面

等级页面主要两部分组成:

  • **信息输入框:**包含名称、主打位置、特征
  • 确认登记 Button
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
 
    <TextView
        android:id="@+id/fstTxt"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="100dp"
        android:layout_marginTop="150dp"
        android:text="名称" />
 
    <EditText
        android:id="@+id/txtName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="100dp"
        android:ems="10" />
 
    <TextView
        android:id="@+id/secTxt"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="100dp"
        android:text="主打位置" />
 
    <EditText
        android:id="@+id/txtLocation"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="100dp"
        android:ems="10" />
 
    <TextView
        android:id="@+id/thirdTxt"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="100dp"
        android:text="特征描述" />
 
    <EditText
        android:id="@+id/txtDesignation"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="100dp"
        android:ems="10" />
 
    <Button
        android:id="@+id/btnSave"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="100dp"
        android:text="确认登记" />
</LinearLayout>

5.2 数据库辅助类

辅助类就是提供操作英雄登记表数据的增删改查,使用第 4 小节学习的 API 即可,创建“DbHelper.Java”类,编写如下代码:

package com.emercy.myapplication;
 
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
 
import java.util.ArrayList;
import java.util.HashMap;
 
public class DbHelper extends SQLiteOpenHelper {
    private static final int DB_VERSION = 1;
    private static final String DB_NAME = "herodb";
    private static final String TABLE_Heres = "herodetails";
    private static final String KEY_ID = "id";
    static final String KEY_NAME = "name";
    static final String KEY_POS = "position";
    static final String KEY_DESG = "designation";
 
    public DbHelper(Context context) {
        super(context, DB_NAME, null, DB_VERSION);
    }
 
    @Override
    public void onCreate(SQLiteDatabase db) {
        String CREATE_TABLE = "CREATE TABLE " + TABLE_Heres + "("
                + KEY_ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + KEY_NAME + " TEXT,"
                + KEY_POS + " TEXT,"
                + KEY_DESG + " TEXT" + ")";
        db.execSQL(CREATE_TABLE);
 
        initHero(db, "安琪拉","中路", "草丛");
        initHero(db, "马超","对抗路", "帅气");
        initHero(db, "伽罗","发育路", "禁掉");
        initHero(db, "鲁班","发育路", "手长");
        initHero(db, "兰陵王","打野", "绕后");
        initHero(db, "猴子","打野", "爆发");
    }
 
    private void initHero(SQLiteDatabase db, String name, String location, String designation) {
        ContentValues cValues = new ContentValues();
        cValues.put(KEY_NAME, name);
        cValues.put(KEY_POS, location);
        cValues.put(KEY_DESG, designation);
        long newRowId = db.insert(TABLE_Heres, null, cValues);
    }
 
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        // 重建数据表
        db.execSQL("DROP TABLE IF EXISTS " + TABLE_Heres);
        onCreate(db);
    }
 
    // 增加英雄角色详情
    void insertUserDetails(String name, String location, String designation) {
        // 获取可写数据库对象
        SQLiteDatabase db = this.getWritableDatabase();
        // 以key为列创建数据
        ContentValues cValues = new ContentValues();
        cValues.put(KEY_NAME, name);
        cValues.put(KEY_POS, location);
        cValues.put(KEY_DESG, designation);
 
        long newRowId = db.insert(TABLE_Heres, null, cValues);
        db.close();
    }
 
    // 查询数据详情
    public ArrayList<HashMap<String, String>> GetUsers() {
        SQLiteDatabase db = this.getWritableDatabase();
        ArrayList<HashMap<String, String>> userList = new ArrayList<>();
        StringBuilder queryBuilder = new StringBuilder();
        String query = "SELECT " + KEY_NAME + ", " + KEY_POS + ", " + KEY_DESG + " FROM " + TABLE_Heres;
        Cursor cursor = db.rawQuery(query, null);
        while (cursor.moveToNext()) {
            HashMap<String, String> user = new HashMap<>();
            user.put(KEY_NAME, cursor.getString(cursor.getColumnIndex(KEY_NAME)));
            user.put(KEY_POS, cursor.getString(cursor.getColumnIndex(KEY_POS)));
            user.put(KEY_DESG, cursor.getString(cursor.getColumnIndex(KEY_DESG)));
            userList.add(user);
        }
        return userList;
    }
 
    // 获取某个英雄的详情数据
    public ArrayList<HashMap<String, String>> GetUserByUserId(int userid) {
        SQLiteDatabase db = this.getWritableDatabase();
        ArrayList<HashMap<String, String>> userList = new ArrayList<>();
        String query = "SELECT name, location, designation FROM " + TABLE_Heres;
        Cursor cursor = db.query(TABLE_Heres, new String[]{KEY_NAME, KEY_POS, KEY_DESG}, KEY_ID + "=?", new String[]{String.valueOf(userid)}, null, null, null, null);
        if (cursor.moveToNext()) {
            HashMap<String, String> user = new HashMap<>();
            user.put("name", cursor.getString(cursor.getColumnIndex(KEY_NAME)));
            user.put("designation", cursor.getString(cursor.getColumnIndex(KEY_DESG)));
            user.put("location", cursor.getString(cursor.getColumnIndex(KEY_POS)));
            userList.add(user);
        }
        return userList;
    }
 
    // 删除数据
    public void DeleteUser(int userid) {
        SQLiteDatabase db = this.getWritableDatabase();
        db.delete(TABLE_Heres, KEY_ID + " = ?", new String[]{String.valueOf(userid)});
        db.close();
    }
 
    // 更新英雄数据
    public int UpdateUserDetails(String location, String designation, int id) {
        SQLiteDatabase db = this.getWritableDatabase();
        ContentValues cVals = new ContentValues();
        cVals.put(KEY_POS, location);
        cVals.put(KEY_DESG, designation);
        int count = db.update(TABLE_Heres, cVals, KEY_ID + " = ?", new String[]{String.valueOf(id)});
        return count;
    }
}

我们在onCreate中创建了英雄表格,并预置了几条英雄数据(英雄描述纯手打,有误请谅解 - -!),接着对外暴露了表格的 CURD 接口。

5.3 英雄登记逻辑

读取输入框的英雄属性,接着通过DbHelper类提供的 API 进行插入操作:

 
package com.emercy.myapplication;
 
import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
 
public class MainActivity extends Activity {
    EditText name, loc, desig;
    Button saveBtn;
    Intent intent;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        name = (EditText) findViewById(R.id.txtName);
        loc = (EditText) findViewById(R.id.txtLocation);
        desig = (EditText) findViewById(R.id.txtDesignation);
        saveBtn = (Button) findViewById(R.id.btnSave);
        saveBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String username = name.getText().toString();
                String location = loc.getText().toString();
                String designation = desig.getText().toString();
                DbHelper DbHelper = new DbHelper(MainActivity.this);
                DbHelper.insertUserDetails(username, location, designation);
                intent = new Intent(MainActivity.this, SecondActivity.class);
                startActivity(intent);
                Toast.makeText(getApplicationContext(), "英雄信息登记成功", Toast.LENGTH_SHORT).show();
            }
        });
    }
}

5.4 英雄列表 listView

创建 list.xml 布局文件,用来展示英雄的名称、位置和描述:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:padding="5dp">
 
    <TextView
        android:id="@+id/name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="17sp"
        android:textStyle="bold" />
 
    <TextView
        android:id="@+id/designation"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/name"
        android:layout_marginTop="7dp"
        android:textColor="#343434"
        android:textSize="14dp" />
 
    <TextView
        android:id="@+id/location"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBaseline="@+id/designation"
        android:layout_alignBottom="@+id/designation"
        android:layout_alignParentEnd="true"
        android:textColor="#343434"
        android:textSize="14sp" />
</RelativeLayout>

5.5 英雄列表展示

最后就是一个英雄列表的 Activity,里面放置一个 listView用于展示所有的英雄数据:

package com.emercy.myapplication;
 
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.SimpleAdapter;
 
import java.util.ArrayList;
import java.util.HashMap;
 
import static com.emercy.myapplication.DbHelper.KEY_DESG;
import static com.emercy.myapplication.DbHelper.KEY_NAME;
import static com.emercy.myapplication.DbHelper.KEY_POS;
 
public class SecondActivity extends Activity {
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.second);
        DbHelper db = new DbHelper(this);
        ArrayList<HashMap<String, String>> userList = db.GetUsers();
        ListView lv = (ListView) findViewById(R.id.user_list);
        ListAdapter adapter = new SimpleAdapter(SecondActivity.this, userList, R.layout.list, new String[]{KEY_NAME, KEY_DESG, KEY_POS}, new int[]{R.id.name, R.id.designation, R.id.location});
        lv.setAdapter(adapter);
        Button back = (Button) findViewById(R.id.btnBack);
        back.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finish();
            }
        });
    }
}

通过DbHelper提供的检索接口,拿到每一行的数据绑定到 ListView 上即可。编译运行,完成登陆之后便会跳转到英雄列表,此时列表中展示的是预置的数据以及我们刚刚登记的数据。


最后记得在 AndroidManifest.xml 中添加 SecondActivity 的注册。


 

        <activity android:name=".SecondActivity" />

6. 小结

本节学习了一个比较专业的存储方式,SQLite 是一种轻量级的开源数据库框架,已经被预置到 Android 系统中,我们可以非常方便的使用。既可以通过标准的 SQL 语句进行操作,也可以使用Android 提供的 Java 接口做 CURD。通过最后一个例子可以学习到 SQLite 的所有常用接口,在今后需要用到数据库的时候,可以回头来看看本节的例子。

相关文章
|
28天前
|
监控 Java 应用服务中间件
达梦数据库DEM监控部署教程分享
达梦数据库DEM监控部署教程分享
52 2
|
14天前
|
SQL 关系型数据库 数据库
七天.NET 8操作SQLite入门到实战详细教程(选型、开发、发布、部署)
七天.NET 8操作SQLite入门到实战详细教程(选型、开发、发布、部署)
|
22天前
|
SQL 存储 小程序
【教程】navicat配合HTTP通道远程连接SQLite数据库
本文介绍了如何通过 Navicat Premium 工具配合 n_tunnel_sqlite.php 和 HTTP 通道远程连接服务器上的 SQLite 数据库。SQLite 是一种自给自足的、无服务器的 SQL 数据库引擎,由于其端口未对外开放,直接使用 Navicat 进行远程连接不可行。文章详细记录了使用 HTTP 通道实现远程连接的过程,包括定位本地 `ntunnel_sqlite.php` 文件,将其上传至服务器,并通过 Navicat 配置 HTTP 通道连接 SQLite 数据库的具体步骤。
22 0
【教程】navicat配合HTTP通道远程连接SQLite数据库
|
30天前
|
安全 Java 关系型数据库
毕设项目&课程设计&毕设项目:基于springboot+jsp实现的健身房管理系统(含教程&源码&数据库数据)
本文介绍了一款基于Spring Boot和JSP技术实现的健身房管理系统。随着健康生活观念的普及,健身房成为日常锻炼的重要场所,高效管理会员信息、课程安排等变得尤为重要。该系统旨在通过简洁的操作界面帮助管理者轻松处理日常运营挑战。技术栈包括:JDK 1.8、Maven 3.6、MySQL 8.0、JSP、Shiro、Spring Boot 2.0等。系统功能覆盖登录、会员管理(如会员列表、充值管理)、教练管理、课程管理、器材管理、物品遗失管理、商品管理及信息统计等多方面。
|
28天前
|
JavaScript Java 关系型数据库
毕设项目&课程设计&毕设项目:基于springboot+vue实现的前后端分离的考试管理系统(含教程&源码&数据库数据)
在数字化时代背景下,本文详细介绍了如何使用Spring Boot框架结合Vue.js技术栈,实现一个前后端分离的考试管理系统。该系统旨在提升考试管理效率,优化用户体验,确保数据安全及可维护性。技术选型包括:Spring Boot 2.0、Vue.js 2.0、Node.js 12.14.0、MySQL 8.0、Element-UI等。系统功能涵盖登录注册、学员考试(包括查看试卷、答题、成绩查询等)、管理员功能(题库管理、试题管理、试卷管理、系统设置等)。
毕设项目&课程设计&毕设项目:基于springboot+vue实现的前后端分离的考试管理系统(含教程&源码&数据库数据)
|
1月前
|
JavaScript Java Maven
毕设项目&课程设计&毕设项目:springboot+vue实现的在线求职管理平台(含教程&源码&数据库数据)
本文介绍了一款基于Spring Boot和Vue.js实现的在线求职平台。该平台采用了前后端分离的架构,使用Spring Boot作为后端服务
毕设项目&课程设计&毕设项目:springboot+vue实现的在线求职管理平台(含教程&源码&数据库数据)
|
1月前
|
资源调度 关系型数据库 MySQL
【Flink on YARN + CDC 3.0】神操作!看完这篇教程,你也能成为数据流处理高手!从零开始,一步步教会你在Flink on YARN模式下如何配置Debezium CDC 3.0,让你的数据库变更数据瞬间飞起来!
【8月更文挑战第15天】随着Apache Flink的普及,企业广泛采用Flink on YARN部署流处理应用,高效利用集群资源。变更数据捕获(CDC)工具在现代数据栈中至关重要,能实时捕捉数据库变化并转发给下游系统处理。本文以Flink on YARN为例,介绍如何在Debezium CDC 3.0中配置MySQL连接器,实现数据流处理。首先确保YARN上已部署Flink集群,接着安装Debezium MySQL连接器并配置Kafka Connect。最后,创建Flink任务消费变更事件并提交任务到Flink集群。通过这些步骤,可以构建出从数据库变更到实时处理的无缝数据管道。
78 2
|
24天前
|
SQL Shell API
python Django教程 之 模型(数据库)、自定义Field、数据表更改、QuerySet API
python Django教程 之 模型(数据库)、自定义Field、数据表更改、QuerySet API
|
2月前
|
SQL 关系型数据库 数据库
关系型数据库SQLserver教程
【7月更文挑战第26天】
48 6
|
2月前
|
SQL Oracle 关系型数据库
MySQL、SQL Server和Oracle数据库安装部署教程
数据库的安装部署教程因不同的数据库管理系统(DBMS)而异,以下将以MySQL、SQL Server和Oracle为例,分别概述其安装部署的基本步骤。请注意,由于软件版本和操作系统的不同,具体步骤可能会有所变化。
139 3