Servlet 数据库访问(手把手讲解)

更新时间:

💡一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

  • 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...点击查看项目介绍 ;
  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;

截止目前, 星球 内专栏累计输出 82w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 2900+ 小伙伴加入学习 ,欢迎点击围观

在 Web 应用开发中,Servlet 数据库访问是连接前端交互与后端数据存储的核心环节。无论是用户注册、订单查询还是实时数据统计,都需要通过 Servlet 与数据库高效通信。对于初学者而言,理解这一流程如同掌握“桥梁建造的技巧”——既要熟悉建筑材料(如 JDBC、连接池),又要确保结构稳固(如事务、安全防护)。本文将从基础概念讲起,逐步拆解实现方法,并通过完整案例演示具体操作,帮助开发者构建稳健的数据交互能力。


一、Servlet 数据库访问的底层逻辑

1.1 Servlet 与数据库的角色分工

Servlet 是 Java Web 开发中的“信使”,负责接收 HTTP 请求并处理业务逻辑;而数据库则是“数据仓库”,存储用户信息、商品库存等关键数据。两者通过 JDBC(Java Database Connectivity)这一“翻译官”实现通信。

1.2 核心流程图解

以用户登录为例,流程可简化为以下步骤:

  1. 用户提交用户名和密码 → Servlet 收到请求
  2. Servlet 通过 JDBC 连接数据库
  3. 执行 SQL 查询验证用户凭证
  4. 返回结果 → Servlet 构建响应返回给前端

这一过程类似于快递员(Servlet)将包裹(请求参数)送达仓库(数据库),仓库返回签收状态(查询结果)的过程。


二、环境搭建与基础配置

2.1 必备工具与依赖

2.1.1 数据库驱动

以 MySQL 为例,需在项目中添加 mysql-connector-java 依赖。Maven 配置如下:

<dependency>  
    <groupId>mysql</groupId>  
    <artifactId>mysql-connector-java</artifactId>  
    <version>8.0.26</version>  
</dependency>  

2.1.2 连接参数配置

通常将数据库 URL、用户名、密码等敏感信息存入 application.properties 文件,避免硬编码:

db.url=jdbc:mysql://localhost:3306/mydb?useSSL=false  
db.username=root  
db.password=123456  

三、实现步骤详解

3.1 获取数据库连接

3.1.1 简单连接方式

通过 DriverManager 动态加载驱动并建立连接:

// 读取配置文件  
String url = "jdbc:mysql://localhost:3306/mydb";  
String user = "root";  
String password = "123456";  

// 获取连接  
Connection conn = DriverManager.getConnection(url, user, password);  

比喻:这就像拨打特定电话号码(URL)联系客服(数据库),并提供身份验证(用户名密码)。

3.1.2 连接池优化

频繁创建连接会导致性能下降。使用 HikariCP 连接池可复用连接,如同“共享快递车”提升效率:

HikariConfig config = new HikariConfig();  
config.setJdbcUrl(url);  
config.setUsername(user);  
config.setPassword(password);  
HikariDataSource dataSource = new HikariDataSource(config);  

// 后续通过 dataSource.getConnection() 获取连接  

3.2 执行 SQL 语句

3.2.1 查询操作示例

String sql = "SELECT * FROM users WHERE username = ?";  
PreparedStatement pstmt = conn.prepareStatement(sql);  
pstmt.setString(1, username); // 参数化查询防 SQL 注入  

ResultSet rs = pstmt.executeQuery();  
if (rs.next()) {  
    String storedPassword = rs.getString("password");  
    // 验证密码逻辑  
}  

安全提示? 占位符如同“过滤网”,防止恶意输入(如 ' OR '1'='1)破坏查询逻辑。

3.2.2 更新操作示例(如注册用户)

String insertSql = "INSERT INTO users (username, password) VALUES (?, ?)";  
PreparedStatement insertStmt = conn.prepareStatement(insertSql);  
insertStmt.setString(1, username);  
insertStmt.setString(2, hashedPassword);  
int affectedRows = insertStmt.executeUpdate(); // 返回影响行数  

3.3 结果集处理与资源释放

3.3.1 封装数据对象

将查询结果映射为 Java 对象,提升可读性:

