Skip to main content

Overview

The Library API uses JPA entities to model the core domain concepts: books, authors, users, and rental transactions. These entities are mapped to database tables with relationships defined using JPA annotations.

Entity Relationships

Author Entity

Defined in AuthorEntity.java:19-29, represents book authors:
@Entity
@Table(name = "authors")
public class AuthorEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "author_id_sequence")
    private Long id;

    private String name;

    private Integer age;
}

Fields

id
Long
required
Auto-generated sequence ID serving as the primary key
name
String
Author’s full name
age
Integer
Author’s age

Book Entity

Defined in BookEntity.java:24-43, represents books in the library:
@Entity
@Table(name = "books")
public class BookEntity {

    @Id
    private String isbn;

    private String title;

    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "author_id")
    private AuthorEntity author;

    @OneToMany(mappedBy = "book", orphanRemoval = true)
    private Set<RentalEntity> rentals;

    private Integer stock;

    @Column(nullable = true)
    private String coverImage;
}

Fields

isbn
String
required
International Standard Book Number, serving as the primary key
title
String
Book title
author
AuthorEntity
Many-to-one relationship with Author. Cascade operations are enabled, so saving a book can create/update its author
rentals
Set<RentalEntity>
One-to-many relationship with Rental records. Uses orphanRemoval = true to automatically delete rental records when removed from this collection
stock
Integer
Number of available copies. Decremented when books are rented and incremented when returned
coverImage
String
URL or path to the book’s cover image (optional)

Cascade Behavior

The @ManyToOne(cascade = CascadeType.ALL) on the author relationship means that when you save or update a book, the associated author will also be persisted automatically.

User Entity

Defined in UserEntity.java:23-48, represents registered users:
@Entity
@Table(name = "users")
public class UserEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_id_sequence")
    private Long id;

    private String firstname;

    private String lastname;

    @Column(unique = true, nullable = false)
    private String email;

    @OneToMany(mappedBy = "user")
    private Set<RentalEntity> borrowedBooks;

    @Column(nullable = true)
    private String password;

    @Column(nullable = true)
    private String googleId;

    @Column(nullable = true)
    private String displayPicture;
}

Fields

id
Long
required
Auto-generated sequence ID serving as the primary key
firstname
String
User’s first name
lastname
String
User’s last name
email
String
required
Unique email address used as the username for authentication. Marked as unique = true and nullable = false
borrowedBooks
Set<RentalEntity>
One-to-many relationship with Rental records tracking all books this user has borrowed
password
String
BCrypt-hashed password for local authentication (optional if using OAuth)
googleId
String
Google OAuth identifier for social authentication (optional)
displayPicture
String
URL to user’s profile picture (optional)
The email field is the unique identifier for authentication. The UserPrincipal.java:24-26 implementation uses email as the username.

Rental Entity

Defined in RentalEntity.java:23-43, tracks book loans:
@Entity
@Table(name = "rentals")
public class RentalEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "rental_id_sequence")
    private Long id;

    @ManyToOne
    @JoinColumn(name = "user_id")
    private UserEntity user;

    @ManyToOne
    @JoinColumn(name = "book_id")
    private BookEntity book;

    private ZonedDateTime loanDate;

    private ZonedDateTime returnDate;

    private boolean returned;
}

Fields

id
Long
required
Auto-generated sequence ID serving as the primary key
user
UserEntity
Many-to-one relationship identifying who borrowed the book
book
BookEntity
Many-to-one relationship identifying which book was borrowed
loanDate
ZonedDateTime
Timestamp when the book was borrowed (stored in UTC as seen in RentalController.java:78)
returnDate
ZonedDateTime
Expected or actual return date. Set to 2 weeks after loan date by default (RentalController.java:80)
returned
boolean
Flag indicating whether the book has been returned. Updated by the return endpoint in RentalController.java:94

Rental Logic

When a book is rented (RentalController.java:59-85):
  1. Validates that both book and user exist
  2. Checks book stock availability
  3. Decrements book stock by 1
  4. Creates rental record with loan date and return date (2 weeks)
  5. Returns HTTP 201 (CREATED) with the rental details
When a book is returned (RentalController.java:88-106):
  1. Marks the rental as returned
  2. Increments book stock by 1
  3. Returns HTTP 200 (OK) with updated rental details

Borrowed Book Entity

Defined in BorrowedBookEntity.java:23-40, provides an alternative representation of borrowed books:
@Entity
@Table(name = "borrowed_books")
public class BorrowedBookEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne
    @JoinColumn(name = "user_id")
    private UserEntity user;

    @ManyToOne
    @JoinColumn(name = "book_id")
    private BookEntity book;

    private LocalDate loanDate;

    private LocalDate returnDate;
}

Fields

id
Long
required
Auto-generated identity ID serving as the primary key
user
UserEntity
Many-to-one relationship identifying who borrowed the book
book
BookEntity
Many-to-one relationship identifying which book was borrowed
loanDate
LocalDate
Date when the book was borrowed (date only, no time component)
returnDate
LocalDate
Expected or actual return date (date only, no time component)
This entity differs from RentalEntity by using LocalDate instead of ZonedDateTime and GenerationType.IDENTITY instead of SEQUENCE. The choice between these entities depends on whether you need timezone information and which ID generation strategy is preferred.

Key Design Patterns

Lombok Annotations

All entities use Lombok annotations for boilerplate reduction:
  • @Data - Generates getters, setters, toString, equals, and hashCode
  • @AllArgsConstructor - Generates constructor with all fields
  • @NoArgsConstructor - Generates no-argument constructor (required by JPA)
  • @Builder - Implements builder pattern for object construction

Primary Key Strategies

Sequence Generation

Used by Author, User, and Rental entities. Generates IDs using database sequences for better performance with batch inserts.

Natural Key

Book entity uses ISBN as a natural primary key since ISBNs are unique and meaningful identifiers.

Identity Generation

BorrowedBook entity uses database identity columns for auto-incrementing IDs.

Bidirectional Relationships

Several bidirectional relationships exist in the model:
  • Book ↔ Rental: Book has Set<RentalEntity> rentals, Rental has BookEntity book
  • User ↔ Rental: User has Set<RentalEntity> borrowedBooks, Rental has UserEntity user
When working with bidirectional relationships, maintain both sides of the relationship to avoid data inconsistencies. The mappedBy attribute indicates which side owns the relationship.

Build docs developers (and LLMs) love