Files
Claude 26ece9cc9b Add library-management module: console app evolved into a Spring Boot 4 REST API
Stage 2 console classes and the shared LoanPolicy, then a Spring Boot 4.1.2 + JPA API
(H2, @Version optimistic locking, problem+json errors). Tests write output/ transcripts
while asserting the same numbers; scripts regenerate everything.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_017ic4b4DPL6dtpFg4U4LHc2
2026-11-14 21:21:16 +05:30

4.1 KiB

library-management

Companion code for the ankurm.com article Library Management System in Java: Console Version to Spring Boot REST API. One module, three stages: the same lending rules, first in plain Java, then split into classes, then served over HTTP with Spring Boot 4 and JPA.

Every number, status code and SQL line quoted in the article comes from a file in output/. The tests write those files while asserting the same numbers, so the transcripts cannot drift from the code.

Versions

Component Version Verified against
JDK 25.0.5 (Temurin, LTS) java -version
Spring Boot 4.1.2 (4.1.0 went GA on 2026-06-10) maven-metadata.xml on Maven Central
Spring Framework 7.0.10 mvn dependency:list
Hibernate ORM 7.4.13.Final mvn dependency:list
Spring Data JPA 4.1.2 mvn dependency:list
Tomcat (embedded) 11.0.27 mvn dependency:list
H2 2.4.240 mvn dependency:list
Maven 3.9.11 mvn -v

The three stages

Stage Where Run it
1. One file, no build tool LibraryConsole.java (attached to the article, not stored here) java LibraryConsole.java < scripts/demo-input.txt
2. Split into classes, rules extracted src/main/java/com/ankurm/library/console/ + core/LoanPolicy.java mvn -q compile && java -cp target/classes com.ankurm.library.console.ConsoleApp < scripts/demo-input.txt
3. Spring Boot REST API with JPA src/main/java/com/ankurm/library/api/ + core/LoanPolicy.java scripts/run.sh

core/LoanPolicy.java (14-day loans, 3 active loans, Rs 2 a day fine) is shared by stages 2 and 3 and did not change when the app moved to a database.

Quickstart

export JAVA_HOME=/path/to/jdk-25 && export PATH="$JAVA_HOME/bin:$PATH"
mvn test                  # 6 tests; also regenerates output/02 to output/06
scripts/run.sh            # API on http://localhost:8080 (in-memory H2, clean library per start)
scripts/api-demo.sh       # in another terminal: curl walk-through

The first Maven run must be online so the plugins are cached; -o works afterwards. scripts/run-all.sh regenerates every file in output/.

Endpoints

Method and path Purpose Refusals
POST /api/books add a book with N copies 400 bad fields, 409 duplicate ISBN
GET /api/books list books with copies available
POST /api/members register a member 400 bad fields, 409 duplicate email
POST /api/loans borrow: {"memberId":1,"bookId":2} 404 unknown id, 409 no copies, 422 limit or duplicate loan
POST /api/loans/{id}/return return, optional {"returnedOn":"2026-11-20"} 409 already returned, 422 before borrow date
GET /api/loans all loans, one SQL statement

Errors are RFC 9457 application/problem+json.

Captured output

File What it shows Produced by
01-console-structured.txt stage 2 run of scripts/demo-input.txt (identical to stage 1) run-all.sh
02-loan-policy.txt the fine table LoanPolicyTest
03-rest-api.txt every success and refusal over MockMvc LibraryApiTest
04-query-count.txt 11 SQL statements versus 1 for the same list QueryCountTest
05-race-with-version.txt 8 threads, last copy, @Version: one winner BorrowRaceTest
06-race-without-version.txt same race without @Version: 8 loans for 1 copy BorrowRaceTest
07-curl-demo.txt the same API on a real Tomcat, driven with curl (dates are the day it ran) scripts/api-demo.sh
08-open-in-view-warning.txt the Boot default this module overrides scripts/open-in-view-warning.sh
09-borrow-sql.txt the exact SQL one borrow sends, including where id=? and version=? scripts/sql-trace.sh

Timing-dependent detail: in 05 the 7 refused borrows split between "optimistic-lock failure" (a thread read the book before the winner committed) and "any other reason" (a thread started late and found no copy left). Over repeated runs I saw splits ranging from 7/0 to 4/3, which is why the transcript counts them separately. The invariants (1 winner, 1 loan, 0 copies left) are asserted.