企业项目管理、ORK、研发管理与敏捷开发工具平台

网站首页 > 精选文章 正文

37. 数据库操作解析,数据存储探秘

wudianyun 2025-07-23 18:53:10 精选文章 3 ℃

哈喽 大家好!

我是老陈,在学这节课之前,如果你对数据库不了解,建议你先看《程序员必修数据库》合集。操作数据库,就像人通过笔记本(数据库)记录信息,先找到笔记本(连接数据库),再按格式写内容(创建表结构),之后随时翻看、修改、补充(增删改查)。

我们以SQLite为例,学习Java是如何操作数据库的。首先,你需要去下载sqlite-jdbc开发包并导入到工程里。

37.1 创建数据库

通过JDBC实现与SQLite的交互,核心是通过驱动程序建立连接并执行数据库操作。SQLiteFeatures类包含创建数据库文件、连接管理及异常处理等核心功能。

package com.sqlite.demo;

import java.sql.*;
import java.io.File;

/**
 * @author 今日头条:老陈说编程
 * 使用Java的JDBC API连接SQLite数据库,
 * 包含数据库文件自动创建、连接管理和基本异常处理功能。
 * 使用说明:
 * 1. 确保项目中包含SQLite JDBC驱动依赖
 * 2. 运行程序后将在当前目录生成crm.db文件
 * 3. 可通过修改dbPath变量指定其他数据库路径
 * 异常处理:
 * - 数据库连接失败时捕获SQLException并输出错误信息
 * - 处理其他可能的运行时异常并提供堆栈跟踪
 */
public class SQLiteFeatures {
    public static void main(String[] args) {
        // 定义SQLite数据库文件路径
        String dbPath = "crm.db";
        // 创建文件对象用于检查数据库文件状态
        File dbFile = new File(dbPath);

        try {
            // 检查数据库文件是否存在
            if (!dbFile.exists()) {
                System.out.println("数据库文件不存在,将自动创建:" + dbFile.getAbsolutePath());
                // SQLite会在首次连接时自动创建不存在的数据库文件
            }

            // 使用try-with-resources自动关闭数据库连接
            try (Connection conn = DriverManager.getConnection("jdbc:sqlite:" + dbPath)) {
                System.out.println("成功连接到SQLite数据库");

                // 连接成功后再次检查文件状态并输出信息
                if (dbFile.exists()) {
                    System.out.println("数据库文件大小:" + dbFile.length() + " 字节");
                }
                // 可在此处添加更多数据库操作代码

            } catch (SQLException e) {
                // 捕获SQL异常并输出错误信息和堆栈跟踪
                System.out.println("数据库连接错误:" + e.getMessage());
                e.printStackTrace();
            }
        } catch (Exception e) {
            // 捕获其他未知异常
            System.out.println("发生未知错误:" + e.getMessage());
            e.printStackTrace();
        }
    }
}

37.2 创建表并插入数据

SQLiteBasicOps类使用JDBC在 SQLite 中创建具有自增主键和自动时间戳功能的数据表,搞懂资源自动管理和 SQL 异常处理机制。

package com.sqlite.demo;

import java.sql.*;

/**
 * @author 今日头条:老陈说编程
 * 使用Java的JDBC API连接SQLite数据库
 * 创建表,并采用了try-with-resources自动关闭资源
 */
public class SQLiteBasicOps {
    public static void main(String[] args) {
        // 数据库文件路径
        String dbPath = "crm.db";

        // 使用try-with-resources自动关闭Connection连接
        try (Connection conn = DriverManager.getConnection("jdbc:sqlite:" + dbPath)) {
            System.out.println("成功连接到SQLite数据库:" + dbPath);
            // 1. 创建用户表,包含自增主键和创建时间戳
            String createTableSQL = "CREATE TABLE IF NOT EXISTS users (" +
                    "id INTEGER PRIMARY KEY AUTOINCREMENT," +
                    "username TEXT NOT NULL," +
                    "email INTEGER UNIQUE," +
                    "created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)";
            // 使用try-with-resources自动关闭Statement
            try (Statement stmt = conn.createStatement()) {
                stmt.execute(createTableSQL);
                System.out.println("用户表创建成功");
            }
        } catch (SQLException e) {
            // 捕获SQL异常并打印错误信息和堆栈跟踪
            System.out.println("数据库操作错误:" + e.getMessage());
            e.printStackTrace();
        }
    }
}

