目录
该图书系统实现的五个功能:管理员上架功能、读者注册功能、读者借书功能、读者还书功能、读者查阅借阅资料功能。
一、建立连接
1. 思路
为了写代码的方便,新建了一个 uti l包下的 DBUtil 类来实现连接的建立。要使用代码时,直接调用DBUtil下的connection() 方法即可。而其他功能写在新建的 lib 包下。
2. 代码
package util;
import com.mysql.cj.jdbc.MysqlDataSource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
/**
* Created with IntelliJ IDEA.
* Description: 用来创建连接对象 Connection
* User: WangWZ
* Date: 2022-12-08
* Time: 19:00
*/
public class DBUtil {
private static final DataSource dataSource;
static {
MysqlDataSource mysqlDataSource = new MysqlDataSource();
mysqlDataSource.setUrl("jdbc:mysql://127.0.0.1:3306/java45_1206?characterEncoding=utf8&&useSSL=false&&serverTimezone=Asia/Shanghai");
mysqlDataSource.setUser("root");
mysqlDataSource.setPassword("12345");
dataSource = mysqlDataSource;
}
public static Connection connection() throws SQLException {
return dataSource.getConnection();
}
}
二、 管理员上架功能
1. 思路
(1) 首先要读取要上架的图书信息:书名、要上架多少本书;
(2) 执行 SQL 语句:
上架书籍:要先知道书架里有没有这本书 。select bid from books where name = ... ;
- 若原来没有这本书,select 查找结果为一条记录。然后 intert 添加这本书 insert into books (name,count,total) values ...
- 若有这本书,select 查找结果为 0 条结果。然后 update 更新这本书的数据 update books set current = current +..., total = total + ... where bid =...;
总结得出要执行三条SQL语句:connection 连接只需要一个,准备执行的对象 ps 要三个。
2. 代码
public class 管理员图书上架功能 {
public static void main(String[] args) throws SQLException {
//首先要读取要上架的图书信息:书名、要上架多少本书
Scanner scanner = new Scanner(System.in);
System.out.println("管理员图书上架功能:");
System.out.print("请输入要上架的图书书名:");
if (!scanner.hasNextLine()) {
System.out.println("退出");
return;
}
String name = scanner.nextLine();
System.out.print("请输入本次上架的图书数量:");
if (!scanner.hasNextLine()) {
System.out.println("退出");
return;
}
int count = Integer.parseInt(scanner.nextLine());
//执行 SQL 语句
try (Connection c = DBUtil.connection()) {
//连接已经创建好
//(1)判断书架上有没有这本书
Integer bid;
String sql = "select bid from books where name = ?";
try (PreparedStatement ps = c.prepareStatement(sql)) {
ps.setString(1,name); // 对该 sql 语句中的 ? 进行参数绑定
System.out.println("DEBUG: 执行 sql:" + ps);
try (ResultSet rs = ps.executeQuery()) {
//这里进行是否有这本书的判断
if (rs.next() == false) {
bid = null;
} else {
bid = rs.getInt("bid");
}
}
}
//根据 bid 分情况执行 SQL 语句
if (bid == null) {
//没有这本书,添加
sql = "insert into books (name,current,total) values (?,?,?)";
try (PreparedStatement ps = c.prepareStatement(sql)) {
ps.setString(1,name);
ps.setInt(2,count);
ps.setInt(3,count);
System.out.println("DEBUG: 执行 sql:" + ps);
//因为这里执行的是没有结果集的 insert 操作,所以使用 ps.executeUpdate()
ps.executeUpdate();
}
} else {
//有这本书,更新
sql = "update books set current = count + ?,total = total + ? where bid = ?";
try (PreparedStatement ps = c.prepareStatement(sql)) {
ps.setInt(1,count);
ps.setInt(2,count);
ps.setInt(3,bid);
System.out.println("DEBUG: 执行 sql:" + ps);
//因为这里执行的是没有结果集的 update 操作,所以使用 ps.executeUpdate()
ps.executeUpdate();
}
}
}
//关闭连接之后,提示用户上架成功
System.out.println("书籍上架成功");
}
}
三、读者注册功能
1. 思路
(1) 先读取用户要注册需要的信息:用户名、密码。
(2) 执行 SQL 语句:
读者注册:要先判断该用户是否存在,即用户名是否存在。
- 若存在该用户:注册失败,提示用户该用户名已存在。
- 若不存在该用户:进行插入操作 insert into users (username, password) values (?, ?);
(3)因为在 users 表中,username 是唯一键,所以当已经存在该用户名时再进行插入会报错,我们可以通过处理异常来提醒用户注册失败。
2. 代码
public class 读者注册功能 {
public static void main(String[] args) throws SQLException {
//先读取用户要注册需要的信息:用户名、密码
Scanner scanner = new Scanner(System.in);
System.out.println("读者注册功能:");
System.out.print("请输入要注册的用户名:");
if (!scanner.hasNextLine()) {
System.out.println("退出");
return;
}
String username = scanner.nextLine();
System.out.print("请输入要注册的密码:");
if (!scanner.hasNextLine()) {
System.out.println("退出");
return;
}
String password = scanner.nextLine();
//执行 SQL 语句
try (Connection c = DBUtil.connection()) {
String sql = "insert into users (username,password) values (?,?)";
try (PreparedStatement ps = c.prepareStatement(sql)) {
ps.setString(1,username);
ps.setString(2,password);
System.out.println("DEBUG: 执行 sql:" + ps);
ps.executeUpdate();
}
} catch (SQLIntegrityConstraintViolationException exc) {
//说明 username 的唯一约束被破坏了
System.out.println("注册失败,该用户名已存在");
return;
}
System.out.println("注册成功");
}
}
四、读者借书功能
1. 思路
(1) 先提示用户输入借书功能需要的信息:①先登录:用户名、密码。 ②借书信息:书名
(2) 执行 SQL 语句:要完成一个完整的读者借书功能需要考虑三部分:①登录验证;②书名判断;③进行借书。
① 登录验证:用户输完用户名和密码后判断 users 表中有无该用户以及密码是否正确,用户名和密码均正确才显示登录成功,并且获取 uid 值,反之则显示登录失败。select uid from users where username = ? and password = ?; (用 uid 是因为在 records 表中要用)
② 书名判断:用户输完书名后,判断 books 表中是否有该书,以及 current 是否不为 0。
- 有书的情况下,再调用 rs.getInt() 方法获取出 该条记录的current 的值,若等于 0 提示用户库存不够无法借书,反之获取 bid 值并进行下一步借书操作
- 没书的情况下,提示用户没有该书。select bid,current from books where name = ?; (用 bid 是因为在 records 表中要用)
③ 进行借书:代码执行到这里说明可以正常借书。要完成借书操作:在 records 表中插入借书记录;更新 books 表中的 current 数据。
insert into records (uid, bid, borrowed_at) values (?, ?, ?);
update books set current = current - 1 where bid = ?;
(3)因为借书需要获取当前的时间,所以写了一个方法nowTime() 来进行当前时间的获取。
2. 代码
public class 读者借书功能 {
public static void main(String[] args) throws SQLException {
//先提示用户输入借书功能需要的信息:①先登录:用户名、密码。 ②借书信息:书名
//执行 SQL 语句
Scanner scanner = new Scanner(System.in);
System.out.println("读者借书功能:");
System.out.print("请输入用户名:");
if (!scanner.hasNextLine()) {
System.out.println("退出");
return;
}
String username = scanner.nextLine();
System.out.print("请输入密码:");
if (!scanner.hasNextLine()) {
System.out.println("退出");
return;
}
String password = scanner.nextLine();
int uid;
int bid;
//登录验证
try (Connection c = DBUtil.connection()) {
String sql = "select uid from users where username = ? and password = ?";
try (PreparedStatement ps = c.prepareStatement(sql)) {
ps.setString(1,username);
ps.setString(2,password);
System.out.println("DEBUG: 执行 SQL :" + ps);
try (ResultSet rs = ps.executeQuery()) {
if (rs.next() == false) {
System.out.println("登录失败");
return;
} else {
System.out.println("登陆成功");
uid = rs.getInt("uid");
// uid = rs.getInt(1); 也可以,表示获取 select 字段的第一个
}
}
}
}
//书名判断
System.out.print("请输入要借的书:");
if (!scanner.hasNextLine()) {
System.out.println("退出");
return;
}
String name = scanner.nextLine();
try (Connection c = DBUtil.connection()) {
String sql = "select bid, current from books where name = ?";
try (PreparedStatement ps = c.prepareStatement(sql)) {
ps.setString(1,name);
try (ResultSet rs = ps.executeQuery()) {
if (rs.next() == false) {
System.out.println("没有该书");
return;
} else {
int current = rs.getInt("current");
if (current <= 0) {
System.out.println("该书已被全部借书");
return;
} else {
bid = rs.getInt("bid");
}
}
}
}
}
//进行借书
try (Connection c = DBUtil.connection()) {
String sql = "insert into records (uid, bid, borrowed_at) values (?, ? ,?)";
try (PreparedStatement ps = c.prepareStatement(sql)) {
ps.setInt(1,uid);
ps.setInt(2,bid);
ps.setString(3,nowTime()); //要传入借书的时间,这里使用方法传入时间字符串
System.out.println("DEBUG: 执行 SQL:" + ps);
ps.executeUpdate();
}
sql = "update books set current = current - 1 where bid = ?";
try (PreparedStatement ps = c.prepareStatement(sql)) {
ps.setInt(1,bid);
System.out.println("DEBUG: 执行 SQL :" + ps);
ps.executeUpdate();
}
}
System.out.println("借书成功");
}
private static String nowTime() {
LocalDateTime now = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
return now.format(formatter);
// yyyy 代表 4 个长度的年,如 2022
// MM 代表 2 个长度的月,如 02
// dd 代表 2 个长度的日,如 06
// HH 代表 24 小时,如 20
// mm :37
// ss :秒
}
}
五、读者还书功能
1. 思路
(1)先提示用户输入借书功能需要的信息:①先登录:用户名、密码。 ②还书信息:书名。
(2)执行 SQL:要完成一个完整的读者还书功能需要考虑四部分:①登录验证;②是否有该书判断;③是否借过该书判断;④进行还书。
① 登录验证:用户输完用户名和密码后判断 users 表中有无该用户以及密码是否正确,用户名和密码均正确才显示登录成功,并且获取 uid 值,反之则显示登录失败。select uid from users where username = ? and password = ?; (用 uid 是因为在 records 表中要用)
② 是否有该书判断:用户输完书名后,在 books 表中查找是否有该本书。(为了获取 bid,因为 records 表中记录的是 bid 而不是 name)
- 没有该书,提示用户没有该书。
- 有该书,获取 bid 的值,进行下一步操作。 select bid from books where name = ?;
③ 是否借过该书判断:判断 books 中有该本书后,判断 records 表中是否有该书的借书记录。
- 有借书记录书的情况下,再调用 rs.getInt() 方法获取出该条记录的 id 的值。(为了进行还书的操作,即要修改哪条记录)
- 没借书记录的情况下,提示用户没有借过该书。select id from records where uid = ? and bid = ? and returned_at is null;
④ 进行还书:代码执行到这里说明可以正常还书。要完成还书操作:在 records 表中更新借书记录(填入还书时间);更新 books 表中的 current 数据。
update records set returned_at = ? where id = ?;
update books set current = current + 1 where bid = ?;
(3)这里还是用到 nowTime() 方法。
2. 代码
public class 读者还书功能 {
public static void main(String[] args) throws SQLException {
//先提示用户输入借书功能需要的信息:①先登录:用户名、密码。 ②还书信息:书名
//执行 SQL
Scanner scanner = new Scanner(System.in);
System.out.println("读者还书功能:");
System.out.print("请输入用户名:");
if (!scanner.hasNextLine()) {
System.out.println("退出");
return;
}
String username = scanner.nextLine();
System.out.print("请输入密码:");
if (!scanner.hasNextLine()) {
System.out.println("退出");
return;
}
String password = scanner.nextLine();
int uid;
int bid;
int id;
//登录验证
try (Connection c = DBUtil.connection()) {
String sql ="select uid from users where username = ? and password = ?";
try (PreparedStatement ps = c.prepareStatement(sql)) {
ps.setString(1,username);
ps.setString(2,password);
System.out.println("DEBUG: 执行 SQL :" + ps);
try (ResultSet rs = ps.executeQuery()) {
if (rs.next() == false) {
System.out.println("登录失败,没有该用户");
return;
} else {
System.out.println("登陆成功");
uid = rs.getInt("uid");
}
}
}
}
//是否有该书判断
System.out.print("请输入要还的书书名:");
if (!scanner.hasNextLine()) {
System.out.println("退出");
return;
}
String name = scanner.nextLine();
try (Connection c = DBUtil.connection()) {
String sql = "select bid from books where name = ?";
try (PreparedStatement ps = c.prepareStatement(sql)) {
ps.setString(1,name);
System.out.println("DEBUG: 执行 SQL:" + ps);
try (ResultSet rs = ps.executeQuery()) {
if (rs.next() == false) {
System.out.println("没有该书");
return;
} else {
bid = rs.getInt("bid");
}
}
}
}
//是否借过该书判断
try (Connection c = DBUtil.connection()) {
String sql = "select id from records where bid = ? and uid = ? and returned_at is null";
try (PreparedStatement ps = c.prepareStatement(sql)) {
ps.setInt(1,bid);
ps.setInt(2,uid);
System.out.println("DEBUG: 执行 SQL: " + ps);
try (ResultSet rs = ps.executeQuery()) {
if (rs.next() == false) {
System.out.println("没有相关借书记录");
return;
} else {
id = rs.getInt("id");
}
}
}
}
//进行还书
try (Connection c = DBUtil.connection()) {
String sql = "update records set returned_at = ? where id = ?";
try (PreparedStatement ps = c.prepareStatement(sql)) {
ps.setString(1,nowTime());
ps.setInt(2,id);
System.out.println("DEBUG: 执行 SQL: " + ps);
ps.executeUpdate();
}
sql = "update books set current = current + 1 where bid = ?";
try (PreparedStatement ps = c.prepareStatement(sql)) {
ps.setInt(1,bid);
System.out.println("DEBUG: 执行 SQL: " + ps);
ps.executeUpdate();
}
}
System.out.println("还书成功");
}
private static String nowTime() {
LocalDateTime now = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
return now.format(formatter);
}
}
六、读者查看借阅历史功能
1. 思路
(1)先提示用户输入查看借阅历史需要的信息:用户名、密码。
(2)大致思路:
- 因为一名用户可能会借阅好多本书,所以我们查询时可以按时间倒序或正序的方式查找;
- 查找后因为需要显示,所以可以定义一个类( Record ),里面存放要显示的信息,同时通过重写 toString 方法来进行显示;
- 一名用户可能会有很多条借阅记录,每条记录都是一个 Record 对象,所以要显示所有记录,选择用链表进行存储每条记录;
(3)期望显示的内容:
按照借阅时间的倒叙排序(最新借的在最上面)
2022-12-06 20:43:18 借了 《红楼梦》
2022-12-06 20:39:22 借了 《西游记》
下面部分展示的曾经结果并且已经还了的书,也按照借阅时间的倒叙
2022-12-06 20:43:18 借了 《红楼梦》,于 2022-12-06 21:43:28 归还
2022-12-06 20:39:22 借了 《西游记》,于 2022-12-06 21:43:28 归还
(4)执行 SQL :要完成一个完整的读者查看借阅历史功能需要考虑三部分:①登录验证;②查询该用户的借阅记录;③将查询出来的 bid 都转换为书名 name。
① 登录验证:用户输完用户名和密码后判断 users 表中有无该用户以及密码是否正确,用户名和密码均正确才显示登录成功,并且获取 uid 值,反之则显示登录失败。select uid from users where username = ? and password = ?; (用 uid 是因为在 records 表中要用)
② 查询该用户的借阅记录:
- 若没有借阅记录,提示用户没有借阅记录。
- 若有借阅记录,则进行下一步操作。select bid, borrowed_at, returned_at from records where uid = ? order by borrowed_at desc;
③ 将查询出来的 bid 都转换成书名 name:
- 先用 HashSet 存储 bid 列表(因为要求不重复,不要求顺序,所以选择使用 Set 接口下的 HashSet);
- 因为要根据 bid 对照出 name,涉及到映射,所以使用 Map 类定义一个对象存储 映射关系。
- 最后通过遍历存储借阅记录的链表,将每条记录里对应的 bid 的 name 根据 映射关系 找出并填入。
2. 代码
(1)Record 类
public class Record {
public int bid;
public String bookName;
public String borrowedAt;
public String returnedAt;
public Record(int bid, String borrowedAt, String returnedAt) {
this.bid = bid;
this.borrowedAt = borrowedAt;
this.returnedAt = returnedAt;
}
@Override
public String toString() {
if (returnedAt == null) {
return borrowedAt + " 借了《" + bookName + "》";
}
return borrowedAt + " 借了《" + bookName + "》于" + returnedAt + "归还";
}
}
(2)实现功能代码
public class 读者查看借阅历史功能 {
public static void main(String[] args) throws SQLException {
//先提示用户输入查看借阅历史需要的信息:用户名、密码。
Scanner scanner = new Scanner(System.in);
System.out.println("读者查看借阅历史功能:");
System.out.print("请输入用户名:");
if (!scanner.hasNextLine()) {
System.out.println("退出");
return;
}
String username = scanner.nextLine();
System.out.print("请输入密码:");
if (!scanner.hasNextLine()) {
System.out.println("退出");
return;
}
String password = scanner.nextLine();
int uid;
//登录验证
try (Connection c = DBUtil.connection()) {
String sql = "select uid from users where username = ? and password = ?";
try (PreparedStatement ps = c.prepareStatement(sql)) {
ps.setString(1,username);
ps.setString(2,password);
System.out.println("DEBUG: 执行 SQL:" + ps);
try (ResultSet rs = ps.executeQuery()) {
if (!rs.next()) {
System.out.println("登录失败");
return;
} else {
System.out.println("登陆成功");
uid = rs.getInt("uid");
}
}
}
}
//查询该用户的借阅记录 -> 存储
List<Record> recordList = new ArrayList<>();
try (Connection c = DBUtil.connection()) {
String sql = "select bid, borrowed_at, returned_at from records where uid = ?";
try (PreparedStatement ps = c.prepareStatement(sql)) {
ps.setInt(1,uid);
System.out.println("DEBUG: 执行 SQL: " + ps);
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
int bid = rs.getInt("bid");
String borrowedAt = rs.getString("borrowed_at");
String returnedAt = rs.getString("returned_at");
Record record = new Record(bid,borrowedAt,returnedAt);
recordList.add(record);
}
}
}
}
//若链表为空,说明没有借阅记录,直接提示用户即可
if (recordList.isEmpty()) {
System.out.println("无借阅记录");
return;
}
//将查询出来的 bid 都转换成书名 name
Set<String> bidSet = new HashSet<>();
for (Record r : recordList) {
int bid = r.bid;
//将 bid 转为 String 类型是为了用 String.join()方法,最后用在 sql 语句中 selece....in(×, ×,×);
String bidStr = String.valueOf(bid);
bidSet.add(bidStr);
}
Map<Integer,String> bidToBookNameMap = new HashMap<>();
//String.join("x", list):将列表中的数据以 x 的间隔格式显示
String in = String.join(", ",bidSet);
try (Connection c = DBUtil.connection()) {
//这里只能使用字符串拼接
String sql = String.format("select bid, name from books where bid in (%s)", in);
try (PreparedStatement ps = c.prepareStatement(sql)) {
System.out.println("DEBUG: 执行 SQL: " + ps);
try (ResultSet rs = ps.executeQuery()) {
//将映射关系放入 bidToBookNameMap
while (rs.next()) {
int bid = rs.getInt("bid");
String name = rs.getString("name");
bidToBookNameMap.put(bid,name);
}
}
}
}
//通过 bidToBookNameMap 可以将 recordList 中的 bookName 进行填写了
for (Record record : recordList) {
int bid = record.bid;
String bookName = bidToBookNameMap.get(bid);
record.bookName = bookName;
}
//打印 recordList
for (Record record : recordList) {
if (record.returnedAt == null) {
System.out.println(record);
}
}
for (Record record : recordList) {
if (record.returnedAt != null) {
System.out.println(record);
}
}
}
}