Sitemap

Record vs JPA Entity

4 min readJun 7, 2026

--

Here’s a deep dive comparison: Java record vs JPA @Entity, structured in an interview + real-world design perspective, tailored to your experience level.

1. Conceptual Difference (Core Idea)

Press enter or click to view image in full size

Key takeaway

  • Record → data representation
  • Entity → database representation

2. Mutability & State Management

Record (Immutable)

public record UserDTO(Long id, String name) {}

  • Fields are final
  • No setters
  • Immutable → thread-safe by design
  • State cannot change after creation

JPA Entity (Mutable)

@Entity
public class User {

@Id
private Long id;
private String name;

public void setName(String name) {
this.name = name;
}
}
  • Mutable fields
  • Managed lifecycle (dirty checking, updates)
  • ORM modifies the state internally

Why this matters

  • JPA relies on changing state (dirty checking) → incompatible with immutability

3. Identity vs Value Semantics

Record → Value-based equality

UserDTO u1 = new UserDTO(1L, "John");
UserDTO u2 = new UserDTO(1L, "John");
System.out.println(u1.equals(u2)); // true

Entity → Identity-based equality

User e1 = entityManager.find(User.class, 1L);
User e2 = entityManager.find(User.class, 1L);
System.out.println(e1 == e2); // true (same persistence context)

Equality is tricky in entities:

  • Should be based on ID
  • Before persistence → ID is null → equals issues

Insight

  • Records are pure values
  • Entities represent database identity

4. JPA Requirements (Why Record Fails as Entity)

JPA entity requires:

No-arg constructor
Non-final fields
Proxy support (Hibernate uses subclassing/bytecode enhancement)
Setters or field access
Lifecycle mutations allowed

Get Syed Nazeer’s stories in your inbox

Join Medium for free to get updates from this writer.

Record violates:

  • Fields are final
  • No no-arg constructor
  • Cannot be proxied
  • Immutable → no dirty checking

Therefore, Records cannot be used as JPA Entities (in standard JPA/Hibernate)

5. Lifecycle & Persistence Context

Entity Lifecycle

Transient → Managed → Detached → Removed
  • Managed by EntityManager
  • Changes automatically tracked (dirty checking)
  • Lazy loading supported

Record Lifecycle

Created → Used → Garbage collected
  • No lifecycle management
  • No ORM awareness
  • No lazy loading

6. Lazy Loading & Proxies

Entity Example

@ManyToOne(fetch = FetchType.LAZY)
private Order order;
  • Hibernate creates a proxy
  • Load on demand

Record Problem

public record User(Long id, Order order) {}

Cannot:

  • Replace with proxy
  • Inject lazy loader
  • Modify the field later

Records break lazy loading completely

7. Use Cases (Real-World Architecture)

>Where to use Record

1. API DTOs

public record UserResponse(Long id, String name) {}

2. Projection queries (Spring Data JPA)

@Query("SELECT new com.example.UserDTO(u.id, u.name) FROM User u")
List<UserDTO> fetchUsers();

3. Immutable Value Objects

public record Money(BigDecimal amount, String currency) {}

4. Event payloads (Kafka, REST)

public record OrderPlacedEvent(Long orderId, Instant timestamp) {}

>Where to use Entity

1. Domain models

@Entity
class Order {

@Id
Long id;

@OneToMany
List<OrderItem> items;

}

2. Aggregates (DDD)

  • Managed consistency
  • Transaction-aware updates

3. ORM interactions

  • Lazy loading
  • Dirty checking
  • Cascading

8. Performance & Memory

Press enter or click to view image in full size

Records are ideal for:

  • Read-heavy operations
  • API layer
  • Microservices response models

9. Best Practice: Combine Both (Modern Architecture)

Recommended pattern (very important for interviews)

Entity + Record DTO separation

@Entity
class User {
@Id
Long id;
String name;
}

public record UserDTO(Long id, String name) {
public static UserDTO from(User user) {
return new UserDTO(user.getId(), user.getName());
}
}

With Spring Boot Mapping

@RestController
class UserController {

@GetMapping("/users/{id}")
public UserDTO getUser(@PathVariable Long id) {
User user = repository.findById(id).orElseThrow();
return new UserDTO(user.getId(), user.getName());
}

}

10. Advanced Insight (Senior-Level Discussion)

Can Records be used with JPA at all?

Partial support:

Constructor-based projections

public record UserDTO(Long id, String name) {}

Works with JPQL:

SELECT new com.example.UserDTO(u.id, u.name)

Experimental (Hibernate)

  • Some experiments exist for immutable entities
  • Still not standard / production-friendly

DDD Perspective

11. Common Interview Questions

Can we use Record as an Entity?

No (due to immutability, proxies, lifecycle)

When should you prefer Record over Entity?

When:

  • No persistence needed
  • Immutable data required
  • Used for API communication

Why does JPA require mutability?

Because of:

  • Dirty checking
  • Lazy loading
  • Proxy injection

Is Record good for microservices?

Yes (DTO layer, contracts, events)

Final Summary

Press enter or click to view image in full size

Final Architecture Recommendation

Never replace JPA Entity with Record

Instead:

Entity (DB layer)  →  Record DTO (API layer)

--

--

Syed Nazeer
Syed Nazeer

Written by Syed Nazeer

I'm a Java enthusiast, developing passion for technical content creation.