Java
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented Java
-
Section 4: Collections Framework
-
Section 5: Exception Handling
-
Section 6: Generics
-
Section 7: Functional Java
-
Section 8: Concurrency
-
Section 9: I/O and NIO
-
Section 10: JVM Internals
-
Section 11: Modern Java Features
-
Section 12: Build Tools and Project Structure
-
Section 13: Testing
-
Section 14: Databases and Persistence
-
Section 15: Networking
-
Section 16: Design and Best Practices
-
Section 17: Reflection and Annotations
-
Section 18: Logging and Diagnostics
-
Section 19: Date, Time, and Internationalization
-
Section 20: Java Platform Module System
-
Section 21: Security in Java
-
Section 22: Advanced Collections and Data Structures
-
Section 23: More Concurrency Patterns
-
Section 24: Compression, Files, and System Integration
-
Section 25: GUI Programming
-
Section 26: Practical Projects
-
Section 27: Data Structures and Algorithms
-
Section 28: Interview and Algorithm Practice
-
Section 29: JSON and Data Interchange
-
Section 30: More Concurrency Utilities
-
Section 31: More Collections and Streams Practice
-
Section 32: More File and System Programming
-
Section 33: Standard Library Deep Dive
-
Section 34: More Practice and Drills
-
Section 35: More Testing and Quality
-
Section 36: More Design Patterns and Architecture
-
Section 37: Career and Ecosystem
-
Section 38: More OOP and Architecture Practice
-
Section 39: More Enterprise Concepts
-
Section 40: Advanced JavaFX
-
Section 41: More Interview Practice
137: Entity Mapping Basics
Up until now, we've been treating our Java objects as temporary things that live and die with the application's runtime. But in the real world, data needs to persist. That's where Entity Mapping comes in. We're essentially telling Java: "This class isn't just a container for data; it's a blueprint for a table in my database."
I like to think of JPA (Java Persistence API) as a translator. It maps the object-oriented world—where we have classes and references—to the relational world, where we have tables and foreign keys. Let's build a simple project tracking system to see this in action.
Defining our Project entity
First, let's create a Project class. To make it an entity, we need the @Entity annotation. This tells the JPA provider (like Hibernate) that this class should be mapped to a database table. I'll also give it a unique ID, because every row in a relational table needs a primary key.
import jakarta.persistence.*;
import java.time.LocalDate;
@Entity
public class Project {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private LocalDate deadline;
public Project(String name, LocalDate deadline) {
this.name = name;
this.deadline = deadline;
}
// Getters and setters...
}
Pretty straightforward, right? I've used GenerationType.IDENTITY here because I want the database to handle the ID incrementing for me. It's cleaner than trying to manage unique IDs manually in Java code.
The 'No-Args' stumbling block
Now, if I tried to run this right now, I'd hit a wall. I'd get a HibernateException complaining that the entity doesn't have a default constructor. I make this mistake more often than I'd like to admit.
Here's the deal: when JPA retrieves a record from the database, it doesn't use your custom constructor. It uses reflection to instantiate the object first and then populates the fields using the setters or direct field access. If there's no no-argument constructor, the whole process crashes.
I'll add a protected no-args constructor. I make it protected so that other parts of my application can't accidentally create "empty" projects, but JPA can still get in there and do its job:
// Inside the Project class
protected Project() {
// Required by JPA
}
Connecting Projects to Tasks
A project without tasks is just a wish list. To make this useful, I need a Task entity. This is where mapping gets interesting because we're moving from simple fields to relationships. One project will have many tasks.
I'll create the Task entity first, then link it back to the Project. I'm using @ManyToOne here because, from the perspective of a single task, it belongs to exactly one project.
@Entity
public class Task {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String description;
private boolean isCompleted;
@ManyToOne
@JoinColumn(name = "project_id")
private Project project;
protected Task() {}
public Task(String description, Project project) {
this.description = description;
this.project = project;
}
// Getters and setters...
}
The @JoinColumn is key. It tells the database to create a column called project_id in the Task table to store the foreign key. If I left that out, JPA would try to create a join table, which is overkill for a simple one-to-many relationship. I prefer keeping the schema lean.
Finally, if I want to be able to call project.getTasks(), I can add a @OneToMany list to the Project class, using the mappedBy attribute to tell JPA that the Task entity is the one actually controlling the relationship.
📋 Practical Task
Mapping a Customer and Order Relationship
Now it's your turn to apply these concepts. You need to build a small persistence model for an e-commerce system.
- Create a
Customerentity with anid,fullName, andemail. - Create an
Orderentity with anid,orderDate, andtotalAmount. - Establish a relationship where one
Customercan have multipleOrders, but eachOrderbelongs to only oneCustomer. - Ensure both entities have the necessary constructors to avoid the reflection errors we discussed.
- Use
@JoinColumnto explicitly name the foreign key in theOrdertable ascustomer_id.
There are no comments for now.