JSP历险记:与数据库谈场"恋爱"——JDBC连接全攻略
JSP历险记:与数据库谈场"恋爱"——JDBC连接全攻略
NianSir's BLOG

JSP历险记:与数据库谈场"恋爱"——JDBC连接全攻略

NianSir
2025-03-18 / 0 评论 / 15 阅读

JSP学习笔记分享-数据库连接

序章:当Web程序遇上"数据焦虑"

各位小伙伴好呀~今天咱们要聊的可是JavaWeb开发里的"灵魂伴侣"——JDBC数据库连接!想象一下这个场景:你兴冲冲地写了个网页程序,用户输入了姓名年龄,结果数据只能存在内存里,服务器一重启就"失忆"了。是不是像极了金鱼脑男友?这时候就需要给咱们的Web程序找个"持久化"的另一半啦!

第一章:JDBC的前世今生

在远古的Java 1.0时代,程序员们想要操作数据库可费劲了——每个数据库厂商都有自己的驱动API,写套MySQL代码换个Oracle就得全盘重构。直到Sun公司推出JDBC(Java Database Connectivity),就像给数据库世界制定了"普通话"标准。现在不管是MySQL、Oracle还是SQLite,统统都能用同一套API搞定,是不是突然感觉代码"兼容性"buff叠满了?

📌 技术冷知识
JDBC其实是个"翻译官",它把Java代码翻译成数据库能听懂的SQL方言。不同数据库的JDBC驱动就像不同国家的翻译,虽然语言不同,但接口是统一的。

第二章:搭建"相亲"环境

工欲善其事必先利其器,咱们先来准备三件套:

  1. 数据库:推荐新手村必备MySQL(毕竟免费又好用),记得装个图形化工具如Navicat(或者JetBrains家的DataGrip甚至IDEA已经内置了,学生可以去看我之前的免费JetBrains全家桶教育免费的申请教程: 【JetBrains正版激活】IDEA正版软件学生免费获取,申请指南 ),操作起来像在数据库里"逛街"
  2. 驱动包:去MySQL官网下载最新JDBC驱动(现在叫Connector/J),就像给程序装个"翻译器": https://downloads.mysql.com/archives/c-j/
    jdbc-downloadpage
    在页面选择对应的驱动版本根据你的数据库版本而定,MySQL8.0后的数据库驱动程序请选择后缀带8.x的驱动,我这里用的是MySQL 5.7所以还是选择后缀5.x的驱动版本 😀 注意,这里下载下来的是一个独立的压缩包,解压后找到驱动对应名字的jar包,一般是有两个的,选带bin后缀的jar包复制进项目lib目录
  3. 开发环境:Eclipse/IDEA任选,推荐IDEA(智能提示简直开挂)

💡 安装小剧场
把驱动jar包放进项目的WEB-INF/lib目录时,记得要"右键→Add as Library",不然就像给程序准备了满汉全席却忘了递筷子~

第三章:JDBC连接五部曲(带弹幕解说版)

Step1:加载驱动(破冰环节)

Class.forName("com.mysql.jdbc.Driver"); 
// 新版驱动类名变了哦,别记错"身份证号",8.0以后的驱动是com.mysql.cj.jdbc.Driver
// 弹幕:驱动类加载失败?检查MySQL版本是否匹配!

这行代码就像给数据库寄了封介绍信,告诉它:"有个Java程序想认识你~"

Step2:建立连接(初次约会)

String url = "jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=UTC";
String user = "root";
String password = "123456";
Connection conn = DriverManager.getConnection(url, user, password);
// 弹幕:连接超时?检查防火墙是否放行3306端口!
// 弹幕:时区报错?在url里明确指定serverTimezone=Asia/Shanghai

URL就像约会地点,记得把mydb改成你的数据库名。时区设置是常见坑点,就像跨国恋要倒时差~

Step3:创建Statement(准备话题)

Statement stmt = conn.createStatement();
// 弹幕:Statement有两种类型:TYPE_SCROLL_INSENSITIVE(可滚动)和TYPE_FORWARD_ONLY(默认)

