diff --git a/backend/src/main/java/org/raddatz/familienarchiv/dto/PersonNameAliasDTO.java b/backend/src/main/java/org/raddatz/familienarchiv/dto/PersonNameAliasDTO.java new file mode 100644 index 00000000..2ce8a04d --- /dev/null +++ b/backend/src/main/java/org/raddatz/familienarchiv/dto/PersonNameAliasDTO.java @@ -0,0 +1,9 @@ +package org.raddatz.familienarchiv.dto; + +import org.raddatz.familienarchiv.model.PersonNameAliasType; + +public record PersonNameAliasDTO( + String lastName, + String firstName, + PersonNameAliasType type +) {} diff --git a/backend/src/main/java/org/raddatz/familienarchiv/exception/ErrorCode.java b/backend/src/main/java/org/raddatz/familienarchiv/exception/ErrorCode.java index 5952dba5..b105df54 100644 --- a/backend/src/main/java/org/raddatz/familienarchiv/exception/ErrorCode.java +++ b/backend/src/main/java/org/raddatz/familienarchiv/exception/ErrorCode.java @@ -11,6 +11,8 @@ public enum ErrorCode { // --- Persons --- /** A person with the given ID does not exist. 404 */ PERSON_NOT_FOUND, + /** A person name alias with the given ID does not exist. 404 */ + ALIAS_NOT_FOUND, // --- Documents --- /** A document with the given ID does not exist. 404 */ diff --git a/backend/src/main/java/org/raddatz/familienarchiv/model/Person.java b/backend/src/main/java/org/raddatz/familienarchiv/model/Person.java index ce2595b5..3bd6f418 100644 --- a/backend/src/main/java/org/raddatz/familienarchiv/model/Person.java +++ b/backend/src/main/java/org/raddatz/familienarchiv/model/Person.java @@ -1,9 +1,12 @@ package org.raddatz.familienarchiv.model; +import com.fasterxml.jackson.annotation.JsonIgnore; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.persistence.*; import lombok.*; +import java.util.ArrayList; +import java.util.List; import java.util.UUID; @Entity @Table(name = "persons") @@ -35,4 +38,12 @@ public class Person { private Integer birthYear; private Integer deathYear; + + // Entity-graph navigation for JPA JOIN queries (e.g. DocumentSpecifications.hasText). + // Uses entity relationship rather than cross-domain repository access, avoiding a + // separate DB roundtrip while respecting domain boundaries. + @OneToMany(mappedBy = "person", cascade = CascadeType.ALL, orphanRemoval = true) + @JsonIgnore + @Builder.Default + private List nameAliases = new ArrayList<>(); } \ No newline at end of file diff --git a/backend/src/main/java/org/raddatz/familienarchiv/model/PersonNameAlias.java b/backend/src/main/java/org/raddatz/familienarchiv/model/PersonNameAlias.java new file mode 100644 index 00000000..0b63bead --- /dev/null +++ b/backend/src/main/java/org/raddatz/familienarchiv/model/PersonNameAlias.java @@ -0,0 +1,48 @@ +package org.raddatz.familienarchiv.model; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.persistence.*; +import lombok.*; +import org.hibernate.annotations.CreationTimestamp; + +import java.time.Instant; +import java.util.UUID; + +@Entity +@Table(name = "person_name_aliases") +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class PersonNameAlias { + + @Id + @GeneratedValue(strategy = GenerationType.UUID) + @Schema(requiredMode = Schema.RequiredMode.REQUIRED) + private UUID id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "person_id", nullable = false) + private Person person; + + @Column(name = "last_name", nullable = false) + @Schema(requiredMode = Schema.RequiredMode.REQUIRED) + private String lastName; + + @Column(name = "first_name") + private String firstName; + + @Enumerated(EnumType.STRING) + @Column(nullable = false) + @Schema(requiredMode = Schema.RequiredMode.REQUIRED) + private PersonNameAliasType type; + + @Column(name = "sort_order", nullable = false) + @Schema(requiredMode = Schema.RequiredMode.REQUIRED) + private Integer sortOrder; + + @CreationTimestamp + @Column(name = "created_at", updatable = false) + @Schema(requiredMode = Schema.RequiredMode.REQUIRED) + private Instant createdAt; +} diff --git a/backend/src/main/java/org/raddatz/familienarchiv/model/PersonNameAliasType.java b/backend/src/main/java/org/raddatz/familienarchiv/model/PersonNameAliasType.java new file mode 100644 index 00000000..38ad90cf --- /dev/null +++ b/backend/src/main/java/org/raddatz/familienarchiv/model/PersonNameAliasType.java @@ -0,0 +1,8 @@ +package org.raddatz.familienarchiv.model; + +public enum PersonNameAliasType { + BIRTH, + WIDOWED, + DIVORCED, + OTHER +} diff --git a/backend/src/main/java/org/raddatz/familienarchiv/repository/PersonNameAliasRepository.java b/backend/src/main/java/org/raddatz/familienarchiv/repository/PersonNameAliasRepository.java new file mode 100644 index 00000000..1f97860b --- /dev/null +++ b/backend/src/main/java/org/raddatz/familienarchiv/repository/PersonNameAliasRepository.java @@ -0,0 +1,16 @@ +package org.raddatz.familienarchiv.repository; + +import org.raddatz.familienarchiv.model.PersonNameAlias; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; + +import java.util.List; +import java.util.UUID; + +public interface PersonNameAliasRepository extends JpaRepository { + + List findByPersonIdOrderBySortOrderAscCreatedAtAsc(UUID personId); + + @Query("SELECT COALESCE(MAX(a.sortOrder), -1) FROM PersonNameAlias a WHERE a.person.id = :personId") + int findMaxSortOrder(UUID personId); +}