37.3 数据操作:增删改查

旁白: SQLiteCRUD类通过 JDBC 操作 SQLite 数据库的完整增删改查流程,采用面向对象设计封装数据实体,并使用预编译语句防止 SQL 注入。

package com.sqlite.demo;

import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * @author 今日头条:老陈说编程
 * SQLite数据库的完整CRUD操作
 * 包含用户对象定义和对用户表的增删改查方法
 * 使用了预编译语句防止SQL注入,并通过try-with-resources自动处理资源关闭
 */
public class SQLiteCRUD {

    /**
     * 用户实体类,封装用户信息
     */
    static class Users {
        private int id;         // 用户ID,对应数据库主键
        private String username;    // 用户姓名
        private String email;        //邮件地址

        // Getter和Setter方法
        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        public String getUsername() {
            return username;
        }

        public void setUsername(String username) {
            this.username = username;
        }

        public String getEmail() {
            return email;
        }

        public void setEmail(String email) {
            this.email = email;
        }

        // 重写toString方法,便于打印用户信息
        @Override
        public String toString() {
            return "Users{id=" + id + ", username='" + username + "', email=" + email + "}";
        }
    }

    public static void main(String[] args) {
        String dbPath = "crm.db"; // 数据库文件路径

        // 使用try-with-resources自动关闭数据库连接
        try (Connection conn = DriverManager.getConnection("jdbc:sqlite:" + dbPath)) {
            // 确保数据库连接成功
            System.out.println("成功连接到SQLite数据库");

            // 1. 查询所有用户记录
            List<Users> userList = queryAllUsers(conn);
            System.out.println("查询到" + userList.size() + "个用户:");
            userList.forEach(System.out::println);

            // 2. 插入新用户并返回自动生成的ID
            int newId = insertUser(conn, "王五", "user1@example.com");
            System.out.println("插入新用户,ID:" + newId);

            // 3. 更新指定用户的邮件
            updateUserEmail(conn, newId, "user1@laochen.com");
            System.out.println("更新用户邮箱为user666@laochen.com");

            // 4. 根据ID删除用户
            deleteUser(conn, 1); // 删除ID=1的用户
            System.out.println("删除ID=1的用户");

            // 5. 再次查询验证操作结果
            userList = queryAllUsers(conn);
            System.out.println("最终用户列表:");
            userList.forEach(System.out::println);

        } catch (SQLException e) {
            // 异常处理:打印SQL异常堆栈信息
            System.err.println("SQL操作错误: " + e.getMessage());
            e.printStackTrace();
        }
    }

    /**
     * 查询所有用户记录
     *
     * @param conn 数据库连接
     * @return 用户列表
     * @throws SQLException SQL操作异常
     */
    private static List<Users> queryAllUsers(Connection conn) throws SQLException {
        List<Users> list = new ArrayList<>();
        String sql = "SELECT id, username, email FROM Users ORDER BY id";

        // 使用try-with-resources自动关闭Statement和ResultSet
        try (Statement stmt = conn.createStatement();
             ResultSet rs = stmt.executeQuery(sql)) {

            // 遍历结果集,将每条记录转换为Users对象
            while (rs.next()) {
                Users user = new Users();
                user.setId(rs.getInt("id"));         // 获取ID字段
                user.setUsername(rs.getString("username"));   // 获取姓名字段
                user.setEmail(rs.getString("email"));       // 获取邮箱
                list.add(user);
            }
        }
        return list;
    }

    /**
     * 插入新用户记录
     *
     * @param conn  数据库连接
     * @param name  用户名
     * @param email 邮件地址
     * @return 新记录的ID,失败时返回-1
     * @throws SQLException SQL操作异常
     */
    private static int insertUser(Connection conn, String name, String email) throws SQLException {
        String sql = "INSERT INTO Users(username, email) VALUES (?, ?)";

        // 使用预编译语句并要求返回生成的键
        try (PreparedStatement pstmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
            pstmt.setString(1, name);  // 设置第一个参数
            pstmt.setString(2, email);     // 设置第二个参数
            pstmt.executeUpdate();

            // 获取自动生成的ID
            try (ResultSet rs = pstmt.getGeneratedKeys()) {
                if (rs.next()) {
                    return rs.getInt(1); // 返回自动生成的主键值
                }
            }
        }
        return -1; // 插入失败时返回-1
    }