Statement就像聊天时的"开场白",接下来要说什么SQL语句由它传递。

Step4:执行SQL(深入交流)

// 查询示例(带分页)
ResultSet rs = stmt.executeQuery("SELECT * FROM users LIMIT 10 OFFSET 20");
while(rs.next()) {
    String name = rs.getString("name");
    int age = rs.getInt("age");
    // 弹幕:列名不存在?检查数据库字段名是否匹配!
    // 弹幕:结果集遍历要用next()方法,不能用for循环!
}

// 更新示例(带事务)
conn.setAutoCommit(false);
try {
    stmt.executeUpdate("UPDATE users SET age=26 WHERE name='小王'");
    stmt.executeUpdate("INSERT INTO logs VALUES('年龄更新')");
    conn.commit();
} catch (SQLException e) {
    conn.rollback();
}

ResultSet就像数据库返回的"情书",用next()方法逐行读取,是不是像极了拆盲盒?

Step5:关闭资源(礼貌告别)

// 传统写法(容易漏关)
rs.close();
stmt.close();
conn.close();

// 现代写法(try-with-resources,自动关闭)
try (Connection conn = DriverManager.getConnection(url, user, password);
     Statement stmt = conn.createStatement();
     ResultSet rs = stmt.executeQuery(sql)) {
    // 操作代码
} // 自动关闭资源
// 弹幕:关闭顺序要反过来:ResultSet→Statement→Connection

