Mapping Entities with JPA: @Entity, @Table, and @Id Explained

Illustration for Mapping Entities with JPA: @Entity, @Table, and @Id Explained
By Last updated:

Entity mapping is the foundation of JPA (Java Persistence API). Without properly mapped entities, JPA cannot synchronize your Java objects with relational database tables. The annotations @Entity, @Table, and @Id form the backbone of this mapping.

In this tutorial, we’ll explore entity mapping step by step, with practical examples, configuration details, common pitfalls, and best practices.


What is an Entity in JPA?

  • An Entity is a lightweight Java object representing a table in a relational database.
  • Each instance of the entity corresponds to a row in the table.
  • Entities must have a primary key to uniquely identify rows.

Analogy: Think of an Entity as a student record in a classroom register. Each student (row) has unique roll number (primary key), and the register (table) stores all of them.


Configuring persistence.xml

Place this file under src/main/resources/META-INF/.

<persistence xmlns="https://jakarta.ee/xml/ns/persistence" version="3.0">
  <persistence-unit name="examplePU">
    <class>com.example.model.User</class>
    <properties>
      <property name="jakarta.persistence.jdbc.driver" value="org.h2.Driver"/>
      <property name="jakarta.persistence.jdbc.url" value="jdbc:h2:mem:testdb"/>
      <property name="jakarta.persistence.jdbc.user" value="sa"/>
      <property name="jakarta.persistence.jdbc.password" value=""/>
      <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
      <property name="hibernate.hbm2ddl.auto" value="update"/>
      <property name="hibernate.show_sql" value="true"/>
    </properties>
  </persistence-unit>
</persistence>

Mapping with @Entity, @Table, and @Id

Basic Example

import jakarta.persistence.*;

@Entity
@Table(name = "users")
public class User {

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

    @Column(nullable = false)
    private String name;

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

    // Constructors, Getters & Setters
}

Key Annotations

  • @Entity → Marks the class as a JPA entity.
  • @Table(name = "users") → Maps the entity to a specific table.
  • @Id → Marks the primary key.
  • @GeneratedValue(strategy = GenerationType.IDENTITY) → Auto-generates IDs.
  • @Column → Defines column constraints.

Mapping Relationships

One-to-Many and Many-to-One Example

@Entity
@Table(name = "orders")
public class Order {

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

    private String product;

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

CRUD Operations with EntityManager

EntityManagerFactory emf = Persistence.createEntityManagerFactory("examplePU");
EntityManager em = emf.createEntityManager();

// Create
em.getTransaction().begin();
User user = new User();
user.setName("Alice");
user.setEmail("alice@example.com");
em.persist(user);
em.getTransaction().commit();

// Read
User found = em.find(User.class, user.getId());

// Update
em.getTransaction().begin();
found.setName("Alice Updated");
em.merge(found);
em.getTransaction().commit();

// Delete
em.getTransaction().begin();
em.remove(found);
em.getTransaction().commit();

em.close();
emf.close();

Queries with Mapped Entities

JPQL

List<User> users = em.createQuery("SELECT u FROM User u WHERE u.name = :name", User.class)
                     .setParameter("name", "Alice")
                     .getResultList();

Criteria API

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<User> query = cb.createQuery(User.class);
Root<User> root = query.from(User.class);
query.select(root).where(cb.equal(root.get("email"), "alice@example.com"));
List<User> results = em.createQuery(query).getResultList();

Native SQL

List<Object[]> results = em.createNativeQuery("SELECT * FROM users").getResultList();

Fetching Strategies

  • Lazy (default) → Loads data only when accessed.
  • Eager → Loads immediately.
@OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
private List<Order> orders;

Analogy: Lazy fetching = ordering food when hungry; eager fetching = ordering the buffet upfront.


Performance Considerations

  • Always define indexes in your database for frequently queried fields.
  • Use DTO projections for read-only queries.
  • Beware of the N+1 problem with lazy fetching.

Common Pitfalls

  • Missing @Id annotation → Entity will fail to map.
  • Overusing eager fetching → Performance bottlenecks.
  • Using cascade incorrectly → Unintended deletes or updates.

Best Practices

  • Always define a primary key with @Id.
  • Prefer lazy fetching with selective eager loading.
  • Use @Column for constraints like nullable, unique, and length.
  • Keep entity classes clean and POJO-style (only fields + getters/setters).

📌 JPA Version Notes

  • JPA 2.0 → Introduced Criteria API, Metamodel.
  • JPA 2.1 → Added stored procedures, entity graphs.
  • Jakarta Persistence (EE 9/10/11) → Migrated from javax.persistencejakarta.persistence.

Real-World Integration

Spring Boot

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    List<User> findByEmail(String email);
}

Jakarta EE

@Stateless
public class UserService {
    @PersistenceContext
    private EntityManager em;

    public void saveUser(User user) {
        em.persist(user);
    }
}

Conclusion and Key Takeaways

  • Entities map Java objects to database tables.
  • Use @Entity, @Table, and @Id as the foundation of JPA mappings.
  • Relationships (@OneToMany, @ManyToOne) extend entity functionality.
  • Proper fetching strategy and query optimization are key to performance.

FAQ

1. What’s the difference between JPA and Hibernate?
JPA is a specification; Hibernate is an implementation.

2. How does JPA handle persistence context?
It manages entity states and keeps them synced with the DB.

3. What are drawbacks of eager fetching?
It loads unnecessary data, reducing performance.

4. How can I solve the N+1 problem?
Use JOIN FETCH or entity graphs.

5. Can I use JPA without Hibernate?
Yes, with providers like EclipseLink and OpenJPA.

6. What’s the best inheritance mapping strategy?
SINGLE_TABLE for speed, JOINED for normalization.

7. How does JPA handle composite keys?
Using @EmbeddedId or @IdClass.

8. What changes with Jakarta Persistence?
Namespace moved from javax.persistence to jakarta.persistence.

9. Is JPA suitable for microservices?
Yes, but transactions should be short-lived.

10. When should I avoid JPA?
For high-performance analytics or NoSQL systems.