Java-Spring入门指南(三十二)Android SQLite数据库实战

Java-Spring入门指南(三十二)Android SQLite数据库实战:手把手实现用户信息CRUD完整系统

  • 前言
  • 前置准备
  • 一、Android中SQLite核心概念
    • [1.1 核心组件关系](#1.1 核心组件关系)
    • [1.2 关键设计原则](#1.2 关键设计原则)
  • 二、项目结构总览
  • 三、代码实现
    • [3.1 实体类:User(数据模型)](#3.1 实体类:User(数据模型))
    • [3.2 数据库核心:UserDBHelper(SQLiteOpenHelper子类)](#3.2 数据库核心:UserDBHelper(SQLiteOpenHelper子类))
    • [3.3 界面交互:UserActivity(核心Activity)](#3.3 界面交互:UserActivity(核心Activity))
    • [3.4 界面布局:activity_user.xml](#3.4 界面布局:activity_user.xml)
    • [3.5 清单文件:AndroidManifest.xml](#3.5 清单文件:AndroidManifest.xml)
  • 四、运行测试
    • [4.1 测试步骤](#4.1 测试步骤)
    • [4.2 预期效果](#4.2 预期效果)

前言

上一篇我们深度解析了Android的Intent组件,掌握了跨组件通信的核心方式。但在实际开发中,应用往往需要本地存储数据 ------比如保存用户账号密码、缓存配置信息等,此时就需要用到Android内置的轻量级数据库SQLite

SQLite是Android原生支持的关系型数据库,无需额外安装,占用资源少、操作简单,非常适合存储本地结构化数据。本文将基于Android Studio + 模拟器环境,通过"实体类→数据库助手→界面交互"的完整流程,实现用户信息的新增、查询、修改、删除(CRUD) 功能,最终完成一个可直接运行的本地数据库系统。

我的个人主页,欢迎阅读其他文章
https://blog.csdn.net/2402_83322742?spm=1011.2415.3001.5343

我的Java-Spring入门指南专栏
欢迎指出不足
https://blog.csdn.net/2402_83322742/category_13040333.html?spm=1001.2014.3001.5482


前置准备

开始前先确保以下工具和环境已就绪:

  • 开发工具:Android Studio 2025.1.4;
  • JDK:17(Android Studio默认集成,无需额外配置);
  • 模拟器:Android 14(API 34)或其他兼容版本(确保能正常运行);
  • 核心依赖:Android原生SDK(无需额外导入第三方库,SQLite已内置)。

一、Android中SQLite核心概念

Android操作SQLite的核心是SQLiteOpenHelper类------它是Google提供的数据库辅助工具,封装了数据库的创建、升级、连接管理等底层逻辑,开发者无需直接编写复杂的SQL连接代码。

1.1 核心组件关系

组件 作用 类比后端角色
SQLiteOpenHelper 数据库创建、升级、连接管理 数据库连接池(如C3P0)+ 建表脚本
SQLiteDatabase 执行具体SQL操作(增删改查) JDBC的Statement/PreparedStatement
实体类(如User 封装数据模型(对应数据库表) 后端POJO/Entity
Activity(如UserActivity 界面交互+调用数据库操作 后端Controller(接收请求+调用Service)

1.2 关键设计原则

  • 单例模式SQLiteOpenHelper实例需全局唯一,避免多线程下数据库连接冲突;
  • 连接管理:使用时打开数据库连接(读/写),用完后及时关闭,避免资源泄漏;
  • CRUD封装 :将数据库操作(增删改查)封装在SQLiteOpenHelper子类中,Activity仅调用方法,解耦界面与数据层。

二、项目结构总览

本文项目结构

clike 复制代码
com.example.ch6_1
├── MainActivity.java       // 启动页(实际复用UserActivity布局)
├── UserActivity.java       // 核心界面:用户交互+CRUD调用
├── User.java               // 实体类:封装用户数据(id、name、pwd)
├── UserDBHelper.java       // 数据库助手:SQLite核心操作封装
├── res
│   ├── layout
│   │   └── activity_user.xml  // 界面布局:输入框+按钮+列表显示
│   └── values
│       └── strings.xml        // 字符串资源(应用名称等)
└── AndroidManifest.xml     // 清单文件:Activity注册+权限配置

三、代码实现

3.1 实体类:User(数据模型)

User类对应数据库中的user表,封装了用户的id(自增主键)、name(用户名)、pwd(密码)三个字段,提供构造方法、getter/setter和toString方法。

java 复制代码
package com.example.ch6_1;

public class User {
    private int id;         // 自增主键(数据库自动生成)
    private String name;    // 用户名(唯一标识)
    private String pwd;     // 密码

    // 无参构造(必须,SQLite查询时需反射实例化)
    public User() {}

    // 新增用户时使用(无需传入id,数据库自动生成)
    public User(String name, String pwd) {
        this.name = name;
        this.pwd = pwd;
    }

    // 查询/修改时使用(需包含id)
    public User(int id, String name, String pwd) {
        this.id = id;
        this.name = name;
        this.pwd = pwd;
    }

    // Getter 和 Setter 方法(用于读写字段值)
    public int getId() { return id; }
    public void setId(int id) { this.id = id; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public String getPwd() { return pwd; }
    public void setPwd(String pwd) { this.pwd = pwd; }

    // 重写toString:方便打印用户信息(用于列表显示)
    @Override
    public String toString() {
        return "User{id=" + id + ", name='" + name + "', pwd='" + pwd + "'}";
    }
}
  • 关键说明:无参构造方法是必需的,SQLite查询结果映射为User对象时,需通过反射调用无参构造。

3.2 数据库核心:UserDBHelper(SQLiteOpenHelper子类)

UserDBHelper是操作SQLite的核心类,封装了数据库创建、表创建、读写连接管理、CRUD方法,采用单例模式确保全局唯一实例。

java 复制代码
package com.example.ch6_1;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import androidx.annotation.Nullable;

import java.util.ArrayList;
import java.util.List;

public class UserDBHelper extends SQLiteOpenHelper {
    // 数据库名称(固定,后缀可自定义)
    private static final String DB_NAME = "java2.db";
    // 表名(用户表)
    private static final String TABLE_NAME = "user";
    // 单例实例(全局唯一)
    private static UserDBHelper mDBHelper = null;
    // 只读数据库连接(查询操作)
    private SQLiteDatabase mRDB = null;
    // 只写数据库连接(增删改操作)
    private SQLiteDatabase mWDB = null;
    // 数据库版本(升级时修改)
    private static final int VERSION = 1;

    // 构造方法(私有,仅单例模式内部调用)
    public UserDBHelper(@Nullable Context context) {
        super(context, DB_NAME, null, VERSION);
    }

    // 单例模式:获取UserDBHelper唯一实例(避免多实例冲突)
    public static UserDBHelper getInstance(Context context) {
        if (mDBHelper == null) {
            mDBHelper = new UserDBHelper(context);
        }
        return mDBHelper;
    }

    // 打开只读连接(查询操作使用)
    public SQLiteDatabase openReadLink() {
        if (mRDB == null || !mRDB.isOpen()) {
            mRDB = mDBHelper.getReadableDatabase();
        }
        return mRDB;
    }

    // 打开只写连接(增删改操作使用)
    public SQLiteDatabase openWriteLink() {
        if (mWDB == null || !mWDB.isOpen()) {
            mWDB = mDBHelper.getWritableDatabase();
        }
        return mWDB;
    }

    // 关闭数据库连接(避免资源泄漏)
    public void closeDB() {
        // 关闭读连接
        if (mRDB != null && mRDB.isOpen()) {
            mRDB.close();
            mRDB = null; // 便于GC回收
        }
        // 关闭写连接
        if (mWDB != null && mWDB.isOpen()) {
            mWDB.close();
            mWDB = null; // 便于GC回收
        }
    }

    // 数据库第一次创建时调用(仅执行一次):创建user表
    @Override
    public void onCreate(SQLiteDatabase db) {
        // 建表SQL:id自增主键,name和pwd为字符串类型
        String sql = "create table " + TABLE_NAME + "(id integer primary key autoincrement,name varchar,pwd varchar )";
        db.execSQL(sql); // 执行SQL
        Log.e("UserDBHelper类", "onCreate执行了:创建user表成功");
    }

    // 数据库版本升级时调用(VERSION修改后触发):本文暂不实现
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {}

    // -------------- CRUD核心方法 --------------
    // 1. 新增用户
    public long add(User user) {
        ContentValues values = new ContentValues(); // 封装字段值(类似Map)
        values.put("name", user.getName()); // 用户名
        values.put("pwd", user.getPwd());   // 密码
        // 插入数据:参数1=表名,参数2=空列占位符,参数3=字段值封装
        long insertResult = mWDB.insert(TABLE_NAME, null, values);
        return insertResult; // 返回值:成功返回行号(>0),失败返回-1
    }

    // 2. 查询所有用户
    public List<User> querUsers() {
        List<User> userList = new ArrayList<>();
        // 查询所有数据:参数依次为表名、查询字段、条件、条件值、分组、筛选、排序
        Cursor cursor = mRDB.query(TABLE_NAME, null, null, null, null, null, null);
        // 遍历查询结果(游标移动到下一行)
        while (cursor.moveToNext()) {
            int id = cursor.getInt(0);       // 第0列:id
            String name = cursor.getString(1); // 第1列:name
            String pwd = cursor.getString(2);  // 第2列:pwd
            userList.add(new User(id, name, pwd)); // 封装为User对象添加到列表
        }
        cursor.close(); // 关闭游标(必须,避免资源泄漏)
        return userList; // 返回所有用户列表
    }

    // 3. 根据用户名删除用户
    public long deleteByName(String name) {
        String whereClause = "name=?"; // 条件:name等于指定值(?为占位符)
        String[] whereArgs = {name};   // 条件值(替换?)
        // 删除数据:参数1=表名,参数2=条件,参数3=条件值
        long deleteResult = mWDB.delete(TABLE_NAME, whereClause, whereArgs);
        return deleteResult; // 成功返回删除行数(>0),失败返回0
    }

    // 4. 根据用户名修改密码
    public long updatePwdByName(String name, String newPwd) {
        ContentValues values = new ContentValues();
        values.put("pwd", newPwd); // 要修改的字段(密码)
        String whereClause = "name=?"; // 条件:用户名
        String[] whereArgs = {name};   // 用户名值
        // 更新数据:参数1=表名,参数2=修改字段,参数3=条件,参数4=条件值
        long updateResult = mWDB.update(TABLE_NAME, values, whereClause, whereArgs);
        return updateResult; // 成功返回更新行数(>0),失败返回0
    }

    // 5. 根据ID删除用户(扩展方法)
    public long deleteById(int id) {
        String whereClause = "id=?";
        String[] whereArgs = {String.valueOf(id)};
        long result = mWDB.delete(TABLE_NAME, whereClause, whereArgs);
        return result;
    }

    // 6. 根据ID修改用户信息(扩展方法)
    public long updateById(int id, String newName, String newPwd) {
        ContentValues values = new ContentValues();
        values.put("name", newName);
        values.put("pwd", newPwd);
        String whereClause = "id=?";
        String[] whereArgs = {String.valueOf(id)};
        long result = mWDB.update(TABLE_NAME, values, whereClause, whereArgs);
        return result;
    }
}
  • 关键说明:
    1. ContentValues:用于封装字段名和值,避免直接拼接SQL字符串(防止SQL注入);
    2. Cursor:查询结果游标,遍历后必须关闭,否则会导致内存泄漏;
    3. 读写分离:查询用getReadableDatabase(),增删改用getWritableDatabase(),提高效率。

3.3 界面交互:UserActivity(核心Activity)

UserActivity负责界面展示和用户交互,通过绑定布局控件,实现"新增、查询、修改、删除"按钮的点击事件,调用UserDBHelper的CRUD方法完成数据操作,并刷新界面显示。

java 复制代码
package com.example.ch6_1;

import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;

import java.util.List;

public class UserActivity extends AppCompatActivity implements View.OnClickListener {
    private EditText name;         // 用户名输入框
    private EditText pwd;          // 密码输入框
    private UserDBHelper userDBHelper; // 数据库助手实例
    private TextView textView;     // 用户列表显示控件

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        EdgeToEdge.enable(this); // 启用全屏(沉浸式)布局
        setContentView(R.layout.activity_user); // 绑定布局

        // 适配系统状态栏(避免控件被遮挡)
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
            Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
            return insets;
        });

        // 初始化控件(绑定布局中的ID)
        name = findViewById(R.id.name);
        pwd = findViewById(R.id.pwd);
        textView = findViewById(R.id.textView1);

        // 为四个按钮设置点击事件(当前Activity实现OnClickListener接口)
        findViewById(R.id.save).setOnClickListener(this);
        findViewById(R.id.update).setOnClickListener(this);
        findViewById(R.id.del).setOnClickListener(this);
        findViewById(R.id.query).setOnClickListener(this);
    }

    // 按钮点击事件回调(统一处理所有按钮点击)
    @Override
    public void onClick(View view) {
        String username = name.getText().toString().trim(); // 获取用户名(去空格)
        String password = pwd.getText().toString().trim();  // 获取密码(去空格)

        // 新增用户
        if (view.getId() == R.id.save) {
            if (username.isEmpty() || password.isEmpty()) { // 校验输入不为空
                Toast.makeText(this, "用户名和密码不能为空", Toast.LENGTH_SHORT).show();
                return;
            }
            // 调用新增方法:传入User对象
            long addResult = userDBHelper.add(new User(username, password));
            if (addResult > 0) {
                Toast.makeText(this, "新增成功", Toast.LENGTH_LONG).show();
                clearInput(); // 清空输入框
                refreshUserList(); // 刷新用户列表显示
            } else {
                Toast.makeText(this, "新增失败", Toast.LENGTH_LONG).show();
            }
        }
        // 查询所有用户
        else if (view.getId() == R.id.query) {
            refreshUserList(); // 直接刷新列表
        }
        // 根据用户名删除用户
        else if (view.getId() == R.id.del) {
            if (username.isEmpty()) { // 校验用户名不为空
                Toast.makeText(this, "请输入要删除的用户名", Toast.LENGTH_SHORT).show();
                return;
            }
            long deleteResult = userDBHelper.deleteByName(username);
            if (deleteResult > 0) {
                Toast.makeText(this, "删除成功", Toast.LENGTH_LONG).show();
                clearInput();
                refreshUserList();
            } else {
                Toast.makeText(this, "删除失败,用户不存在", Toast.LENGTH_LONG).show();
            }
        }
        // 根据用户名修改密码
        else if (view.getId() == R.id.update) {
            if (username.isEmpty() || password.isEmpty()) { // 校验输入不为空
                Toast.makeText(this, "用户名和新密码都不能为空", Toast.LENGTH_SHORT).show();
                return;
            }
            long updateResult = userDBHelper.updatePwdByName(username, password);
            if (updateResult > 0) {
                Toast.makeText(this, "修改成功", Toast.LENGTH_LONG).show();
                clearInput();
                refreshUserList();
            } else {
                Toast.makeText(this, "修改失败,用户不存在", Toast.LENGTH_LONG).show();
            }
        }
    }

    // 辅助方法:刷新用户列表显示(查询所有用户并展示在TextView)
    private void refreshUserList() {
        List<User> userList = userDBHelper.querUsers(); // 调用查询方法
        if (userList.isEmpty()) { // 无用户数据
            textView.setText("暂无用户数据");
        } else { // 有用户数据:拼接字符串显示
            StringBuilder sb = new StringBuilder();
            sb.append("用户列表:\n\n");
            for (User user : userList) {
                sb.append("ID: ").append(user.getId())
                  .append(", 用户名: ").append(user.getName())
                  .append(", 密码: ").append(user.getPwd())
                  .append("\n");
            }
            textView.setText(sb.toString());
        }
    }

    // 辅助方法:清空输入框
    private void clearInput() {
        name.setText("");
        pwd.setText("");
    }

    // 生命周期:Activity启动时打开数据库连接
    @Override
    protected void onStart() {
        super.onStart();
        userDBHelper = UserDBHelper.getInstance(this); // 获取单例实例
        userDBHelper.openWriteLink(); // 打开写连接(增删改)
        userDBHelper.openReadLink();  // 打开读连接(查询)
        refreshUserList(); // 启动时自动刷新列表
    }

    // 生命周期:Activity停止时关闭数据库连接(避免资源泄漏)
    @Override
    protected void onStop() {
        super.onStop();
        userDBHelper.closeDB(); // 关闭所有连接
    }
}
  • 关键说明:
    1. 生命周期管理:onStart打开数据库连接,onStop关闭,确保资源及时释放;
    2. 输入校验:避免空值导致的数据库操作失败;
    3. 界面刷新:每次数据操作后调用refreshUserList(),实时展示最新数据。

3.4 界面布局:activity_user.xml


采用垂直LinearLayout布局,包含两个输入框(用户名、密码)、四个功能按钮(新增、删除、修改、查询)和一个文本框(显示用户列表),布局简洁直观。

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/main"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_horizontal"
    android:lineSpacingExtra="8dp"
    android:padding="12dp"
    android:textColor="#333333">

    <!-- 用户名输入框 -->
    <EditText
        android:id="@+id/name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="用户名"/>

    <!-- 密码输入框 -->
    <EditText
        android:id="@+id/pwd"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="密码"/>

    <!-- 新增按钮 -->
    <Button
        android:id="@+id/save"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="新增"/>

    <!-- 删除按钮 -->
    <Button
        android:id="@+id/del"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="删除"/>

    <!-- 修改按钮 -->
    <Button
        android:id="@+id/update"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="修改"/>

    <!-- 查询按钮 -->
    <Button
        android:id="@+id/query"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="查询"/>

    <!-- 用户列表显示控件 -->
    <TextView
        android:id="@+id/textView1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="12dp"
        android:textSize="16sp"/>

</LinearLayout>
  • 布局属性说明:
    • match_parent:宽度/高度占满父容器;
    • wrap_content:宽度/高度适应内容;
    • padding:内边距,避免控件紧贴屏幕边缘;
    • lineSpacingExtra:控件之间的间距。

3.5 清单文件:AndroidManifest.xml

配置应用基本信息、Activity注册(设置UserActivity为启动页),。

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-permission android:name="android.permission.INTERNET" />
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.MyApplication">


        <activity
            android:name=".UserActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
  • 关键配置:
    • android:exported="true":允许外部启动(启动页必需);
    • intent-filter中的MAINLAUNCHER:指定UserActivity为应用入口。

四、运行测试

4.1 测试步骤

  1. 启动Android模拟器(或连接真实设备);
  2. 点击Android Studio的"运行"按钮,安装应用并启动;
  3. 功能测试:
    • 新增 :输入用户名(如zhangsan)和密码(如123456),点击"新增",提示"新增成功",再点击"查询",列表显示新增用户;
    • 查询:直接点击"查询",显示所有已添加的用户;
    • 修改 :输入已存在的用户名(如zhangsan)和新密码(如654321),点击"修改",提示"修改成功",查询后显示密码已更新;
    • 删除 :输入已存在的用户名(如zhangsan),点击"删除",提示"删除成功",查询后列表中该用户消失。

4.2 预期效果


  • 新增用户后,查询列表显示完整的用户ID、用户名、密码;
  • 修改/删除操作后,列表实时刷新,数据与数据库保持一致;
  • 输入为空时,弹出Toast提示,避免无效操作。

我的个人主页,欢迎来阅读我的其他文章
https://blog.csdn.net/2402_83322742?spm=1011.2415.3001.5343

我的Java-Spring入门指南知识文章专栏
欢迎来阅读指出不足
https://blog.csdn.net/2402_83322742/category_13040333.html?spm=1001.2014.3001.5482

|--------------------|
| 非常感谢您的阅读,喜欢的话记得三连哦 |

相关推荐
刘一说2 小时前
深入理解 Spring Boot 高级特性:条件化 Bean 注册机制
java·spring boot·后端
Han.miracle2 小时前
JavaEE ——多线程的线程安全集合类
java·java-ee
2501_941111252 小时前
自动化与脚本
jvm·数据库·python
DO your like2 小时前
Activiti工作流
java·工作流
合作小小程序员小小店2 小时前
web开发,在线%小区,物业%管理系统,基于idea,html,jsp,java,ssm,mysql数据库
java·数据库·mysql·jdk·intellij-idea
m***11902 小时前
Redis 设置密码(配置文件、docker容器、命令行3种场景)
数据库·redis·docker
豐儀麟阁贵3 小时前
6.2 Object类
java·开发语言·python
Eric_Makabaka3 小时前
微服务重要知识点
java
lkbhua莱克瓦243 小时前
Java进阶——集合进阶(MAP)
java·开发语言·笔记·github·学习方法·map