第四章:常见"翻车"现场(带解决方案)

  1. ClassNotFoundException

    • 🔍 检查驱动包是否在WEB-INF/lib
    • 🔍 检查驱动类名是否正确(MySQL8+用com.mysql.cj.jdbc.Driver
    • 🔍 是否执行了Class.forName()加载驱动?
  2. Access denied for user

    • 🔑 检查用户名密码是否正确
    • 🔑 登录MySQL执行SELECT user,host FROM mysql.user,确认用户有远程访问权限
    • 🔑 尝试用GRANT ALL PRIVILEGES ON mydb.* TO 'root'@'%'授权
  3. SSL连接问题

    • 🔒 在URL中添加useSSL=false
    • 🔒 或者配置MySQL的SSL证书(生产环境推荐)
  4. 中文乱码

    • 🌐 URL添加characterEncoding=UTF-8
    • 🌐 确保数据库、表、字段都设置为utf8mb4
    • 🌐 连接后执行SET NAMES utf8mb4
  5. ResultSet关闭异常

    • 💡 使用try-with-resources自动关闭
    • 💡 单独关闭时检查是否为null

第五章:进阶玩法(感情升温)

  1. PreparedStatement防SQL注入

    String sql = "SELECT * FROM users WHERE name = ? AND age > ?";
    PreparedStatement pstmt = conn.prepareStatement(sql);
    pstmt.setString(1, userInput);    // 索引从1开始!
    pstmt.setInt(2, 18);
    // 弹幕:参数化查询就像给SQL语句戴"安全套"
    // 弹幕:预编译语句还能提升性能(数据库会缓存执行计划)
  2. 连接池优化(以HikariCP为例)

    # 配置文件:src/main/resources/hikari.properties
    dataSourceClassName=com.mysql.cj.jdbc.MysqlDataSource
    dataSource.url=jdbc:mysql://localhost:3306/mydb
    dataSource.user=root
    dataSource.password=123456
    maximumPoolSize=10
    HikariConfig config = new HikariConfig("/hikari.properties");
    HikariDataSource ds = new HikariDataSource(config);
    Connection conn = ds.getConnection();
    // 弹幕:连接池就像给数据库"排班",避免频繁创建销毁连接
    // 弹幕:HikariCP是目前最快的连接池,Spring Boot默认使用
  3. 事务管理(实战版)

    conn.setAutoCommit(false);
    Savepoint svpt = conn.setSavepoint("updateStart");
    try {
     // 转账操作
     stmt.executeUpdate("UPDATE accounts SET balance=balance-100 WHERE id=1");
     stmt.executeUpdate("UPDATE accounts SET balance=balance+100 WHERE id=2");
     conn.commit();
    } catch (SQLException e) {
     conn.rollback(svpt); // 回滚到保存点
     // 处理异常...
    }
    // 弹幕:保存点就像游戏存档,可以部分回滚
    // 弹幕:默认隔离级别是REPEATABLE_READ,可能需要根据业务调整

第六章:实战演练(约会实战)

任务:做个简单用户管理系统,包含增删改查功能

步骤

  1. 建表SQL

    CREATE TABLE users (
     id INT PRIMARY KEY AUTO_INCREMENT,
     name VARCHAR(50) NOT NULL,
     age INT,
     create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
  2. JSP页面(list.jsp)

    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    <table>
     <tr><th>ID</th><th>姓名</th><th>年龄</th><th>操作</th></tr>
     <c:forEach items="${userList}" var="user">
         <tr>
             <td>${user.id}</td>
             <td>${user.name}</td>
             <td>${user.age}</td>
             <td>
                 <a href="edit.jsp?id=${user.id}">编辑</a>
                 <a href="delete?id=${user.id}">删除</a>
             </td>
         </tr>
     </c:forEach>
    </table>
    <a href="add.jsp">新增用户</a>
  3. Servlet代码(UserServlet.java)

    @WebServlet("/user")
    public class UserServlet extends HttpServlet {
     private static final String URL = "jdbc:mysql://localhost:3306/mydb?useSSL=false";
     private static final String USER = "root";
     private static final String PASS = "123456";
    
     protected void doGet(HttpServletRequest request, HttpServletResponse response) 
         throws ServletException, IOException {
         
         String action = request.getParameter("action");
         Connection conn = null;
         try {
             conn = DriverManager.getConnection(URL, USER, PASS);
             
             if ("list".equals(action)) {
                 listUsers(conn, request, response);
             } else if ("delete".equals(action)) {
                 deleteUser(conn, request, response);
             }
             
         } catch (SQLException e) {
             throw new ServletException("数据库错误", e);
         } finally {
             if (conn != null) try { conn.close(); } catch (SQLException ignore) {}
         }
     }
    
     private void listUsers(Connection conn, HttpServletRequest request, HttpServletResponse response) 
         throws SQLException, ServletException, IOException {
         
         Statement stmt = conn.createStatement();
         ResultSet rs = stmt.executeQuery("SELECT * FROM users");
         List<User> userList = new ArrayList<>();
         while (rs.next()) {
             userList.add(new User(
                 rs.getInt("id"),
                 rs.getString("name"),
                 rs.getInt("age")
             ));
         }
         request.setAttribute("userList", userList);
         request.getRequestDispatcher("list.jsp").forward(request, response);
     }
    
     // 其他方法(add/edit/delete)类似...
    }

🛠️ 调试技巧

  1. 在catch块里打印完整异常堆栈:e.printStackTrace()
  2. 使用IDEA的Database工具直接查看数据库变化
  3. 在SQL语句前加SELECT NOW()测试连接是否成功
  4. 用Postman测试RESTful接口

🎯 性能优化建议

  1. 查询时使用SELECT id,name FROM users代替SELECT *
  2. 对频繁查询的字段(如name)加索引
  3. 使用连接池管理数据库连接
  4. 考虑使用缓存(如Redis)存储热点数据

结语:与数据库的"长久之计"

通过JDBC连接数据库就像谈场细水长流的恋爱:开始时要小心翼翼建立连接,过程中要妥善处理各种异常,最后还要优雅地关闭资源。随着技术深入,你会遇到ORM框架(如MyBatis、Hibernate),就像给这场恋爱找个"红娘",让数据操作变得更优雅高效。但无论如何,理解JDBC原理都是打好JavaWeb基础的关键一课。

快去动手实践吧!遇到问题记得回来看看这篇笔记,咱们下期聊聊Servlet~ 🚀

3

评论 (0)

取消