    /**
     * 更新用户年邮箱
     *
     * @param conn  数据库连接
     * @param id    用户ID
     * @param email 新邮箱
     * @throws SQLException SQL操作异常
     */
    private static void updateUserEmail(Connection conn, int id, String email) throws SQLException {
        String sql = "UPDATE Users SET email = ? WHERE id = ?";

        // 使用预编译语句防止SQL注入
        try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
            pstmt.setString(1, email);    // 设置邮箱
            pstmt.setInt(2, id);     // 设置ID参数
            pstmt.executeUpdate();
        }
    }

    /**
     * 根据ID删除用户
     *
     * @param conn 数据库连接
     * @param id   用户ID
     * @throws SQLException SQL操作异常
     */
    private static void deleteUser(Connection conn, int id) throws SQLException {
        String sql = "DELETE FROM Users WHERE id = ?";

        // 使用预编译语句执行删除操作
        try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
            pstmt.setInt(1, id);     // 设置ID参数
            pstmt.executeUpdate();
        }
    }
}

37.4 事务处理:确保数据一致性

旁白: 事务是数据库操作的基本单元,确保一组 SQL 操作要么全部成功执行,要么在发生错误时全部回滚,维持数据的一致性。

package com.sqlite.demo;

import java.sql.*;

/**
 * @author 今日头条:老陈说编程
 * SQLite数据库事务处理
 * 尝试向Users表中顺序插入两条用户记录,
 * 由于使用固定ID(1),第二条插入会触发唯一约束冲突,
 * 从而导致整个事务回滚。
 */
public class SQLiteTransaction {
    /**
     * 程序入口点
     *
     * @param args 命令行参数(未使用)
     */
    public static void main(String[] args) {
        // 数据库文件路径
        String dbPath = "crm.db";

        // 使用try-with-resources自动关闭数据库连接
        try (Connection conn = DriverManager.getConnection("jdbc:sqlite:" + dbPath)) {
            // 关闭自动提交,开启手动事务管理
            conn.setAutoCommit(false);
            System.out.println("开始事务操作");

            try {
                // 步骤1: 插入用户A(赵六)到Users表
                insertUser(conn, "赵六", "user2@example.com");
                System.out.println("插入用户A");

                // 步骤2: 插入用户B(钱七),故意使用重复ID(1)引发约束冲突
                insertUser(conn, "钱七", "user3@example.com");
                System.out.println("插入用户B"); // 若上一步抛异常,此句不会执行

                // 所有操作成功,提交事务
                conn.commit();
                System.out.println("事务提交成功");

            } catch (SQLException e) {
                // 捕获SQL异常,回滚事务到初始状态
                conn.rollback();
                System.out.println("事务回滚:" + e.getMessage());
            } finally {
                // 恢复自动提交模式(默认行为)
                conn.setAutoCommit(true);
            }
        } catch (SQLException e) {
            // 处理数据库连接异常
            e.printStackTrace();
        }
    }

    /**
     * 向Users表插入用户记录(故意使用固定ID=1制造唯一约束冲突)
     *
     * @param conn     数据库连接
     * @param username 用户姓名
     * @param email    邮箱
     * @throws SQLException 执行SQL语句异常
     */
    private static void insertUser(Connection conn, String username, String email) throws SQLException {
        // 注意:固定ID=1会导致多次插入时违反唯一约束
        String sql = "INSERT INTO Users (id, username, email) VALUES (1, ?, ?)";

        // 使用预编译语句防止SQL注入
        try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
            pstmt.setString(1, username);  // 设置第一个占位符(姓名)
            pstmt.setString(2, email);     // 设置第二个占位符(邮箱)
            pstmt.executeUpdate();    // 执行插入操作
        }
    }
}

总结SQLite的适用场景

移动应用本地存储:Android、iOS 和鸿蒙应用的本地数据持久化;

桌面工具数据存储:轻量级桌面应用的数据存储,如笔记和待办事项软件;

嵌入式系统:物联网设备、智能家居的本地数据记录;

下期将讲解网络请求,记得点赞关注,评论区留下你遇到的SQLite 操作问题,我们一起解决!



#数据库##sql##程序员##计算机##热门##热搜##今日头条#

最近发表
标签列表