课次22:JDBC实现登录与新闻发布(含事务)
一、教学目标
实现登录查询(根据用户名密码查询用户)。
实现发布新闻(插入news表,同时更新用户news_count),使用事务保证一致性。
二、核心知识点(简要)
事务:一组操作要么全部成功,要么全部失败。
setAutoCommit(false):关闭自动提交,开启事务。
commit() 和 rollback():提交或回滚事务。
ResultSet:查询结果集,使用next()遍历。
三、操作步骤
- 创建news表
打开SQLyog,选中weitout数据库,点+号,新建查询
image-20260613155552170
在新的询问中输入如下内容:
CREATE TABLE news (-- 创建名为 news 的表
id INT PRIMARY KEY AUTO_INCREMENT,-- 【主键】定义 id 字段,类型为整数,设为主键并开启自增
user_id INT NOT NULL,
title VARCHAR(200) NOT NULL, -- 【标题】
content TEXT, -- 【正文】定义 content 字段,文本类型
publish_time DATETIME DEFAULT CURRENT_TIMESTAMP,
view_count INT DEFAULT 0,-- 【浏览量】默认为 0,后续可通过 UPDATE 语句累加
FOREIGN KEY (user_id) REFERENCES user(id)-- 【外键约束】建立 user_id 与 user 表 id 之间的关联,确保发布的 user_id 必须在 user 表中真实存在
);
然后选中全部内容,点击运行
image-20260613155552170
点开weitoutiao数据库中的表,可以看到新增了一张名为news的表,再点击表数据,点击刷新,新的表就显示出来,表示news数据表已成功创建。
image-20260613155955411
- 创建News实体类(包含id, userId, title, content, publishTime, viewCount)
打开IDEA,继续在上个工程上修改,右键entity,新建java类,News类
image-20260613161102146
image-20260613161141458
News类中编写代码如下:
package com.weitoutiao.entity;// 【包声明】表明该类属于 entity(实体)层
import java.time.LocalDateTime;// 【导入包】引入 Java 8 的时间处理类,对应数据库中的 DATETIME 类型
public class News {// News 实体类,对应数据库中的 news 表
private Integer id; // 【主键】对应表中的 id 字段
private Integer userId;// 【外键】对应表中的 user_id 字段,表示发布该新闻的用户ID
private String title;// 存储新闻的简短标题
private String content; // 存储新闻的详细内容
private LocalDateTime publishTime; //使用 LocalDateTime精确匹配数据库的DATETIME格式
private Integer viewCount;// 统计该新闻被查看的次数
}
3. 在UserDAO中添加login方法
login方法如下:
public User login(String username, String password) {
String sql = "SELECT id, username, role, news_count FROM user WHERE username=? AND password=?";
// 定义 SQL 查询语句:从 user 表中查找匹配的用户名和密码,并只查询需要的字段(id, username, role, news_count)
try (Connection conn = DBUtil.getConnection();// 调用工具类获取数据库连接对象
PreparedStatement ps = conn.prepareStatement(sql)) {// 预编译 SQL 语句,生成 PreparedStatement 对象
ps.setString(1, username); // 设置 SQL 语句中第一个问号 (?) 的值为传入的 username 参数
ps.setString(2, password);
ResultSet rs = ps.executeQuery();// 执行 SQL 查询语句,并将结果存储在 ResultSet 结果集对象中
if (rs.next()) {// 判断结果集中是否有数据(即是否找到了匹配的用户)
User user = new User();// 如果找到用户,实例化一个新的 User 对象用于封装数据
user.setId(rs.getInt("id"));// 从结果集中获取 "id" 字段的值,并设置到 user 对象的 id 属性中
user.setUsername(rs.getString("username"));
user.setRole(rs.getString("role"));
user.setNewsCount(rs.getInt("news_count"));
return user;// 将封装好数据的 user 对象返回给调用者,表示登录成功
}
} catch (SQLException e) {// 捕获在执行数据库操作过程中可能发生的 SQL 异常
e.printStackTrace();// 在控制台打印异常的堆栈信息,方便开发者调试和排查错误
}
return null; //如果没有找到匹配的用户(if 条件不成立)or发生了异常,则返回null,表示登录失败
}
在UserDao类中,login方法添加的位置如下:
image-20260613161644847
ResultSet会爆红,鼠标悬停之上,选择导入类
image-20260613161744339
user的set方法还会爆红,这是因为User类的选择器没有配置,再返回到User实体类中添加
image-20260613161947381
在User实体类中的空白位置右键,选择生成,再选择Getter和Setter,生成set和get方法
image-20260613162133296
image-20260613162226104
可以全选中,一次全部生成
image-20260613162515279
最后生成的方法如下所示:
image-20260613162607697
再看UserDao类中的set已经不红了。
- 创建NewsDAO实现发布新闻(事务)
右键dao包名,新建NewsDAO类
NewsDAO类中的代码如下:
package com.weitoutiao.dao;// 【包声明】表明该类属于 dao(数据访问)层
import com.weitoutiao.util.DBUtil;// 【导入工具类】引入自定义的数据库连接工具
import java.sql.Connection;// 【导入类】数据库连接接口
import java.sql.PreparedStatement;// 【导入类】预编译 SQL 语句接口
import java.sql.SQLException;// 【导入异常】SQL 异常处理类
public class NewsDAO {//新闻数据访问对象
//发布新闻的方法
public boolean publish(int userId, String title, String content) {
//【定义 SQL】向 news 表中插入一条新记录,使用 ? 占位符防注入
String insertSql = "INSERT INTO news (user_id, title, content) VALUES (?, ?, ?)";
// 【定义 SQL】根据用户 id,将该用户的发帖总数 (news_count) 累加 1
String updateSql = "UPDATE user SET news_count = news_count + 1 WHERE id = ?";
Connection conn = null;
PreparedStatement psInsert = null;
PreparedStatement psUpdate = null;
try {
conn = DBUtil.getConnection();// 【获取连接】通过工具类获取一个活跃的数据库连接
conn.setAutoCommit(false); // 开启事务,关闭自动提交功能!这是保证数据一致性的关键一步
psInsert = conn.prepareStatement(insertSql);// 【准备插入语句】预编译第一条 SQL(插入新闻)
psInsert.setInt(1, userId);// 【设置参数】依次为占位符赋值:用户ID、标题、内容
psInsert.setString(2, title);
psInsert.setString(3, content);
psInsert.executeUpdate();// 【执行插入】将新闻写入数据库(此时尚未真正落盘)
psUpdate = conn.prepareStatement(updateSql);// 【准备更新语句】预编译第二条 SQL(增加用户发帖数)
psUpdate.setInt(1, userId);// 【设置参数】指定要更新的用户 ID
psUpdate.executeUpdate();// 【执行更新】增加发帖计数(同样尚未真正落盘)
conn.commit(); // 提交事务,两步操作都成功后,统一提交!让所有修改真正生效
return true; // 【返回结果】全部顺利执行,返回 true 表示发布成功
} catch (SQLException e) {// 【捕获异常】如果上述任何一步发生错误(如插入成功但更新失败),进入此块
// 【回滚事务】检查连接是否存在,保证“插入新闻”和“更新用户发帖数”一致
if (conn != null) {
try { conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); }
}
e.printStackTrace();
return false;
} finally {
try { if (psInsert != null) psInsert.close(); } catch (SQLException e) {}
try { if (psUpdate != null) psUpdate.close(); } catch (SQLException e) {}
try { if (conn != null) conn.close(); } catch (SQLException e) {}
}
}
}
5. 修改Main测试登录和发布新闻
具体内容如下:
package com.weitoutiao;// 【包声明】这是项目的根包,通常用于存放程序的启动类 Main
import com.weitoutiao.dao.UserDAO;// 【导入 DAO 类】引入用户数据访问对象
import com.weitoutiao.dao.NewsDAO;// 【导入 DAO 类】引入新闻数据访问对象
import com.weitoutiao.entity.User;// 【导入实体类】引入 User 实体类,用于接收登录返回的数据
public class Main {
public static void main(String[] args) {
UserDAO userDAO = new UserDAO();
User user = userDAO.login("testuser", "123456");// 【调用登录方法】传入硬编码的用户名和密码进行验证
if (user != null) {
System.out.println("登录成功,用户ID:" + user.getId());
NewsDAO newsDAO = new NewsDAO();
boolean published = newsDAO.publish(user.getId(), "我的第一条新闻", "内容");
System.out.println(published ? "发布成功" : "发布失败");
} else {
System.out.println("登录失败");
}
}
}
6.运行测试
控制台打印如下:
image-20260613164903956
打开SQLyog,查看news数据表如下:
image-20260613165012998
课次22:JDBC实现登录与新闻发布(含事务)
一、教学目标
二、核心知识点(简要)
三、操作步骤
1. 创建news表
2. 创建News实体类(包含id, userId, title, content, publishTime, viewCount)
3. 在UserDAO中添加login方法
4. 创建NewsDAO实现发布新闻(事务)
5. 修改Main测试登录和发布新闻
6.运行测试
EOF
浙公网安备 33010602011771号