Mastering entity relation mapping in Java

Best way to map entity relations

Parent Perspective in Entity Mapping

Have you ever asked: “Should the parent own the relation, with cascade all, and the child DTO not even mention the parent?”

That’s often a good and clean approach, and here’s why.


When the Parent Owns the Relation

@Entity
public class Person {
    @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true)
    @JoinColumn(name = "passport_id")
    private Passport passport;
}

Pros:

  • Clear lifecycle control: When you persist or delete the parent, the child follows.
  • Simpler DTOs: Child DTO doesn’t need a back-reference to parent, reducing nesting.
  • Works great when child is tightly coupled to the parent (e.g., Passport can’t exist without Person).

DTO Example (clean):

public record PersonDto(Long id, String name, PassportDto passport) {}
public record PassportDto(String number, LocalDate issueDate) {}

No need for PersonDto inside PassportDto.


When to Use This (and Cascade ALL)

  • Child only exists with the parent
  • You’re creating/updating data mostly via parent
  • You want to reduce back-reference clutter and circular mapping
  • You don’t care about child-to-parent navigation (e.g., passport.getPerson())

⚠️ When the Child Should Keep a Parent Reference

@Entity
public class Passport {
    @OneToOne
    @JoinColumn(name = "person_id")
    private Person person;
}

Use only when:

  • Child needs context of parent (e.g., audit, filtering, backrefs)
  • You often load children independently and need to reach the parent
  • You’re modeling a bidirectional graph for complex navigation

In DTOs, this can lead to recursive structures or unnecessary nesting:

public record PassportDto(String number, LocalDate issueDate, PersonDto person) {}

Which can get ugly, especially in JSON responses.


Recommended Clean Design

Let the parent own the relationship, use cascade + orphan removal, and avoid child-to-parent references unless strictly necessary.

This way:

  • Your APIs stay flat and clean.
  • Your mappers don’t loop.
  • You don’t overfetch or serialize unintended stuff.

Bonus: @JsonIgnore and Mapping

If you must have a bidirectional entity model, you can still keep DTOs clean:

@Entity
public class Passport {
    @JsonIgnore
    @OneToOne(mappedBy = "passport")
    private Person person;
}

A test poc implementation can be found here.