Skip to content

Coding Models, Representing Cardinalities, and Starting with JPA

Topics to be Covered

  1. Models
  2. Representing Cardinalities
  3. JPA Queries
  4. Inheritance Representation

Understanding Models

Key Concepts:

  • What Are Models?

    • Models are the blueprint entities in an application whose data is stored in the database. They represent real-world objects or concepts within a specific domain, such as products in an e-commerce application or users in a social media platform.
    • Each model typically corresponds to a database table, where the model's attributes are mapped to table columns.
  • Example in Product Service:

    • Products: Represents items available for purchase, including attributes like id, name, description, price, etc.
    • Category: Represents groups or classifications under which products are categorized, with attributes like id, name, and description.
  • Handling Deletion in Models:

    • Soft Delete:
    • Instead of permanently deleting a record from the database, a "soft delete" involves marking the record as deleted by setting an isDeleted flag to true. This approach is preferred in scenarios where data recovery is important, as it avoids permanent data loss.
    • Advantages:
      • Data Integrity: Accidental deletions can be prevented. For instance, if a wrong SQL command deletes records, they can be restored by setting isDeleted back to false.
      • Performance: Soft deletes are often faster than hard deletes because no rows are physically removed, thereby avoiding the overhead of row reorganization in the database.
    • Hard Delete:
    • A "hard delete" permanently removes the record from the database, making it irretrievable. This is straightforward but risky, especially in systems where data integrity is critical.
  • Common Attributes in Models:

    • Every model typically has certain attributes that are common across different entities. These attributes help track the lifecycle of a record and manage its state:
      • id: A unique identifier for each record, often automatically generated by the database.
      • createdAt: A timestamp indicating when the record was first created.
      • lastUpdatedAt: A timestamp showing the last time the record was updated.
      • isDeleted: A boolean flag indicating whether the record has been soft-deleted.
  • Recommended Practice:

    • It is recommended to create a BaseModel class that includes these common attributes. Every other model in the application can then inherit from this BaseModel. This not only ensures consistency across models but also reduces code redundancy.

Practical Implementation:

  1. Setting Up the Development Environment:

    • IDE Setup: Use IntelliJ IDEA or another preferred IDE for the implementation.
    • Dependencies:
      • Spring Data JDBC: To facilitate database interaction using JDBC.
      • MySQL Driver: To connect the application with a MySQL database.
    • Configuration:
      • Add the necessary dependencies in the pom.xml file.
      • Configure application.properties with database connection details such as URL, username, and password. Refer to the guide here for step-by-step instructions.
    • Database Setup:
      • Create a MySQL database specifically for the Product Service.
      • Create a database user with appropriate privileges, and grant all necessary permissions to this user.
    • Model Annotations:
      • Annotate the Product and Category classes with @Entity to map them to database tables.
      • Annotate the id field in the BaseModel class with @Id to designate it as the primary key.
  2. Demonstration:

    • Execution: Run the application to ensure it correctly generates the products and categories tables in the database using the Object-Relational Mapping (ORM) framework, Hibernate.
    • Error Handling: If errors occur, review the annotations and database configuration, guiding students through the debugging process.

Cardinalities and Spring Framework

Conceptual Understanding:

  • What are Cardinalities?

    • Cardinalities define the relationships between two entities in a database. Understanding these relationships is crucial when designing a relational database, as it dictates how tables interact with each other.
  • Types of Relationships:

    • One to One (1:1):
      • Each instance of entity A is associated with exactly one instance of entity B.
      • Example: A User entity and its corresponding UserProfile. Each user has exactly one profile, and each profile belongs to exactly one user.
    • One to Many (1:m):
      • One instance of entity A is associated with multiple instances of entity B.
      • Example: A Category entity can have multiple Product entities. Each category can contain multiple products, but each product belongs to only one category.
    • Many to One (m:1):
      • Multiple instances of entity A are associated with a single instance of entity B.
      • Example: Multiple Product entities can belong to one Category entity.
    • Many to Many (m:m):
      • Multiple instances of entity A can be associated with multiple instances of entity B.
      • Example: A Student entity and a Course entity. A student can enroll in multiple courses, and a course can have multiple students.
  • How to Represent Relationships:

    • One to One:
      • Typically represented by storing the primary key of one entity as a foreign key in the other entity.
    • One to Many / Many to One:
      • The foreign key of the 'one' side is stored on the 'many' side. For example, the Category ID is stored in the Product table.
    • Many to Many:
      • A join table (or mapping table) is used to store the associations between the entities.

Practical Implementation:

  1. Representing Cardinalities in Spring:

    • Annotations:
      • Use @OneToOne, @OneToMany, @ManyToOne, or @ManyToMany annotations to define relationships between entities.
      • Example: For a Product class having a reference to a Category class, use @ManyToOne to indicate that many products belong to one category.
  2. Handling Bidirectional Relationships:

    • When defining relationships in both directions (e.g., Product has a Category, and Category has a list of Product), ensure that Spring understands this as the same relationship.
    • Use the mappedBy attribute to avoid creating duplicate relationships.
    • Example:
      @ManyToOne
      @JoinColumn(name = "category_id")
      private Category category;
      
      @OneToMany(mappedBy = "category")
      private List<Product> products;
      
  3. Managing Cascade Operations:

    • CascadeType.PERSIST: Ensures that when saving a product, if the associated category does not exist, it will be created and persisted.
    • CascadeType.REMOVE: When a category is deleted, all associated products are also deleted to maintain referential integrity.
    • Example:
      • In the Category class, using CascadeType.REMOVE will automatically delete all products when a category is deleted

Creating a New Product

Step-by-Step Implementation:

  1. Creating Repository Interfaces:

    • Purpose: Repository interfaces provide the abstraction layer for CRUD operations on the database.
    • ProductRepository:
    • Extend JpaRepository<Product, Long>, where Product is the entity and Long is the type of the primary key.
    • This interface will inherit methods for common database operations like save, findById, delete, etc.
    • CategoryRepository:
    • Similarly, extend JpaRepository<Category, Long> to manage category entities.
  2. Implementing the Service Layer:

    • SelfProductService:
    • Use constructor injection to inject ProductRepository and CategoryRepository.
    • Example:
      @Service
      public class SelfProductService {
          private final ProductRepository productRepository;
          private final CategoryRepository categoryRepository;
      
          @Autowired
          public SelfProductService(ProductRepository productRepository, CategoryRepository categoryRepository) {
              this.productRepository = productRepository;
              this.categoryRepository = categoryRepository;
          }
      }
      
  3. Creating a New Product:

    • Functionality:
      • Implement a method createProduct that first checks whether the category already exists in the database.
      • If the category exists, the product is added to the existing category. If not, a new category is created and associated with the product.
    • Using CascadeType.PERSIST:
      • Ensure that when a new product is saved, and its associated category does not exist, the category is created automatically.
    • Example:
      public Product createProduct(ProductDTO productDTO) {
          Category category = categoryRepository.findByName(productDTO.getCategoryName())
                              .orElse(new Category(productDTO.getCategoryName()));
          Product product = new Product(productDTO.getName(), productDTO.getPrice(), category);
          return productRepository.save(product);
      }