How PersonCarDog saves the in-memory Repository to a JSON file
and why this is the perfect bridge to Spring Boot + JPA
Core idea:
The Repository acts as an in-memory database.
JsonStore is the “disk” layer that serializes the whole database
into a single JSON file (data/repo.json) and restores it later.
This is exactly the role a real database (H2, PostgreSQL…) plays in Spring Boot.
1. Architecture Mapping (Java SE → Spring Boot)
Controller
Controller.java
→ @RestController
Service
Service.save / load
→ @Service + @Transactional
Repository
Repository (ArrayLists)
→ Spring Data JPA Repo
Persistence
JsonStore + Snapshot
→ Database + EntityManager
Current project (Java SE)
JsonStore
File-based persistence
One JSON file = whole DB
Manual mapping (DTO ↔ Entity)
No transactions, no SQL
Perfect for teaching
Spring Boot equivalent
JPA / Hibernate
Real database (H2, Postgres…)
Tables + foreign keys
Automatic mapping (@Entity)
@Transactional, ACID
Production-ready
2. Why do we need a Snapshot (DTO)?
Domain objects have object references (Person → Car, Person ↔ Dog).
If we serialize them directly with Jackson we get:
Instantiate every Car, Person, Dog with the persisted ID
Put them into temporary Maps keyed by ID
Add them to the Repository
At this point references are still null / missing.
Pass 2 — Wire references
Look up each entity by ID from the Maps
Set person.car, person.dog, dog.owner
Rebuild CarTransaction & DogAdoption objects with real object references
Now every relation is a single shared instance (no duplicates).
Why two passes?
You cannot set a reference to an object that does not exist yet.
First create everything, then connect the dots — the same strategy ORMs use when loading bidirectional associations.
5. Key Classes
Class
Role
Spring Boot analogue
JsonStore
Save / load the whole Repository
EntityManager + Database
RepositorySnapshot
Flat, cycle-free DTO container
Database schema (tables + FKs)
*Dto inner classes
Simple POJOs with id + foreign-key ids
@Entity columns + @JoinColumn
ObjectMapper (Jackson)
JSON serialization / deserialization
Hibernate dialect + JDBC
Service.saveRepository / loadRepository
Public API + error handling
@Service method with Result / ResponseEntity
6. Default File Location
Default path defined in JsonStore:
data/repo.json
Relative to the project working directory. The parent folder is created automatically on save.
7. Teaching Takeaways for Spring Boot
Repository = in-memory DB → later becomes a real JPA Repository.
Snapshot DTOs = foreign keys → in Spring you will use @ManyToOne / @OneToOne and the ORM does the ID mapping for you.
Two-pass load → Hibernate does the same thing when it materializes the object graph from SQL result sets.
Bidirectional relations (Person.dog ↔ Dog.owner) must stay consistent — in JPA you will use mappedBy and cascade options.
JsonStore.save / load → later replaced by automatic dirty checking + entityManager.persist() / flush().
Bottom line for students:
You are already implementing the core persistence concepts (object graph → flat storage → object graph)
with pure Java + Jackson. When you move to Spring Boot you will keep the same domain model
and simply let JPA/Hibernate handle the Snapshot and the two-pass loading for you.