# Database Guidelines > MySQL data and DAO conventions for the library-management system. --- ## Overview MySQL is the project data layer. DAO classes perform CRUD and query operations against MySQL. The initial scaffold schema exists at `src/main/resources/db/schema.sql`; future module tables should follow the same DDL style and DAO boundaries. --- ## Core Tables Use primary keys for every table and foreign keys for cross-entity integrity. Implemented scaffold tables: - `roles`: administrator, librarian, reader, and future role definitions. - `permissions`: permission definitions for protected actions. - `role_permissions`: role-to-permission mapping. - `users`: login accounts for administrator, librarian, and reader roles. - `system_logs`: key operation logs, backup events, and exception traces. - `book_categories`: category names and descriptions for catalog grouping. - `books`: book information, category reference, inventory counts, and catalog status. - `readers`: reader profiles, optional login-account linkage, borrowing eligibility, contact information, and management status. Planned module tables: - `borrow_records`: book-reader borrowing, return, renew, and overdue data. Record new schema changes in `src/main/resources/db/schema.sql` and update this spec with exact table names, key columns, and DAO/service contracts. --- ## DAO Responsibilities - DAOs own database CRUD and query details. - Use parameterized SQL or prepared-statement style access; never concatenate raw request parameters into SQL. - Keep transaction boundaries in the service layer for workflows that span multiple DAO calls, such as borrow/return operations that also update inventory status. - Return entities or small query result objects to services, not HTML or servlet response objects. - Keep MySQL connection details in `src/main/resources/db.properties` loaded by `JdbcUtil`. The required keys are `db.driver`, `db.url`, `db.username`, and `db.password`. --- ## Query Guidance - Book search must support combined lookup by title, author, category, and ID. - Statistics queries should cover borrowing rankings, inventory reports, and overdue reports. - Borrowing records should preserve enough dates/status fields for borrow, return, renew, overdue calculation, and automatic collection status updates. - Permission queries should support role-based checks for administrator, librarian, and reader workflows. --- ## Integrity Constraints - `books.category_id` should reference `book_categories`. - `borrow_records.book_id` should reference `books`. - `borrow_records.reader_id` should reference `readers`. - Administrator-role and role-permission mapping tables should use foreign keys to preserve authorization integrity. - `users.role_code` must reference `roles.code`. - `role_permissions.role_code` must reference `roles.code`. - `role_permissions.permission_code` must reference `permissions.code`. - `books.status` must match `BookStatus` enum codes: `available`, `unavailable`, and `archived`. - `readers.user_id` may reference `users.id` when a reader profile is linked to a login account. - `readers.status` must match `ReaderStatus` enum codes: `active`, `suspended`, and `inactive`. - `readers.max_borrow_count` must stay between 1 and 50. ## Scenario: Book Catalog And Management Slice ### 1. Scope / Trigger - Trigger: the first concrete business module introduced catalog search and basic book management across MySQL, DAO, service, Servlet, and JSP layers. - Schema path: `src/main/resources/db/schema.sql`. - JSP paths: `WEB-INF/jsp/books/catalog.jsp`, `books/manage.jsp`, and `books/form.jsp`. ### 2. Signatures - DAO signatures: `BookDao.findAllCategories()`, `BookDao.search(BookSearchCriteria criteria)`, `findById(long id)`, `findByIdentifier(String identifier)`, `create(Book book)`, `update(Book book)`, and `delete(long id)`. - Entity/search signatures: `Book` fields are `id`, `identifier`, `title`, `author`, `categoryId`, `categoryName`, `totalCopies`, `availableCopies`, `status`, `createdAt`, and `updatedAt`; `BookSearchCriteria` fields are `identifier`, `title`, `author`, and nullable `categoryId`. - Service signatures: `BookService.listCategories()`, `searchBooks(BookSearchCriteria criteria)`, `findBook(long id)`, `createBook(AuthenticatedUser actor, Book book)`, `updateBook(AuthenticatedUser actor, Book book)`, and `deleteBook(AuthenticatedUser actor, long id)`, all returning `ServiceResult`. - Read route: `GET /catalog` with optional `identifier`, `title`, `author`, and `categoryId` query fields. - Management routes: `GET /books`, `GET /books/new`, `GET /books/edit?id=...`, `POST /books`, `POST /books/update`, and `POST /books/delete`. - Protected permissions: `/catalog` requires `VIEW_CATALOG`; `/books*` requires `MANAGE_BOOKS`. - DB signatures: - `book_categories(id, name, description, created_at, updated_at)`, with unique key `uk_book_categories_name(name)`. - `books(id, book_identifier, title, author, category_id, total_copies, available_copies, status, created_at, updated_at)`, with unique key `uk_books_identifier(book_identifier)`, indexes on title, author, category, and status, foreign key `fk_books_category`, and checks for non-negative copy counts and allowed status values. ### 3. Contracts - `book_categories.name` is unique and displayed through category selectors. - `books.book_identifier` is the unique user-facing book ID. - `books.category_id` references `book_categories.id`. - `books.total_copies` and `books.available_copies` are non-negative, and available copies cannot exceed total copies. - `books.status` stores the Java `BookStatus` code exactly. - Servlet controllers parse request fields and set JSP attributes such as `criteria`, `categories`, `books`, `book`, `statuses`, `errors`, `errorMessage`, and `successMessage`. - `ServiceResult` is the service-to-controller response contract: `successful`, nullable `data`, nullable `message`, and field-level `errors`. Controllers must pass validation errors to JSPs so form/search redisplay can highlight the exact field, for example `errors.categoryId`. - JSP pages render JavaBean properties only; they must not call DAOs or embed SQL. ### 4. Validation & Error Matrix - Missing identifier, title, author, category, copy counts, or status -> return to `books/form.jsp` with field errors. - Negative total or available copies -> return a field error. - Available copies greater than total copies -> return `Available copies cannot exceed total copies.` - Duplicate `book_identifier` -> return a field error on `identifier`. - Reader or unauthenticated actor attempts write -> HTTP 403 through authorization filter or service denial. - DAO failure during list/search/write -> log server-side details and return `Book service is temporarily unavailable. Please try again later.` - Successful create/update/delete -> redirect to `/books` with a short flash message. ### 5. Good/Base/Bad Cases - Good: a librarian creates `BK-1002`, sees it in `/books`, and readers can find it from `/catalog` without write controls. - Base: catalog search with no filters lists available records ordered by title, author, and identifier. - Bad: a JSP opens JDBC, builds SQL from request parameters, or renders a stack trace from a failed DAO call. ### 6. Tests Required - Run `BookServiceCheck` or equivalent assertions for invalid inventory, duplicate identifiers, reader write denial, successful librarian CRUD, search, and DAO failure fallback. - Run `PermissionPolicyCheck` to confirm readers lack `MANAGE_BOOKS` and retain `VIEW_CATALOG`. - Scan JSPs for scriptlets and SQL/JDBC references. - When Maven/Tomcat dependencies are installed, run `mvn clean package` to compile Servlets and package JSP resources. ### 7. Wrong vs Correct #### Wrong ```text books/form.jsp -> JDBC -> INSERT INTO books using request parameters ``` #### Correct ```text books/form.jsp -> BookManagementServlet -> BookService -> BookDao -> books ``` ## Scenario: Reader Information Management Slice ### 1. Scope / Trigger - Trigger: the reader profile foundation was implemented after login, permissions, catalog search, and book management. - Schema path: `src/main/resources/db/schema.sql`. - JSP paths: `WEB-INF/jsp/readers/manage.jsp` and `readers/form.jsp`. ### 2. Signatures - DAO signatures: `ReaderDao.search(ReaderSearchCriteria criteria)`, `findById(long id)`, `findByIdentifier(String identifier)`, `findByUserId(long userId)`, `create(Reader reader)`, `update(Reader reader)`, and `deactivate(long id)`. - Entity/search signatures: `Reader` fields are `id`, `identifier`, nullable `userId`, nullable `username`, `fullName`, `phone`, `email`, `status`, `maxBorrowCount`, `createdAt`, and `updatedAt`; `ReaderSearchCriteria` fields are `identifier`, `name`, `contact`, and `statusCode`. - Status signature: `ReaderStatus` enum codes are `active`, `suspended`, and `inactive`. - Service signatures: `ReaderService.searchReaders(ReaderSearchCriteria)`, `findReader(long id)`, `createReader(AuthenticatedUser actor, Reader reader)`, `updateReader(AuthenticatedUser actor, Reader reader)`, and `deactivateReader(AuthenticatedUser actor, long id)`, all returning `ServiceResult`. - Management routes: `GET /readers`, `GET /readers/new`, `GET /readers/edit?id=...`, `POST /readers`, `POST /readers/update`, and `POST /readers/delete`. - Protected permission: `/readers*` requires `MANAGE_READERS`. - DB signature: `readers(id, reader_identifier, user_id, full_name, phone, email, status, max_borrow_count, created_at, updated_at)`, with unique keys on `reader_identifier` and nullable `user_id`, indexes on name/contact/status, a foreign key to `users(id)`, and checks for supported status values and borrow-limit range. ### 3. Contracts - `readers.reader_identifier` is the unique user-facing reader ID. - `readers.user_id` is optional; when present, only one reader profile may link to the same login account. - `readers.status` stores the Java `ReaderStatus` code exactly. Soft-delete behavior deactivates the profile by setting status to `inactive`. - Reader searches support partial identifier, full-name, and phone/email contact matching plus exact status filtering. - Servlet controllers parse request fields and set JSP attributes such as `criteria`, `readers`, `reader`, `statuses`, `formValues`, `errors`, `errorMessage`, and `successMessage`. - JSP pages render JavaBean properties only; they must not call DAOs or embed SQL. - Navigation should expose reader management to administrator/librarian users, matching the current `MANAGE_READERS` policy. ### 4. Validation & Error Matrix - Missing identifier or full name -> return to `readers/form.jsp` with field errors. - Missing both phone and email -> return a field error on `phone`. - Invalid phone or email format -> return field errors on `phone` or `email`. - Unsupported status -> return a field error on `status`. - Max borrow count outside 1 to 50 -> return a field error on `maxBorrowCount`. - Non-positive linked `user_id` -> return a field error on `userId`. - Duplicate `reader_identifier` -> return a field error on `identifier`. - Duplicate linked `user_id` -> return a field error on `userId`. - Reader or unauthenticated actor attempts write -> HTTP 403 through authorization filter or service denial. - DAO failure during list/search/write -> log server-side details and return `Reader service is temporarily unavailable. Please try again later.` - Successful create/update/deactivate -> redirect to `/readers` with a short flash message. ### 5. Good/Base/Bad Cases - Good: a librarian creates `RD-1003`, searches by email, edits the borrow limit, and can later deactivate the profile without deleting future borrowing history. - Base: `/readers` with no filters lists reader records ordered by full name and reader identifier. - Bad: normal readers access `/readers`, a JSP opens JDBC, or deleting a reader profile removes rows that future borrow records need to reference. ### 6. Tests Required - Run `ReaderServiceCheck` or equivalent assertions for invalid contact, invalid borrow limit, duplicate identifiers, duplicate linked users, reader write denial, successful librarian CRUD-like operations, search, deactivate, and DAO failure fallback. - Run `PermissionPolicyCheck` to confirm librarians have `MANAGE_READERS` and readers do not. - Scan reader JSPs for scriptlets and SQL/JDBC references. - When Maven/Tomcat dependencies are installed, run `mvn clean package` to compile Servlets and package JSP resources. ### 7. Wrong vs Correct #### Wrong ```text readers/form.jsp -> JDBC -> DELETE FROM readers using request parameters ``` #### Correct ```text readers/form.jsp -> ReaderManagementServlet -> ReaderService -> ReaderDao -> readers.status = inactive ``` ## Scenario: Borrowing Circulation Management Slice ### 1. Scope / Trigger - Trigger: borrowing circulation now spans `borrow_records`, book inventory, reader eligibility, Servlet routes, service transactions, DAO locks, and JSP management/history screens. - Schema path: `src/main/resources/db/schema.sql`. - JSP paths: `WEB-INF/jsp/borrowing/manage.jsp`, `borrowing/form.jsp`, and `reader/loans.jsp`. ### 2. Signatures - Entity signatures: `BorrowRecord` fields are `id`, `readerId`, `readerIdentifier`, `readerName`, `bookId`, `bookIdentifier`, `bookTitle`, `borrowedAt`, `dueAt`, nullable `returnedAt`, `renewalCount`, `status`, `createdAt`, and `updatedAt`. - Status signature: `BorrowRecordStatus` enum codes are `active` and `returned`; overdue is derived from active, non-returned rows where `due_at < CURRENT_TIMESTAMP`. - Search signature: `BorrowRecordSearchCriteria(readerIdentifier, bookIdentifier, statusCode)` where `statusCode` may be empty, `active`, `returned`, or the derived filter `overdue`. - DAO signatures: `BorrowRecordDao.search(criteria)`, `findByReaderId(readerId)`, `findReaderByUserId(userId)`, `findReaderByIdentifierForUpdate(connection, identifier)`, `findBookByIdentifierForUpdate(connection, identifier)`, `findByIdForUpdate(connection, id)`, `countActiveByReaderId(connection, readerId)`, `create(connection, record)`, `decrementAvailableCopies(...)`, `incrementAvailableCopies(...)`, `markReturned(...)`, and `renew(...)`. - Service signatures: `BorrowingService.searchRecords(actor, criteria)`, `borrowBook(actor, readerIdentifier, bookIdentifier)`, `returnBook(actor, recordId)`, `renewLoan(actor, recordId)`, and `listCurrentReaderHistory(actor)`, all returning `ServiceResult`. - Management routes: `GET /borrowing`, `GET /borrowing/new`, `POST /borrowing/create`, `POST /borrowing/return`, and `POST /borrowing/renew`. - Reader route: `GET /reader/loans`. - Protected permissions: `/borrowing*` requires `MANAGE_BORROWING`; `/reader/loans` requires `BORROW_BOOKS` and the `reader` role because it displays only the signed-in reader's own history. - DB signature: `borrow_records(id, reader_id, book_id, borrowed_at, due_at, returned_at, renewal_count, status, created_at, updated_at)`, with foreign keys to `readers(id)` and `books(id)`, indexes on reader, book, status, and due date, and checks for non-negative renewal count and supported statuses. ### 3. Contracts - Borrow operations require an active reader and a borrowable book with `BookStatus.AVAILABLE` and `available_copies > 0`. - Reader active-loan count is rows with `status = active` and `returned_at IS NULL`; overdue rows still count toward `max_borrow_count`. - Borrowing creates one `borrow_records` row and decrements `books.available_copies` in the same transaction. - Returning an active loan sets `status = returned`, stores `returned_at`, and increments `books.available_copies` with a cap at `total_copies` in the same transaction. - Renewing an active loan extends `due_at`, increments `renewal_count`, and is limited to one renewal per MVP loan. - `JdbcUtil.executeInTransaction` is the local transaction helper for multi-table borrowing workflows. Services decide the workflow boundary; DAOs own SQL and row-lock statements. - Demo `readers` and `books` seed rows must not overwrite existing rows during schema replay, because resetting reader eligibility or `available_copies` can corrupt live borrowing state. - Servlet controllers set JSP attributes such as `criteria`, `statuses`, `overdueStatus`, `maxRenewals`, `borrowRecords`, `formValues`, `errors`, `errorMessage`, and `successMessage`. - JSP pages render JavaBean properties only; they must not call DAOs or embed SQL. ### 4. Validation & Error Matrix - Missing reader ID or book ID -> return to `borrowing/form.jsp` with field errors. - Unknown reader -> field error on `readerIdentifier`. - Inactive or suspended reader -> field error on `readerIdentifier`. - Reader at or above `max_borrow_count` active loans -> field error on `readerIdentifier`. - Unknown book -> field error on `bookIdentifier`. - Unavailable, archived, or zero-copy book -> field error on `bookIdentifier`. - Missing or non-positive record ID for return/renew -> flash error. - Returning or renewing a returned loan -> validation failure on `status`. - Renewing after the renewal limit -> validation failure on `renewalCount`. - DAO/transaction failure -> log server-side details and return `Borrowing service is temporarily unavailable. Please try again later.` ### 5. Good/Base/Bad Cases - Good: a librarian creates a loan for active reader `RD-1000` and available book `BK-1000`; the loan appears in `/borrowing` and book availability is decremented. - Base: `/borrowing?status=overdue` lists active, non-returned loans past due without requiring a scheduler or stored overdue status. - Bad: a Servlet updates `books.available_copies` outside the borrowing transaction or a JSP issues SQL to filter loan records. ### 6. Tests Required - Run `BorrowingServiceCheck` assertions for permission denial, inactive readers, unavailable/no-copy books, max active loan count, successful borrow, return inventory restoration, one-renewal limit, overdue search, reader loan history, and DAO failure fallback. - Run `PermissionPolicyCheck` to confirm readers have `BORROW_BOOKS`, readers lack `MANAGE_BORROWING`, and librarians lack reader self-borrow permission. Service or filter checks should also confirm staff use `/borrowing`, not the reader-only `/reader/loans` history route. - Scan JSPs for scriptlets and SQL/JDBC references. - When Maven/Tomcat dependencies are installed, run `mvn clean package` to compile Servlets and package JSP resources. ### 7. Wrong vs Correct #### Wrong ```text borrowing/manage.jsp -> JDBC -> UPDATE books SET available_copies = ... ``` #### Correct ```text borrowing/form.jsp -> BorrowingManagementServlet -> BorrowingService -> BorrowRecordDao -> borrow_records + books in one transaction ``` ## Scenario: Login And Permission Scaffold Schema ### 1. Scope / Trigger - Trigger: the initial Java Web scaffold introduced a concrete MySQL schema and login contract. - Schema path: `src/main/resources/db/schema.sql`. - Example configuration path: `src/main/resources/db.properties.example`. ### 2. Signatures - DAO signature: `UserDao.findActiveByUsername(String username)`. - Service signature: `AuthService.authenticate(String username, String password)`. - Permission signature: `AuthService.hasPermission(AuthenticatedUser user, Permission permission)`. - Servlet route: `POST /login` with `username`, `password`, and optional same-application `redirect`. - Protected routes: `/dashboard`, `/admin/home`, `/librarian/home`, and `/reader/home`. - Session keys: `authenticatedUser`, `userRole`, and `userPermissions`. - DB config keys: `db.driver`, `db.url`, `db.username`, and `db.password`. - Login tables: `roles`, `permissions`, `role_permissions`, `users`, and `system_logs`. ### 3. Contracts - `users.username`: unique login identifier submitted by `LoginServlet`. - `users.password_hash`: PBKDF2 hash in `pbkdf2_sha256$iterations$saltBase64$hashBase64` format. - `users.role_code`: foreign key to `roles.code`; supported scaffold values are `administrator`, `librarian`, and `reader`. - `users.active`: only rows with `active = 1` can authenticate. - `roles.code`, `permissions.code`, and `role_permissions` must match the Java `Role` and `Permission` enum codes exactly. - `db.properties` must be local configuration. Commit `db.properties.example`, but do not commit real credentials. - Session state stores an `AuthenticatedUser` snapshot, role code, and permission-code set. It must not store raw passwords or DAO result objects with password hashes. - Login redirects must stay inside the current application context. Reject values that do not start with a single `/` or that contain CR/LF characters. ### 4. Validation & Error Matrix - Missing username or password -> request returns to login JSP with `Username and password are required.` - Unknown user, inactive user, or hash mismatch -> request returns to login JSP with `Invalid username or password.` - Unsafe or blank redirect -> ignore and route to `/dashboard` after success. - Missing `db.properties`, JDBC failure, or unsupported role code -> request returns a generic service-unavailable message and logs server-side details. - Authenticated user missing a required permission -> HTTP 403 and `WEB-INF/jsp/auth/unauthorized.jsp`. ### 5. Good/Base/Bad Cases - Good: `admin` resolves to `administrator`, receives all scaffold permissions, and can access `/admin/home`. - Base: `reader` resolves to `reader`, can access `/reader/home`, and cannot access `/admin/home`. - Bad: a JSP reads SQL or password hashes directly from the database. Keep that logic in DAO/service code. ### 6. Tests Required - Compile service/DAO/entity/util classes with `javac` when Maven is unavailable. - Run `PermissionPolicyCheck` or equivalent assertions for administrator, librarian, and reader permissions. - Run `AuthServiceCheck` or equivalent assertions for required-field failures, invalid credentials, success, permission checks, and DAO failure fallback. - When Maven/Tomcat dependencies are installed, run `mvn test` or `mvn clean package` to compile Servlet and JSP integration. ### 7. Wrong vs Correct #### Wrong ```java // JSP, Servlet, or session code opens JDBC and stores password_hash. ``` #### Correct ```text login.jsp -> LoginServlet -> AuthService -> UserDao -> users/roles tables session -> AuthenticatedUser snapshot + role/permission codes only ```