public class User {  
    private String username;  
    private String email;  
    // 省略构造方法与Getter/Setter  
}  

// 在查询后构建对象  
User user = new User(rs.getString("username"), rs.getString("email"));  

3.3.2 资源管理最佳实践

使用 try-with-resources 自动关闭资源,避免内存泄漏:

try (Connection conn = dataSource.getConnection();  
     PreparedStatement pstmt = conn.prepareStatement(sql);  
     ResultSet rs = pstmt.executeQuery()) {  
    // 业务逻辑  
} catch (SQLException e) {  
    e.printStackTrace();  
}  

四、事务管理与异常处理

4.1 事务的原子性保障

事务如同“银行转账操作”——若从 A 账户转出 100 元,必须同时成功转入 B 账户,否则全部回滚。

conn.setAutoCommit(false); // 关闭自动提交  
try {  
    // 执行多个 SQL 操作  
    updateBalance("A", -100);  
    updateBalance("B", +100);  
    conn.commit(); // 提交事务  
} catch (Exception e) {  
    conn.rollback(); // 回滚操作  
    throw e;  
} finally {  
    conn.setAutoCommit(true); // 恢复默认状态  
}  

4.2 异常捕获与日志记录

catch (SQLException e) {  
    logger.error("数据库操作失败: {}", e.getMessage(), e);  
    throw new RuntimeException("服务器繁忙,请稍后再试");  
}  

五、性能优化与安全加固

5.1 连接池与预编译语句

  • 连接池:如 HikariCP 可复用连接,避免每次创建新连接的开销。
  • 预编译语句PreparedStatement):将 SQL 语句预编译为数据库内的存储对象,后续执行更快。

5.2 SQL 注入防护

永远避免拼接 SQL 字符串:

// 错误写法(易受攻击)  
String dangerSql = "SELECT * FROM users WHERE username = '" + username + "'";  

// 正确写法(参数化查询)  
String safeSql = "SELECT * FROM users WHERE username = ?";  
PreparedStatement pstmt = conn.prepareStatement(safeSql);  
pstmt.setString(1, username);  

六、实战案例:用户登录功能

6.1 Servlet 代码实现

@WebServlet("/login")  
public class LoginServlet extends HttpServlet {  
    private static final long serialVersionUID = 1L;  

    protected void doPost(HttpServletRequest request, HttpServletResponse response)  
            throws ServletException, IOException {  
        String username = request.getParameter("username");  
        String password = request.getParameter("password");  

        try (Connection conn = DBUtil.getConnection();  
             PreparedStatement pstmt = conn.prepareStatement(  
                     "SELECT password FROM users WHERE username = ?")) {  

            pstmt.setString(1, username);  
            ResultSet rs = pstmt.executeQuery();  

            if (rs.next()) {  
                String storedHash = rs.getString("password");  
                if (BCrypt.checkpw(password, storedHash)) {  
                    request.getSession().setAttribute("user", username);  
                    response.sendRedirect("/success");  
                } else {  
                    request.setAttribute("error", "密码错误");  
                    request.getRequestDispatcher("/login.jsp").forward(request, response);  
                }  
            } else {  
                request.setAttribute("error", "用户不存在");  
                request.getRequestDispatcher("/login.jsp").forward(request, response);  
            }  
        } catch (SQLException e) {  
            e.printStackTrace();  
            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);  
        }  
    }  
}  

6.2 关键点解析

  • 密码加密:使用 BCrypt 加密存储密码,而非明文。
  • 会话管理:登录成功后将用户信息存入 Session,实现身份保持。
  • 错误处理:区分用户不存在、密码错误等场景,避免信息泄露。

结论

Servlet 数据库访问是 Web 开发的基石,其核心在于“安全、高效、可维护”的代码设计。本文通过分步讲解 JDBC 原理、连接池优化、事务管理及实战案例,帮助开发者构建从基础到进阶的完整认知体系。建议读者通过以下步骤实践:

  1. 完成一个简单的 CRUD(增删改查)项目
  2. 对比分析不同数据库连接方式的性能差异
  3. 尝试使用 MyBatis 等 ORM 框架简化代码

掌握这些技能后,您将能够更从容地应对 Web 开发中复杂的业务场景,为构建高并发、高可靠的系统打下坚实基础。

最新发布