☕ 第十一章 JDBC
📄 一句话概括

四大件各掏一张牌:DriverManager 发连接、Statement 干活分两路(增删改 executeUpdate / 查询 executeQuery)、ResultSet 靠 next() 逐行爬 + getXxx("列名") 取值——把这张方法地图背熟,JDBC 代码就能看懂一大半。

DriverManager:调度员只干一件事

Connection conn = DriverManager.getConnection(url, user, password);
jdbc:mysql://localhost:3306/javaweb?useSSL=false&serverTimezone=Asia/Shanghai
└─┬─┘ └─┬─┘  └───────┬────────┘ └─┬─┘└┬─┘└──┬──┘  └────── 参数(可选)──────┘
 协议  子协议      主机 IP        端口  库名

Statement:干活分两路

Statement st = conn.createStatement();——从连接里造出来,之后按 SQL 的类型选方法:

方法 干什么 返回值
executeUpdate(sql) 增删改(INSERT / UPDATE / DELETE) int:受影响的行数
executeQuery(sql) 查询(SELECT) ResultSet:查到的结果集

记法:Update 是"改动世界",Query 是"看一眼世界"。改动有"改了几行"可数,查询只能给你一箱结果。

int rows = st.executeUpdate("INSERT INTO student VALUES(1,'波仔',18)");
System.out.println(rows > 0 ? "插入成功" : "没插进去");   // 返回值常用来判断成败

ResultSet rs = st.executeQuery("SELECT * FROM student");
⚠️ 用错路的下场

拿 executeUpdate 执行 SELECT → 直接抛 SQLException;拿 executeQuery 执行 INSERT → 同样炸。一路一词,别串门。

ResultSet:光标从表头前出发

查回来的结果箱,光标初始停在表头前面(还没进数据区),必须 next() 才能读到第一行:

while (rs.next()) {
    int id = rs.getInt("id");
    String name = rs.getString("name");
    int age = rs.getInt("age");
    System.out.println(id + " " + name + " " + age);
}
📝 和 -1 哨兵是亲戚

10.2 读文件靠 read() != -1 收工,这里靠 next() == false 收工——都是"走到头就停",只是哨兵从 int 换成了 boolean。循环套路换汤不换药。

Demo12:五步套路完整骨架(API 总装)

还没装 MySQL 也能先把骨架敲熟——类和表名当剧本读:

public class Demo12 {
    public static void main(String[] args) throws Exception {
        // ① 注册驱动(新驱动自动,写上保险)
        Class.forName("com.mysql.cj.jdbc.Driver");
        // ② 获取连接
        try (Connection conn = DriverManager.getConnection(
                "jdbc:mysql://localhost:3306/javaweb?serverTimezone=Asia/Shanghai",
                "root", "123456");
             // ③ 造传话筒
             Statement st = conn.createStatement();
             // ④ 执行 SQL,拿结果集
             ResultSet rs = st.executeQuery("SELECT * FROM student")) {
            // ⑤ 逐行取值
            while (rs.next()) {
                System.out.println(rs.getInt("id") + " "
                        + rs.getString("name") + " " + rs.getInt("age"));
            }
        }
    }
}
💡 30 秒实验一:亲眼看光标卡在表头前

写一段查询代码,故意不调 next() 直接 rs.getString("name") → SQLException: Before start of result set——异常名就是"光标还没进门"的意思。

💡 30 秒实验二:列号从 1 开始

rs.getInt(1) 取的是第一列,rs.getInt(0) → 抛列号越界异常。带过无数新手的坑,一次记住。

💡 30 秒实验三:executeUpdate 跑 SELECT

st.executeUpdate("SELECT * FROM student") → SQLException。方法名就是分工,别串门。

常见误区

  1. 「忘了 next() 直接取值」——光标还在表头前,第一行根本没读,抛 Before start of result set。看到这个异常名就想起光标没进门。
  2. 「列号从 0 开始」——JDBC 的列号从 1 开始(和数组 0 开头正好相反)。但推荐按列名取,改表结构也不怕。
  3. 「executeUpdate 只能改数据」——它管所有"改动世界"的 SQL:INSERT、UPDATE、DELETE,甚至 CREATE TABLE,返回受影响行数。
  4. 「只有 Connection 要关」——Statement 和 ResultSet 也占资源,三件都要还。try-with-resources 排进括号自动倒序关。
  5. 「url 写错是运行前能发现的」——url / 账号 / 密码全错在运行时才炸(CommunicationsException 连不上、Access denied 密码错),编译器帮不上,只能自己看异常信息对症。

自测一下

❓ Q1:Statement 的两个执行方法怎么分工?各返回什么?

executeUpdate 干增删改,返回受影响行数 int;executeQuery 干查询,返回 ResultSet 结果集。Update=改动世界,Query=看一眼世界。

❓ Q2:rs.next() 返回什么?光标初始在哪?

返回 boolean:成功挪到一行数据返回 true,越过最后一行返回 false。光标初始停在表头前面,所以第一行也必须先 next()。

❓ Q3:取值按列号还是列名好?列号从几开始?

推荐列名(改表不怕错位);列号从 1 开始不是 0。

❓ Q4:url 的三段式结构是什么?localhost 和 3306 是什么意思?

jdbc:子协议://主机:端口/库名?参数。localhost=本机,3306=MySQL 默认端口。

❓ Q5(毕业题):默写"查询 student 全表并逐行打印"的完整五步骨架(含资源释放)。

参照 Demo12:Class.forName → try 括号排 Connection/Statement/ResultSet → getConnection(url,user,pass) → createStatement + executeQuery("SELECT * FROM student") → while(rs.next()) 里 getInt/getString 取值打印。自检点:三资源全进 try 括号、循环先 next 再取值。

下一步学什么

相关笔记