DatabaseManager is the low-level persistence layer for Bibliotheca. It wraps a
SQLite connection, owns prepared statements, and provides a narrow set of CRUD
methods that the higher-level BookList uses to keep the database in sync.
sqlite3_open / sqlite3_close).ensure_schema).Book records.DatabaseManager
├── std::string db_path_ // path to the .sqlite file
├── sqlite3* db_ // raw SQLite handle (owned)
└── std::mutex mtx_ // serialises public API calls
All statements are prepared lazily by prepare_statements() and reused for each
operation.
ensure_schema() creates a single table named books:
CREATE TABLE IF NOT EXISTS books (
id TEXT PRIMARY KEY,
title TEXT,
author TEXT,
file_path TEXT,
cover_path TEXT
);
Additional indices or columns should be added here, keeping in mind that the manager is the canonical place to evolve storage.
| Method | Description |
|---|---|
ensure_schema() |
Creates tables if they do not exist. |
upsert_book(book) |
INSERT OR REPLACE using the book’s id. |
remove_book(id) |
Deletes the row; returns false if not present. |
get_book(id) |
Loads a single book (optional result). |
load_all_books() |
Returns a vector of all books. |
Internally each method locks mtx_, executes the prepared statement, and
translates SQLite rows into Book objects.
SQLite is file-based and can momentarily return SQLITE_BUSY if another thread
or process holds a lock. The static busy_handler callback retries with an
exponential backoff. Because all public methods take the mutex, only one thread
issues SQL commands at a time, simplifying concurrency.
ensure_schema() and keep
the Book serialization/deserialization in sync.BookList untouched.BookList owns a single DatabaseManager instance, calls ensure_schema() on
startup, and uses the CRUD helpers whenever a book changes. Other modules should
not talk to SQLite directly; instead they should route all persistence through
this manager to keep logic centralised.