第10次 · JdbcTemplate完成数据访问
第10次 · JdbcTemplate完成数据访问
第1/7页 · 先知道今天要做什么
今天只做一件看得见的事:用 Java 操作自己的练习账户。
今天的场景
你已经会用 SQL 查表。现在有了一个 Java 程序:点一次运行,就能查出账户名单;再运行另一段程序,把你的学号和余额存到数据库。最后把自己的余额从30元改成45元。
下课时拿出这三样结果
| 做到什么 | 在哪里看 | 完成的证据 |
|---|---|---|
| 查到名单 | IDEA 的 Run 控制台 | 账户名单里能看到“小林”以及后来新增的自己。 |
| 新增自己 | AddAccount 控制台 + MySQL 查询 | 新增行数为1;记下实际打印的账户编号;表里有自己的学号、30.00。 |
| 修改余额 | UpdateBalance 控制台 + MySQL 查询 | 修改行数为1;同一个账户变成45.00;小林仍是100.00。 |
你只需要带着这些基础来
会写 main、变量、方法调用和 if;能看懂 SELECT、INSERT、UPDATE。List 就是一组字符串,例如 [小林, 2510101]。如果还不熟,先照着查询例子运行,再看第2页解释。
两课时怎么上(按每课时45分钟)
| 时间 | 老师带着做 | 学生留下什么 |
|---|---|---|
| 第一课时 0—8分钟 | 运行一次查询,展示 Java 输出与数据库同一份数据。 | 知道程序在哪里运行,结果在哪里看。 |
| 8—18分钟 | 解释连接工具、SQL、问号和返回值,只看查询例子的核心三行。 | 能指出 SQL 字符串和 names 列表。 |
| 18—35分钟 | 边演示边让学生新增自己的账户。 | 一个真实账户编号。 |
| 35—45分钟 | 巡视并一起查表,解决卡住的步骤。 | 新增成功、数据库核对通过。 |
| 第二课时 0—15分钟 | 用真实编号演示改余额,先预测再运行。 | 理解“影响1行”和“影响0行”。 |
| 15—33分钟 | 学生独立完成第5页的30→45练习。 | 三项检查记录。 |
| 33—45分钟 | 抽查数据库结果,让学生说明问号和值的对应关系。 | 提交三份 Java 文件和练习记录。 |
从哪里开始
老师已经配好机房环境:按第3页看例子,再跟第4页操作。自己学习或电脑第一次运行:先完成第4页“准备好工程”与“建库连接”,然后回第3页。安装 JDK/MySQL、首次下载依赖放在课前;90分钟用于读懂和练习。
今天先掌握到这里
先会调用 JdbcTemplate 查询、新增和修改。分层、接口、对象映射、事务留到后续课;这课用普通 main,所有关键文件都在课堂包中。
来源:本课自编课堂练习;API依据 Spring Framework 6.1 JdbcTemplate;教材第4章作为课后选读。
第2/7页 · 把新词说清楚
把新名字对应到你已经会的 Java 和 SQL。
四个新名字就够了
| 名字 | 用熟悉的话理解 | 本课里长什么样 |
|---|---|---|
| 数据源 dataSource | 保存“去哪台数据库、用哪个账号连接”的对象。 | Db.java 读取 db.properties 中的地址、账号和密码。 |
| JdbcTemplate | 一个现成的 Java 工具对象,负责把 SQL 和参数交给数据库,并把结果带回来。 | JdbcTemplate jdbc = Db.connect(); |
| 参数占位符 ? | SQL 中留出来的“值的位置”。后面的参数按顺序填进去。 | VALUES (?, ?) 后依次传学号、余额。 |
| 影响行数 rows | 这次新增或修改影响了多少条记录,是一个整数。 | int rows = jdbc.update(…); 成功新增一条,通常是1。 |
把名字分清
JdbcTemplate 是类名;jdbc 是我们给这个对象起的变量名;sql 是保存 SQL 文字的 String 变量;account 是数据库里的表名。变量名不会自动变成数据库里的列。
本课只用三个方法
| 方法 | 什么时候用 | 结果是什么 |
|---|---|---|
| queryForList(sql, String.class) | 查询 username 这一列的多行文字。 | List,例如 [小林, 2510101];空表是 []。 |
| update(sql, 参数…) | 执行 INSERT 或 UPDATE。名字虽然叫 update,也可以新增。 | int,表示影响行数。 |
| queryForObject(sql, 类型.class, 参数…) | 查询明确只应该有一个结果的编号或余额。 | 一个 Integer 编号或 Double 金额;本课只在确认账户存在后查询。 |
String.class 是什么
这里先把它读成“把查到的这一列按字符串取回来”。它不是数据库的列名,也不是要打印的内容。查编号时写 Integer.class,查本例的金额时写 Double.class。
只看这两行:两个问号怎样收值
本课课堂练习工程
String sql = "INSERT INTO account(username, balance) VALUES (?, ?)";
int rows = jdbc.update(sql, "2510101", 30.0);
按位置一一对应
| SQL中的位置 | Java传入的值 | 写到哪里 |
|---|---|---|
| 第一个 ? | “2510101” | username 列 |
| 第二个 ? | 30.0 | balance 列 |
先口头预测
如果第二个参数改成50.0,数据库哪一列会变化?答案是 balance。sql 中的问号不加引号,值通过 update 的参数传入;本课不把学号拼进 SQL 字符串。
来源:本课自编课堂练习;API依据 Spring Framework 6.1 JdbcTemplate;教材第4章作为课后选读。
第3/7页 · 先看一个小例子
先只查名单。下面是完整文件,不是从大工程截下来的半段代码。
先看数据库里的一行
执行课堂包 schema.sql 后,account 表至少有示例账户“小林”,余额100.00。id 是数据库自动分配的,实际值以自己的查询为准。查询程序只读数据,不会新增账户。
在 MySQL 工具里先执行一次
本课课堂练习工程
SELECT username FROM account ORDER BY id;
预期看见什么
第一次建好练习库时,查询结果有一行“小林”。如果你已经做过新增练习,还会有自己的学号,这是正常的。
QueryAccounts.java:完整文件
src/main/java/campus/QueryAccounts.java
package campus;
import java.util.List;
import org.springframework.jdbc.core.JdbcTemplate;
public class QueryAccounts {
public static void main(String[] args) throws Exception {
JdbcTemplate jdbc = Db.connect();
String sql = "SELECT username FROM account ORDER BY id";
List<String> names = jdbc.queryForList(sql, String.class);
System.out.println("账户名单:" + names);
}
}
在 IDEA 怎么运行
完成第4页的工程与数据库准备后,打开 src/main/java/campus/QueryAccounts.java,点击 main 左侧绿色三角,选择 Run QueryAccounts.main()。在底部 Run 控制台找“账户名单:”这一行。
第一次运行的预期输出
本课初始数据库的预期输出,不是替你运行过的结果
账户名单:[小林]
把核心代码一行一行读懂
| 这一行 | 它做了什么 | 你检查什么 |
|---|---|---|
| JdbcTemplate jdbc = Db.connect(); | 调用包中已经写好的连接工具,得到一个可用的 JdbcTemplate 对象。 | Db.java 和 db.properties 都在包中;你要填的是数据库连接。 |
| String sql = “SELECT username FROM account ORDER BY id”; | 准备查询语句。此时只是保存一段文字,还没查询。 | 和上面在 MySQL 工具里执行的 SQL 相同。 |
| List names = jdbc.queryForList(sql, String.class); | 把 SQL 交给数据库,取回 username 这一列的所有值。 | names 保存一组名字,不是表本身。 |
| System.out.println(“账户名单:” + names); | 把取回来的名单打印到控制台。 | 看到小林;新增之后再运行会看到新学号。 |
在纸上走一遍
main → Db.connect() 读取连接设置 → queryForList 把 SELECT 交给 MySQL → MySQL 返回名字 → println 打印。数据库中保存数据,Java 程序这次只是把数据读出来。
先做一个不会改数据的小练习
只把输出前缀“账户名单:”改成“软件251的账户名单:”,保存并重新运行 QueryAccounts。前缀变了,数据库中的“小林”没有变。先把这个结果做出来,再学新增。
来源:本课自编课堂练习;API依据 Spring Framework 6.1 JdbcTemplate;教材第4章作为课后选读。
第4/7页 · 一步一步做出来
跟老师完成“查一次 → 加一条 → 改同一条”,每一步都有检查点。
准备好工程:先把文件放对
- 点讲义顶部“下载课堂练习”,或到“课程资料”下载“第10课 · JdbcTemplate课堂练习包”,取得 lesson-10-jdbc-practice.zip。解压为普通文件夹,进入直接有 pom.xml、schema.sql 和 src 的 lesson-10-jdbc-practice 文件夹。
- IDEA:File → Open,选择这个 pom.xml,选 Open as Project / 新窗口打开。看到 src/main/java/campus 下的 QueryAccounts、AddAccount、UpdateBalance、Db 四个类。
- 需要 JDK17 时点“下载jdk17”,将 Windows x64 ZIP 全部提取到固定位置,例如 D:\Java\jdk-17.0.20.1+1。选择直接含 bin、lib、conf 的目录,bin 里应有 java.exe 和 javac.exe。
- File → Project Structure → Project:SDK 选17;没有就 Add SDK → JDK / Add JDK from disk,选刚解压的根目录。Language level 选17;Modules → Dependencies → Module SDK 选 Project SDK。
- File → Settings → Build, Execution, Deployment → Build Tools → Maven:Importing 中 JDK for importer 选17,Runner 中 JRE 选17。菜单名称因 IDEA 版本略有差异,可搜索 importer 和 Runner。
- 打开 Maven 面板,Reload All Maven Projects,等依赖下载完成。pom.xml 已指定 Java17 和所需库。main 的运行配置若单独指定 JRE,也选17。以 Run 的 java.exe 路径为准。
建库连接:先确认 MySQL 能查到小林
- 先启动自己的 MySQL。打开机房使用的 Navicat / MySQL Workbench,连接自己的本机或教师分配的数据库实例;本机通常为127.0.0.1:3306,账号和密码用实际 MySQL 凭据。
- 在 SQL 查询窗口执行下面完整脚本,也可以打开课堂包根目录的 schema.sql 执行。脚本不会删表,不会覆盖已有账户。若使用教师分配的独立库名,同时修改建库/USE 和 db.properties 中的库名。
- 查看最后 SELECT 的结果,至少有“小林,100.00”。若已有同名 account 表但列或唯一约束不同,先让教师检查;IF NOT EXISTS 只是不重复建表,不会修改旧结构。
- 在 IDEA 打开 src/main/resources/db.properties,把 YOUR_MYSQL_USER 和 YOUR_MYSQL_PASSWORD 换成刚才连接成功的账号密码;地址、端口、库名也要一致。Ctrl+S 保存。
- 运行 QueryAccounts.main()。先成功看见账户名单,再往下做新增。连接失败先对照第6页,不要一边改账号一边改 SQL。
schema.sql:本课独立练习库
课堂包/schema.sql
-- Run in your own MySQL practice environment. No existing rows are deleted.
CREATE DATABASE IF NOT EXISTS jdbc_lesson10 DEFAULT CHARACTER SET utf8mb4;
USE jdbc_lesson10;
CREATE TABLE IF NOT EXISTS account (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
balance DECIMAL(10, 2) NOT NULL
);
-- Add the sample only if it is missing; rerunning keeps students' changes.
INSERT INTO account (username, balance)
SELECT '小林', 100.00
WHERE NOT EXISTS (SELECT 1 FROM account WHERE username = '小林');
SELECT id, username, balance FROM account ORDER BY id;
db.properties:只改连接值,键名保持原样
src/main/resources/db.properties
# Fill in the credentials of YOUR local or teacher-assigned MySQL database.
db.driver=com.mysql.cj.jdbc.Driver
db.url=jdbc:mysql://127.0.0.1:3306/jdbc_lesson10?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
db.username=YOUR_MYSQL_USER
db.password=YOUR_MYSQL_PASSWORD
老师演示1:先让学生猜,再新增
打开 AddAccount.java。先指给学生看 teacher_demo 和20.0,问“运行后会多哪两个值”。只运行一次 AddAccount.main();在控制台找“新增行数:1”和“我的账户编号:实际数字”。把实际编号记到黑板上,不预先写成1或2。
AddAccount.java:完整文件
src/main/java/campus/AddAccount.java
package campus;
import org.springframework.jdbc.core.JdbcTemplate;
public class AddAccount {
public static void main(String[] args) throws Exception {
JdbcTemplate jdbc = Db.connect();
String myName = "teacher_demo"; // 学生练习:改成自己的学号,保留双引号
double startingBalance = 20.0;
String sql = "INSERT INTO account(username, balance) VALUES (?, ?)";
int rows = jdbc.update(sql, myName, startingBalance);
System.out.println("新增行数:" + rows);
Integer myId = jdbc.queryForObject(
"SELECT id FROM account WHERE username = ?", Integer.class, myName);
System.out.println("我的账户编号:" + myId);
}
}
学生跟做1:看见新增真的落到了表里
- 先观察老师运行前后的表。INSERT 没写 id,因为 id 由数据库自动生成。
- 自己跟做时先运行一次本地 AddAccount(保持 teacher_demo、20.0),记下编号;再运行 QueryAccounts,名单里应多一个 teacher_demo。若已经跟做过,不重复新增。
- 在 MySQL 工具执行 SELECT id, username, balance FROM account ORDER BY id;,找到 teacher_demo 的编号和20.00。新增行数、Java 名单、数据库这一行对得上,才继续。
- 同一个 username 在本课表中不能重复。不要连续点击 AddAccount;出现 Duplicate entry 就先查已有记录,不要删库重来。
老师演示2:只改刚才那一条
打开 UpdateBalance.java,把 myId 的0换成刚才实际打印的编号,myName 保持 teacher_demo,newBalance 设35.0。先问学生“哪一行会变、变成多少”,然后运行 UpdateBalance.main()。预期打印“修改行数:1”“修改后的余额:35.0”。
UpdateBalance.java:完整文件
src/main/java/campus/UpdateBalance.java
package campus;
import org.springframework.jdbc.core.JdbcTemplate;
public class UpdateBalance {
public static void main(String[] args) throws Exception {
JdbcTemplate jdbc = Db.connect();
int myId = 0; // 改为 AddAccount 实际打印的账户编号,不猜编号
String myName = "teacher_demo"; // 与自己 AddAccount 中的名字完全一致
double newBalance = 35.0;
String sql = "UPDATE account SET balance = ? WHERE id = ? AND username = ?";
int rows = jdbc.update(sql, newBalance, myId, myName);
System.out.println("修改行数:" + rows);
if (rows == 1) {
Double balance = jdbc.queryForObject(
"SELECT balance FROM account WHERE id = ?", Double.class, myId);
System.out.println("修改后的余额:" + balance);
} else {
System.out.println("没有修改到账户,请检查编号和学号是否属于同一行。");
}
}
}
修改语句的三个问号
| 位置 | 传入变量 | 意思 |
|---|---|---|
| 第1个 ? | newBalance | 要改成的余额35.0 |
| 第2个 ? | myId | 只找这个编号 |
| 第3个 ? | myName | 同时确认这行属于这个名字 |
最后再查表
重新执行 SELECT id, username, balance FROM account ORDER BY id;。teacher_demo 应为35.00,小林应仍为100.00。控制台35.0与数据库35.00表示同一数值。rows 为0时先核对编号和名字,不要删掉 WHERE 来让它“成功”。
Db.java 到底做了什么
它读取 db.properties,创建 DriverManagerDataSource 保存连接设置,再 new JdbcTemplate(dataSource)。完整文件已放在包中,本课先会填连接值和调用它;老师可打开这两行演示工具对象怎样创建,不要求学生当堂重写配置工具。
来源:本课自编课堂练习;API依据 Spring Framework 6.1 JdbcTemplate;教材第4章作为课后选读。
第5/7页 · 换成自己的代码
现在关掉老师的示范结果,用自己的学号独立做一遍。
开始前:在 IDEA 确认 JDK17
- 需要安装时点顶部“下载jdk17”,解压到固定位置,例如 D:\Java\jdk-17.0.20.1+1;选择直接含 bin、lib、conf 的根目录。
- File → Project Structure → Project:SDK 选17;没有就 Add SDK → JDK 选择解压目录;Language level 选17,Modules → Dependencies → Module SDK 选 Project SDK。
- File → Settings → Build Tools → Maven(也可搜索 Maven):Importing 的 JDK for importer、Runner 的 JRE 均选17。Reload Maven;main 运行配置若另选 JRE,也改为17。
- 先运行 QueryAccounts。能看到实际账户名单,再开始下面的个人练习;数据库尚未准备好时按第4页执行 schema.sql 并填写 db.properties。
你的任务
创建 username 为自己学号、初始余额30.00的账户;记下它的真实编号;把同一账户余额改为45.00。最后证明小林和老师的账户没有被你改动。
按这张操作单完成
| 步骤 | 打开哪个文件、改哪里 | 运行后应该看到什么 |
|---|---|---|
| 1 查询基线 | QueryAccounts.java:运行 main,不改 SQL。 | 看见现有名单;记录当前是否已有自己的学号。 |
| 2 新增自己的账户 | AddAccount.java:myName 改为自己的学号(保留双引号);startingBalance 改为30.0。只运行一次。 | 新增行数1;记录自己的真实 id。 |
| 3 重新查名单 | QueryAccounts.java:再运行 main。 | 名单里有自己的学号。 |
| 4 修改同一个账户 | UpdateBalance.java:myId 换自己的真实 id;myName 填同一个学号;newBalance 改成45.0。 | 修改行数1;修改后的余额45.0。 |
| 5 到数据库核对 | MySQL 查询窗口执行下面的检查 SQL。 | 自己的行是45.00;小林仍是100.00。 |
检查结果:把学号换成自己的
本课课堂练习工程
SELECT id, username, balance
FROM account
WHERE username IN ('小林', '2510101')
ORDER BY id;
自己的学号已经存在怎么办
先 SELECT 查出自己的 id 和余额。如果是刚才已新增成功,直接从步骤3继续,记录原余额;不要再次 INSERT。课堂第一次完整练习必须有一次新增记录;需要重新完整演练时,在自己的学号后加 _2 作为本轮标记,并在后续文件和记录里保持一致。
做完基础任务后,再验证一次“没有改到”
- 先记录自己的正确 id 与45.00余额。把 UpdateBalance 的 myId 临时改为 -1,其他内容不变,运行。
- 应显示修改行数0,并提示检查编号和学号。再次查自己的余额,应仍为45.00。这说明没有匹配到记录,不是程序自动改好了。
- 把 myId 恢复正确编号,保存。把这一次对照结果写进练习记录。
最低完成标准:先把这三项做实
| 检查 | 通过条件 |
|---|---|
| 查 | QueryAccounts 能显示自己的学号。 |
| 加 | 记录了新增行数1和真实编号。 |
| 改 | 自己的余额确实变为45.00,其他账户未变。 |
最后交什么
将自己实际运行的 QueryAccounts.java、AddAccount.java、UpdateBalance.java 和练习记录.md 放到代码任务中提交。记录写真实编号、运行输出和数据库查询结果。db.properties 含你的数据库密码,不提交它。
网站已有旧草稿的同学
先保留旧草稿文件,再按课堂包中的真实文件名添加这三份 Java 文件与记录。网站不会替你运行 Java;保存草稿后再正式提交,查看提交版本是否增加。
卡住也能清楚求助
写清卡在操作单第几步、运行的类名、改过的值、第一条错误文字。没有运行成功就写未完成,保留你已经查到的线索;先解决这个步骤,再做下一步。
来源:本课自编课堂练习;API依据 Spring Framework 6.1 JdbcTemplate;教材第4章作为课后选读。
第6/7页 · 出错时从哪里查
先看发生在哪一步,再只改一处。
把现象和检查动作对上
| 看到什么 | 先检查哪里 | 修好后怎样确认 |
|---|---|---|
| IDEA 的 JdbcTemplate 标红 | Maven 是否 Reload 完成;pom.xml 是否作为项目打开;依赖下载是否报错。 | import 不再报红,QueryAccounts 能编译。 |
| invalid target release: 17 / UnsupportedClassVersionError | 项目 SDK、Maven Runner、main 运行配置是否使用 JDK17。 | 重新运行,Run 启动路径指向你的 JDK17。 |
| Access denied for user | db.properties 的账号、密码及该账号的库权限。 | 先用同样凭据在 MySQL 工具连接,再运行 QueryAccounts。 |
| Communications link failure / Connection refused | MySQL 服务是否启动;地址和端口是否正确。 | 同一地址的 MySQL 工具能够连接。 |
| Unknown database / Table … doesn’t exist | schema.sql 是否执行成功;db.url 库名与 USE 的库是否相同。 | 在该库查 account 能看到小林。 |
| Duplicate entry | 是否第二次运行了相同学号的 AddAccount。 | 查出现有账户编号;保留这行,继续查询和修改。 |
| 修改行数:0 | myId 和 myName 是否来自同一条真实记录,是否忘了把默认0换掉。 | 条件对上后影响1行,并查到新余额。 |
| 输出还是旧文字或旧金额 | 是否保存文件;运行的是否是刚改的那个 main。 | 看 Run 标签中的类名,重新运行并到数据库查同一 id。 |
查自己的编号与当前余额
本课课堂练习工程
SELECT id, username, balance
FROM account
WHERE username = '2510101';
不要用这些办法“修复”
修改行数0时,不删 WHERE;新增重复时,不清空全表;找不到库时,不修改正在使用的教学网站数据库。始终在自己的练习库里核对一条账户。
老师巡视时只问三句话
你运行的是哪个 main?你期待哪一行变成什么?现在数据库里实际查到什么?先让学生指出文件和记录,再一起看报错,不直接替学生改完全部文件。
来源:本课自编课堂练习;API依据 Spring Framework 6.1 JdbcTemplate;教材第4章作为课后选读。
第7/7页 · 用自己的话讲一遍
用自己的程序和数据解释,能说清动作比背定义更重要。
先自己答,再对照
| 问题 | 可以这样说 |
|---|---|
| JdbcTemplate 帮我做了什么? | 接收我写的 SQL 和参数,连接数据库执行,再把查询结果或影响行数交回来。 |
| queryForList 与 update 有什么区别? | 前者读出一组结果;后者执行新增或修改,返回影响了几行。 |
| INSERT 的两个问号分别收到什么? | 第一个是我的学号,第二个是初始余额,顺序与列名对应。 |
| 为什么要记真实 id? | 数据库自动分配编号,我不能假设它是1;修改时要找到自己的那一行。 |
| 修改行数0能算完成吗? | 不能。说明没有匹配到要修改的行,要核对编号和学号。 |
一分钟展示
打开自己的 AddAccount 指出学号和30.0;打开 UpdateBalance 指出真实 id、同一学号和45.0;展示 Run 输出与数据库那一行。最后说明“小林为什么没有被我改动”。
老师怎么检查
| 检查项 | 看得到的证据 | 分值 |
|---|---|---|
| 查询 | 运行查询,名单含自己的学号。 | 2 |
| 新增 | 新增行数与生成的编号有实际记录。 | 3 |
| 修改 | 同一账户变为45.00,其他账户未变。 | 3 |
| 解释 | 能对应问号和值,说明影响行数0。 | 2 |
课后只留一个小任务
把自己的目标余额改成60.0,先写预测,再运行并查表。记录你改了哪个 Java 变量、传给第几个问号、数据库哪一列改变。无需新增功能或改工程结构。
下一课怎么接上
今天每次只改一条 SQL。下一课再讨论“两条修改必须一起成功”的情况,届时再引入事务。保留本课工程与练习记录即可。
来源:本课自编课堂练习;API依据 Spring Framework 6.1 JdbcTemplate;教材第4章作为课后选读。