基于IDEA的图书管理系统实战:从建表到借阅闭环全流程

发布时间:2026/10/4 8:39:32
基于IDEA的图书管理系统实战:从建表到借阅闭环全流程 简介这是一套面向Java初学者与课程设计学习者的图书管理系统完整项目源码基于IntelliJ IDEA开发适合用于毕业设计参考、课程作业实践或Java Web入门练手。项目围绕用户管理、书籍维护与借阅流程等典型业务展开涵盖Servlet、JSP、HTML页面及数据库操作等核心环节帮助读者理解MVC分层与前后端交互的基本实现思路。压缩包共68个文件约7.09MB包含16个java源文件与16个class编译文件、5个html页面、5个jsp、5个xml配置、4个jar依赖以及css、js、png等静态资源并附有设计模式类图文档便于对照理解项目结构与模块划分。目前已有1642人学习下载可作为搭建Java Web项目、梳理代码组织方式的实用参考素材。1. 基于IDEA的图书管理系统从建表到借阅闭环一次讲透很多人第一次在 IntelliJ IDEA 里做图书管理系统卡住的地方往往不是“不会写 Java”而是不知道一个能跑起来的系统到底该由哪些部分组成。这个标题背后其实是一套完整的 Java 桌面或 Web 应用开发链路用 IDEA 建工程、用面向对象建模图书和读者、用 JDBC 或框架连数据库、用 Swing 或 Spring Boot 做界面和接口最后把借书、还书、查询这些动作串成闭环。它适合刚学完 Java 基础、想找一个能写进简历的项目练手的人也适合工作几年但一直做业务 CRUD、想回头补齐“从零搭一个系统”经验的工程师。下面我按自己实际做过的路径把选型、建表、编码、排错和进阶一次讲清楚。2. 先定架构再动手IDEA 工程怎么建、技术栈怎么选2.1 三种常见形态先想清楚你要做哪一种图书管理系统在 Java 圈里至少有三种主流做法选错了后面会反复返工。第一种是控制台版用Scanner读输入、System.out打印菜单数据存内存或文件。优点是零依赖、半小时能跑通适合验证面向对象设计缺点是没法演示简历上写出来也偏弱。第二种是 Swing 桌面版用JFrame、JTable做界面JDBC 连 MySQL。这是高校课程设计和培训班最常要求的形态因为“看得见界面”好交差。缺点是布局代码繁琐遇到中文乱码和高分屏适配要折腾。第三种是 Spring Boot Web 版后端提供 REST 接口前端用 Thymeleaf 或 Vue。这是目前企业里最接近真实项目的做法也是面试时最能展开讲的形态。如果你时间够我建议直接做这一种因为idea运行javaweb项目配置这类问题你迟早要面对不如现在就用一个完整项目吃透。选型上还有一个绕不开的点JDK 版本。新项目用 JDK 17 或 21 都行但要注意 Spring Boot 3.x 最低要求 JDK 17如果你还在用 JDK 8就配 Spring Boot 2.7.x。这个对应关系搞错启动时会直接报Unsupported class file major version。2.2 在 IDEA 里建工程的完整步骤打开 IDEAFile → New → Project左侧选Spring Initializr社区版没有这个选项社区版用户走New Project → Maven手动加依赖这也是idea社区版怎么用spring boot被频繁搜索的原因。填好 Group 和 Artifact比如com.example和libraryType 选 MavenJava 版本选 17。依赖勾选这几项Spring Web、Spring Data JPA、MySQL Driver、Lombok、Thymeleaf。Lombok 能省掉大量 getter/setter但要注意 IDEA 需要装 Lombok 插件并开启注解处理否则代码里全是红色报错。# 建好后的目录结构关键部分 library/ ├── src/main/java/com/example/library/ │ ├── LibraryApplication.java # 启动类 │ ├── entity/ # 实体类 │ ├── repository/ # 数据访问层 │ ├── service/ # 业务层 │ └── controller/ # 接口层 ├── src/main/resources/ │ ├── application.yml # 配置文件 │ └── templates/ # 页面模板 └── pom.xml # 依赖管理这个分层不是摆设。实体类只描述数据Repository 只管增删改查Service 放借阅规则Controller 接请求。很多人把所有逻辑堆在 Controller 里后期加一个“逾期计算”就要改十几个地方血泪经验。2.3 数据库连接配置与一个必踩的坑application.yml里配数据源spring: datasource: url: jdbc:mysql://localhost:3306/library?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: your_password driver-class-name: com.mysql.cj.jdbc.Driver jpa: hibernate: ddl-auto: update show-sql: trueserverTimezone不写MySQL 8 会报时区错误characterEncodingutf8不写中文书名存进去变问号。ddl-auto: update在开发期很方便实体改了自动改表结构但上线前一定要改成none或validate否则一次误操作可能把生产表结构改坏。3. 图书管理系统的核心表设计与实体映射3.1 四张表撑起整个系统图书管理系统的数据模型不复杂但设计时要想清楚关系。核心是四张表图书表、读者表、借阅记录表外加一张分类表。表名关键字段说明bookid, isbn, title, author, category_id, total, availabletotal 是总库存available 是可借数量readerid, card_no, name, phone, statusstatus 标记正常/冻结borrow_recordid, book_id, reader_id, borrow_date, due_date, return_date, statusstatus 标记借出/已还/逾期categoryid, name图书分类这里有个设计决策库存到底怎么表示。常见做法是book表存total和available两个字段借书时available - 1还书时 1。另一种做法是不存available每次实时统计借阅记录。前者查询快但有一致性风险后者准确但慢。我一般用前者同时在 Service 层加事务和乐观锁避免并发借书把库存扣成负数。3.2 实体类与 JPA 映射Entity Table(name book) Data public class Book { Id GeneratedValue(strategy GenerationType.IDENTITY) private Long id; Column(unique true, nullable false, length 20) private String isbn; Column(nullable false, length 100) private String title; private String author; ManyToOne JoinColumn(name category_id) private Category category; private Integer total; private Integer available; // 乐观锁版本号防止并发扣减库存出错 Version private Integer version; }Version这个字段很多人不加单机测试时看不出问题一旦有并发借书就可能出现超借。加了之后两个线程同时改同一条记录后提交的会抛OptimisticLockException在 Service 层捕获后提示“请重试”即可。ManyToOne关联分类查询时默认是懒加载如果你在 Controller 里直接返回 Book 对象并序列化可能报LazyInitializationException。解决办法是在 Repository 里用EntityGraph或写 JPQL 的join fetch别等到报错才去查。3.3 借阅记录表的时间字段处理借阅记录里borrow_date、due_date、return_date三个时间字段建议统一用LocalDate而不是Date。Date带时分秒做“是否逾期”判断时容易因为时分秒差异算错一天。Entity Table(name borrow_record) Data public class BorrowRecord { Id GeneratedValue(strategy GenerationType.IDENTITY) private Long id; ManyToOne JoinColumn(name book_id, nullable false) private Book book; ManyToOne JoinColumn(name reader_id, nullable false) private Reader reader; private LocalDate borrowDate; private LocalDate dueDate; private LocalDate returnDate; // 0-借出中 1-已归还 2-逾期 private Integer status; }dueDate一般在借出时算好比如borrowDate.plusDays(30)。逾期判断不要存成固定状态而是查询时动态算returnDate null dueDate.isBefore(LocalDate.now())就是逾期。存死状态的话每天都要跑定时任务刷新多一个故障点。4. 借书还书闭环Service 层怎么写才不出错4.1 借书逻辑与事务边界借书这个动作看着简单实际要处理四件事检查读者状态、检查库存、写借阅记录、扣减库存。这四步必须在一个事务里任何一步失败都要回滚。Service RequiredArgsConstructor public class BorrowService { private final BookRepository bookRepository; private final ReaderRepository readerRepository; private final BorrowRecordRepository recordRepository; Transactional public BorrowRecord borrow(Long bookId, Long readerId) { Reader reader readerRepository.findById(readerId) .orElseThrow(() - new BizException(读者不存在)); if (reader.getStatus() ! 1) { throw new BizException(读者账户已冻结无法借书); } Book book bookRepository.findById(bookId) .orElseThrow(() - new BizException(图书不存在)); if (book.getAvailable() 0) { throw new BizException(该书已全部借出); } // 同一读者不能重复借同一本未还的书 boolean alreadyBorrowed recordRepository .existsByBookIdAndReaderIdAndStatus(bookId, readerId, 0); if (alreadyBorrowed) { throw new BizException(您已借阅此书且未归还); } book.setAvailable(book.getAvailable() - 1); bookRepository.save(book); BorrowRecord record new BorrowRecord(); record.setBook(book); record.setReader(reader); record.setBorrowDate(LocalDate.now()); record.setDueDate(LocalDate.now().plusDays(30)); record.setStatus(0); return recordRepository.save(record); } }Transactional加在方法上Spring 会在方法开始时开事务、正常返回时提交、抛异常时回滚。注意默认只对RuntimeException回滚如果你抛的是受检异常要写Transactional(rollbackFor Exception.class)。existsByBookIdAndReaderIdAndStatus是 Spring Data JPA 的方法名派生查询不用写 SQL但方法名必须严格按字段名拼拼错了启动就报错。4.2 还书逻辑与逾期计算Transactional public BorrowRecord returnBook(Long recordId) { BorrowRecord record recordRepository.findById(recordId) .orElseThrow(() - new BizException(借阅记录不存在)); if (record.getStatus() ! 0) { throw new BizException(该记录已归还); } record.setReturnDate(LocalDate.now()); // 超过应还日期就算逾期 if (LocalDate.now().isAfter(record.getDueDate())) { record.setStatus(2); } else { record.setStatus(1); } recordRepository.save(record); Book book record.getBook(); book.setAvailable(book.getAvailable() 1); bookRepository.save(book); return record; }还书时库存加一这里同样有并发问题。如果两个人同时还同一本书available可能只加了一次。Version乐观锁能挡住这种情况但要在 Service 层捕获异常并重试。更稳妥的做法是用数据库原子更新UPDATE book SET available available 1 WHERE id ?这样不依赖读改写。4.3 查询接口的分页与条件组合图书列表查询通常要支持按书名模糊搜、按分类筛选、分页。用 Spring Data JPA 的Specification动态拼条件最灵活。public PageBook search(String keyword, Long categoryId, int page, int size) { SpecificationBook spec (root, query, cb) - { ListPredicate predicates new ArrayList(); if (StringUtils.hasText(keyword)) { predicates.add(cb.like(root.get(title), % keyword %)); } if (categoryId ! null) { predicates.add(cb.equal(root.get(category).get(id), categoryId)); } return cb.and(predicates.toArray(new Predicate[0])); }; return bookRepository.findAll(spec, PageRequest.of(page, size, Sort.by(id).descending())); }PageRequest.of的 page 从 0 开始前端传 1 的话要减一这个差一错误几乎每个人都犯过。Sort.by(id).descending()保证新书排在前面不加排序时数据库返回顺序不保证稳定翻页可能出现重复或遗漏。5. 避坑与排查IDEA 里做图书管理系统最常见的 5 个翻车点5.1 启动报错 Port 8080 was already in use现象点运行控制台红字提示端口被占用应用起不来。原因上一次运行没正常关闭或者本机有其他服务占了 8080。解决在application.yml里改server.port: 8081或者用命令行netstat -ano | findstr 8080找到进程号taskkill /PID 进程号 /F杀掉。IDEA 里还有个隐蔽情况你开了两个窗口跑同一个项目第二个必然冲突。5.2 中文书名存进数据库变问号现象页面输入“Java编程思想”数据库里查出来是???。原因连接串没指定字符编码或者数据库、表的字符集不是 utf8mb4。解决连接串加characterEncodingutf8建库时用CREATE DATABASE library DEFAULT CHARSET utf8mb4。已经建好的表可以用ALTER TABLE book CONVERT TO CHARACTER SET utf8mb4改。注意 utf8 在 MySQL 里是阉割版存 emoji 会失败统一用 utf8mb4。5.3 IDEA 不显示 target 目录但文件确实存在现象项目能跑但左侧项目树里看不到target目录手动去文件夹里看又有。原因IDEA 默认把target标记为排除目录不显示在项目视图里。解决File → Project Structure → Modules → Sources把target的 Excluded 标记去掉。或者直接在Settings → File Types → Ignored Files and Folders里检查有没有误加。这个不是 bug是 IDEA 的默认行为很多人第一次遇到会以为编译失败。5.4 代码格式化突然失效现象CtrlAltL按了没反应或者格式化后代码风格完全不对。原因常见有三种——快捷键被其他软件占用比如某些输入法、当前文件类型没关联到 Java、或者装了冲突的插件。解决先看Settings → Keymap里Reformat Code绑的是什么键再看Settings → Editor → Code Style → Java的配置有没有被改乱。如果是导入的项目带了.editorconfigIDEA 会优先按它格式化检查项目根目录有没有这个文件。5.5 打包成 jar 后运行报数据库连接失败现象IDEA 里跑得好好的mvn package打出的 jar 用java -jar启动就报连不上数据库。原因配置文件没被打进 jar或者打包时用了application-dev.yml而 jar 里只有application.yml。解决确认src/main/resources下的配置文件会被 Maven 默认打包。如果用了 profile启动时加--spring.profiles.activedev。另外检查pom.xml里有没有配resources过滤把 yml 排除了。这个坑在idea 打包docker镜像场景里更常见因为容器里的数据库地址和本机不一样要用环境变量覆盖。6. 进阶技巧用接口测试和日志把系统验证到位系统能跑起来只是第一步怎么证明它真的对是区分新手和熟手的地方。我一般会做两件事写接口测试覆盖借还闭环以及用日志把关键路径打出来。先看测试。Spring Boot 项目自带spring-boot-starter-test用 MockMvc 可以不起真实服务器就测接口。SpringBootTest AutoConfigureMockMvc class BorrowControllerTest { Autowired private MockMvc mockMvc; Test void borrow_should_decrease_available() throws Exception { // 借书前先查库存 mockMvc.perform(post(/api/borrow) .param(bookId, 1) .param(readerId, 1)) .andExpect(status().isOk()) .andExpect(jsonPath($.status).value(0)); // 再查这本书available 应该少 1 mockMvc.perform(get(/api/book/1)) .andExpect(jsonPath($.available).value(4)); } }这个测试的价值在于它把“借书后库存减一”这个业务规则固化下来了。以后谁改了借书逻辑导致库存不对跑一下测试立刻发现。jsonPath用来断言返回的 JSON 字段比字符串匹配精确得多。再说日志。借书、还书这种关键操作一定要打日志而且要带上业务标识。Slf4j Service public class BorrowService { Transactional public BorrowRecord borrow(Long bookId, Long readerId) { log.info(借书请求开始, bookId{}, readerId{}, bookId, readerId); // ... 业务逻辑 log.info(借书成功, recordId{}, dueDate{}, record.getId(), record.getDueDate()); return record; } }日志级别用info记录业务动作用debug记录参数细节用error记录异常。别用System.out.println因为线上没法按级别过滤也没法输出到文件。Slf4j是 Lombok 提供的编译时自动生成log对象。还有一个验证技巧用数据库的唯一索引兜底。比如借阅记录表加一个联合唯一索引(book_id, reader_id, status)但 status 会变这个索引不成立。更实际的做法是在 Service 层用synchronized或分布式锁控制同一读者的并发借书但单机项目用数据库行锁就够了。最后说一个我自己的习惯每做完一个模块先不急着写下一个而是把当前模块的接口用 Postman 或 IDEA 自带的 HTTP Client 全跑一遍把正常和异常分支都覆盖。图书管理系统看着简单但“读者已冻结”“库存为零”“重复借阅”这些边界才是面试官真正会问的地方。把这些处理干净这个项目才算真正拿得出手。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询