User.java

package com.seebie.server.entity;

import com.github.f4b6a3.uuid.UuidCreator;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;

import java.io.Serializable;
import java.time.Instant;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;

@Entity
@Table(name = "app_user", uniqueConstraints = {@UniqueConstraint(columnNames = {"public_id"})})
public class User implements Serializable {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name = "id", updatable = false, insertable = false, nullable = false)
    private Long id;

    @Column(unique = true, name = "public_id", updatable = false, insertable = true, nullable = false)
    private UUID publicId;

    @NotNull
    private String password = "";

    @NotNull
    @Column(unique=true)
    @Size(min = 3, message = "must be at least three characters")
    private String email = "";

    @NotNull
    @Size(min = 3, message = "must be at least three characters")
    @Column(name="display_name")
    private String displayName = "";

    @Basic
    @NotNull
    private Instant registrationTime;

    @ElementCollection(fetch=FetchType.LAZY, targetClass=Role.class)
    @CollectionTable(name = "user_role", joinColumns = @JoinColumn(name = "user_id"))
    @Column(name = "role_id")
    @Enumerated(EnumType.ORDINAL)
    private Set<Role> roles = new HashSet<>();

    // If we start using more user settings, this could be moved to a settings entity.
    @Basic
    private boolean notificationsEnabled = false;

    /**
     * Since this field isn't a primary key, we can't use Hibernate's generator annotations.
     * Once Postgres supports UUID v7 natively (in PG 18)
     * we can just make that the default value on the column and remove this.
     * Alternatively if we migrate the publicId to be the PK, we can use the generator annotations.
     */
    @PrePersist
    public void ensurePublicIdUuidV7() {
        if (publicId == null) {
            publicId = UuidCreator.getTimeOrderedEpoch();
        }
    }

    protected User() {
        // no arg constructor is required by JPA
    }

    public User(String displayName, String email, String encryptedPassword) {
        this.displayName = displayName;
        this.email = email;
        this.password = encryptedPassword;
        this.registrationTime = Instant.now();
        this.roles.add(Role.USER);
    }

    public User withUserData(String displayName, boolean notificationsEnabled) {
        this.displayName = displayName;
        this.notificationsEnabled = notificationsEnabled;
        return this;
    }

    public UUID getPublicId() {
        return publicId;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getEmail() {
        return email;
    }

    public String getDisplayName() {
        return displayName;
    }

    public Set<Role> getRoles() {
        return roles;
    }

    public Instant getRegistrationTime() {
        return registrationTime;
    }

    public boolean isNotificationsEnabled() {
        return notificationsEnabled;
    }

}