-
Notifications
You must be signed in to change notification settings - Fork 0
Add user and refresh token entities #10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
b8c2d4f
93edee3
72db2ee
c86e051
7a5465f
3213598
68a7215
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,83 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| package com.smartjam.smartjamapi.entity; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.time.Instant; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.UUID; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import jakarta.persistence.*; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.smartjam.smartjamapi.enums.RefreshTokenStatus; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import lombok.*; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.hibernate.annotations.CreationTimestamp; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.hibernate.annotations.UpdateTimestamp; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * JPA entity representing a refresh token record in the SmartJam platform. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * <p>Mapped to the {@code refresh_tokens} database table. Rather than storing the raw token string, only a | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * cryptographic hash of the token is persisted in {@link #tokenHash}. This prevents token replay attacks in the event | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * of a database compromise. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * <p>The lifecycle of a token is tracked via the {@link #status} field. Tokens start as | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * {@link RefreshTokenStatus#ACTIVE} and transition to {@link RefreshTokenStatus#INACTIVE} upon revocation or expiry. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * <p><b>Persistence exceptions:</b> Attempting to persist or merge an instance with a duplicate {@code tokenHash}, a | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * {@code null} required field, or a value that violates a column constraint will cause the JPA provider to throw a | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * {@link jakarta.persistence.PersistenceException} (typically wrapped by Spring as | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * {@link org.springframework.dao.DataIntegrityViolationException}). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Entity | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Table(name = "refresh_tokens") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial Add table indexes for token lifecycle query paths.
⚙️ Proposed schema tuning-@Table(name = "refresh_tokens")
+@Table(
+ name = "refresh_tokens",
+ indexes = {
+ `@Index`(name = "idx_refresh_tokens_user_status", columnList = "user_id,status"),
+ `@Index`(name = "idx_refresh_tokens_expires_at", columnList = "expires_at")
+ })📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Getter | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Setter | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @NoArgsConstructor | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public class RefreshTokenEntity { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** Unique identifier for the refresh token record, generated automatically as a UUID. */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Id | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @GeneratedValue(strategy = GenerationType.UUID) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private UUID id; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Cryptographic hash (e.g. SHA-256 hex digest) of the opaque refresh token string. Stored instead of the raw token | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * to mitigate replay attacks from a compromised database. Length is 64 characters, sufficient for a SHA-256 hex | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * digest. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Column(nullable = false, unique = true, length = 64) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private String tokenHash; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * The user to whom this refresh token belongs. Loaded lazily to avoid unnecessary joins when only token metadata is | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * needed. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @ManyToOne(fetch = FetchType.LAZY) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @JoinColumn(name = "user_id", nullable = false) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private UserEntity user; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** The point in time at which this refresh token expires and must no longer be accepted. */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Column(nullable = false, name = "expires_at") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private Instant expiresAt; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
men229 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * The current lifecycle status of this refresh token. Defaults to {@link RefreshTokenStatus#ACTIVE} upon creation. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Persisted as a {@link String} in the database. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Column(nullable = false) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Enumerated(EnumType.STRING) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private RefreshTokenStatus status = RefreshTokenStatus.ACTIVE; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Timestamp of when this refresh token record was first created. Set automatically by Hibernate on insert and never | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * updated afterwards. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @CreationTimestamp | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Column(name = "created_at", nullable = false, updatable = false) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private Instant createdAt; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Timestamp of the most recent update to this refresh token record. Updated automatically by Hibernate on every | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * merge/flush. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @UpdateTimestamp | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Column(name = "updated_at", nullable = false) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private Instant updatedAt; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,98 @@ | ||
| package com.smartjam.smartjamapi.entity; | ||
|
|
||
| import java.time.Instant; | ||
| import java.util.UUID; | ||
|
|
||
| import jakarta.persistence.*; | ||
| import jakarta.validation.constraints.Email; | ||
|
|
||
| import com.smartjam.smartjamapi.enums.Role; | ||
| import lombok.*; | ||
| import org.hibernate.annotations.CreationTimestamp; | ||
| import org.hibernate.annotations.UpdateTimestamp; | ||
|
|
||
men229 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| /** | ||
| * JPA entity representing a registered user in the SmartJam platform. | ||
| * | ||
| * <p>Mapped to the {@code users} database table. The primary key is a UUID generated automatically by the persistence | ||
| * provider. Equality and hash-code are based solely on {@link #id} to ensure correct behavior in JPA-managed | ||
| * collections and during entity detachment/reattachment cycles. | ||
| * | ||
| * <p>The {@code email} field is the unique login identifier. {@code nickname} is a non-unique display name. Passwords | ||
| * are never stored in plain text — only the hashed value is persisted in {@code passwordHash}. | ||
| * | ||
| * <p><b>Persistence exceptions:</b> Attempting to persist or merge an instance with a duplicate {@code email}, a | ||
| * {@code null} required field, or a value that violates a column constraint will cause the underlying JPA provider to | ||
| * throw a {@link jakarta.persistence.PersistenceException} (typically wrapped by Spring as | ||
| * {@link org.springframework.dao.DataIntegrityViolationException}). | ||
| */ | ||
| @Setter | ||
| @Getter | ||
| @NoArgsConstructor | ||
| @AllArgsConstructor | ||
| @Table(name = "users") | ||
| @Entity | ||
| @EqualsAndHashCode(onlyExplicitlyIncluded = true) | ||
| public class UserEntity { | ||
|
Comment on lines
+29
to
+36
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial Consider replacing The ♻️ Proposed alternative using `@Builder` `@Setter`
`@Getter`
`@NoArgsConstructor`
-@AllArgsConstructor
+@Builder
`@Table`(name = "users")
`@Entity`
`@EqualsAndHashCode`(onlyExplicitlyIncluded = true)
public class UserEntity {🤖 Prompt for AI Agents |
||
|
|
||
| /** Unique identifier for the user, generated automatically as a UUID. */ | ||
| @Id | ||
| @GeneratedValue(strategy = GenerationType.UUID) | ||
| @EqualsAndHashCode.Include | ||
| private UUID id; | ||
|
|
||
| /** Non-unique display name (nickname) of the user shown in the UI. Must not be {@code null}. */ | ||
| @Column(nullable = false) | ||
| private String nickname; | ||
men229 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| /** | ||
| * The user's email address, used as the unique login identifier. Must be a valid email format, non-null, and unique | ||
| * across all users. | ||
| */ | ||
| @Column(nullable = false, unique = true, length = 255) | ||
| private String email; | ||
|
|
||
| /** Bcrypt (or equivalent) hash of the user's password. The plain-text password is never stored. */ | ||
| @Column(name = "password_hash", nullable = false, length = 255) | ||
| private String passwordHash; | ||
|
|
||
| /** Optional first name of the user. */ | ||
| @Column(name = "first_name") | ||
| private String firstName; | ||
|
|
||
| /** Optional last name of the user. */ | ||
| @Column(name = "last_name") | ||
| private String lastName; | ||
|
|
||
| /** Optional URL pointing to the user's avatar image. Supports long URLs (up to 2048 characters). */ | ||
| @Column(name = "avatar_url", length = 2048) | ||
| private String avatarUrl; | ||
|
|
||
| /** | ||
| * The role of the user, determining their permissions within the platform. Defaults to {@link Role#STUDENT} for all | ||
| * newly created users. Persisted as a {@link String} in the database. | ||
| */ | ||
| @Enumerated(EnumType.STRING) | ||
| @Column(nullable = false) | ||
| private Role role = Role.STUDENT; | ||
|
|
||
| /** Optional Firebase Cloud Messaging token used to send push notifications to the user's device. */ | ||
| @Column(name = "fcm_token") | ||
| private String fcmToken; | ||
|
|
||
| /** | ||
| * Timestamp of when the user record was first created. Set automatically by Hibernate on insert and never updated | ||
| * afterwards. | ||
| */ | ||
| @CreationTimestamp | ||
| @Column(name = "created_at", nullable = false, updatable = false) | ||
| private Instant createdAt; | ||
|
|
||
| /** | ||
| * Timestamp of the most recent update to the user record. Updated automatically by Hibernate on every merge/flush. | ||
| */ | ||
| @UpdateTimestamp | ||
| @Column(name = "updated_at", nullable = false) | ||
| private Instant updatedAt; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| package com.smartjam.smartjamapi.enums; | ||
|
|
||
Satlykovs marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| /** | ||
| * Represents the lifecycle status of a refresh token stored in the database. | ||
| * | ||
| * <p>Status values are persisted as {@link String} via {@link jakarta.persistence.EnumType#STRING} in the | ||
| * {@code refresh_tokens} table. | ||
| */ | ||
| public enum RefreshTokenStatus { | ||
| /** The refresh token is valid and can be used to obtain a new access token. */ | ||
| ACTIVE, | ||
| /** | ||
| * The refresh token is no longer valid and cannot be used for token refresh. This covers tokens that have been | ||
| * revoked, used, or expired. | ||
| */ | ||
| INACTIVE | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| package com.smartjam.smartjamapi.enums; | ||
|
|
||
men229 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| /** | ||
| * Represents the role of a user within the SmartJam platform. | ||
| * | ||
| * <p>Roles are used to control access and permissions across the application. The role value is persisted as a {`@link` | ||
| * String} in the database via {`@link` jakarta.persistence.EnumType#STRING}. | ||
| */ | ||
| public enum Role { | ||
| STUDENT, | ||
| TEACHER | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| package com.smartjam.smartjamapi.repository; | ||
|
|
||
| import java.util.Optional; | ||
| import java.util.UUID; | ||
|
|
||
| import com.smartjam.smartjamapi.entity.RefreshTokenEntity; | ||
| import com.smartjam.smartjamapi.entity.UserEntity; | ||
| import com.smartjam.smartjamapi.enums.RefreshTokenStatus; | ||
| import org.springframework.dao.DataAccessException; | ||
| import org.springframework.data.jpa.repository.JpaRepository; | ||
| import org.springframework.data.jpa.repository.Modifying; | ||
| import org.springframework.data.jpa.repository.Query; | ||
| import org.springframework.data.repository.query.Param; | ||
| import org.springframework.transaction.annotation.Transactional; | ||
|
|
||
men229 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| /** | ||
| * Spring Data JPA repository for {@link RefreshTokenEntity} persistence operations. | ||
| * | ||
| * <p>In addition to standard CRUD operations inherited from {@link JpaRepository}, this repository provides token-hash | ||
| * lookups and bulk JPQL {@code UPDATE} statements for efficient status transitions without loading full entity graphs. | ||
| * | ||
| * <p>All {@link Modifying} methods use {@code clearAutomatically = true} and {@code flushAutomatically = true} to | ||
| * ensure the persistence context is flushed before and cleared after each bulk update, preventing stale first-level | ||
| * cache entries from being read after the operation. | ||
| */ | ||
| public interface RefreshTokenRepository extends JpaRepository<RefreshTokenEntity, UUID> { | ||
|
|
||
| /** | ||
| * Finds a refresh token record by the hash of the raw token string. | ||
| * | ||
| * <p> | ||
| * | ||
| * @param tokenHash the SHA-256 hex digest (or equivalent hash) of the refresh token; must not be {@code null} | ||
| * @return an {@link Optional} containing the matching {@link RefreshTokenEntity}, or {@link Optional#empty()} if no | ||
| * record with the given hash exists | ||
| * @throws IllegalArgumentException if {@code token} is {@code null} | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix Javadoc parameter name in The method parameter is ✏️ Proposed fix- * `@throws` IllegalArgumentException if {`@code` token} is {`@code` null}
+ * `@throws` IllegalArgumentException if {`@code` tokenHash} is {`@code` null}🤖 Prompt for AI Agents |
||
| * @throws DataAccessException if a database access error occurs (e.g. connection failure, query execution error) | ||
| */ | ||
| Optional<RefreshTokenEntity> findByTokenHash(String tokenHash); | ||
|
|
||
| /** | ||
| * Updates the status of the refresh token identified by the given token hash. | ||
| * | ||
| * <p>This is a bulk JPQL {@code UPDATE} that does not load the entity into the persistence context, making it more | ||
| * efficient than a find-then-save pattern. | ||
| * | ||
| * <p> | ||
| * | ||
| * @param tokenHash the SHA-256 hex digest of the refresh token whose status should be updated; must not be | ||
| * {@code null} | ||
| * @param status the new {@link RefreshTokenStatus} to set; must not be {@code null} | ||
| * @return the number of rows affected by the update (0 or 1) | ||
| * @throws IllegalArgumentException if {@code tokenHash} or {@code status} is {@code null} | ||
| * @throws org.springframework.dao.DataAccessException if a database access error occurs during the update | ||
| * @throws jakarta.persistence.TransactionRequiredException if called outside an active transaction (normally | ||
| * prevented by the {@code @Transactional} annotation on this method) | ||
| */ | ||
| @Transactional | ||
| @Modifying(clearAutomatically = true, flushAutomatically = true) | ||
| @Query("UPDATE RefreshTokenEntity r SET r.status = :status WHERE r.tokenHash = :tokenHash") | ||
men229 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| int setStatusByTokenHash(@Param("tokenHash") String tokenHash, @Param("status") RefreshTokenStatus status); | ||
|
|
||
| /** | ||
| * Bulk-updates the status of all refresh tokens belonging to the given user whose current status matches | ||
| * {@code currentStatus}. | ||
| * | ||
| * <p>Typically used to invalidate all active tokens for a user on logout or password change, by transitioning them | ||
| * from {@link RefreshTokenStatus#ACTIVE} to {@link RefreshTokenStatus#INACTIVE}. | ||
| * | ||
| * <p> | ||
| * | ||
| * @param user the owner {@link UserEntity} whose tokens should be updated; must not be {@code null} | ||
| * @param currentStatus the status that tokens must currently have to be included in the update; must not be | ||
| * {@code null} | ||
| * @param newStatus the new {@link RefreshTokenStatus} to assign; must not be {@code null} | ||
| * @return the number of rows affected by the update | ||
| * @throws IllegalArgumentException if any of {@code user}, {@code currentStatus}, or {@code newStatus} is | ||
| * {@code null} | ||
| * @throws org.springframework.dao.DataAccessException if a database access error occurs during the update | ||
| * @throws jakarta.persistence.TransactionRequiredException if called outside an active transaction (normally | ||
| * prevented by the {@code @Transactional} annotation on this method) | ||
| */ | ||
| @Transactional | ||
| @Modifying(clearAutomatically = true, flushAutomatically = true) | ||
| @Query(""" | ||
| UPDATE RefreshTokenEntity r | ||
| SET r.status = :newStatus | ||
| WHERE r.user = :user | ||
| AND r.status = :currentStatus | ||
| """) | ||
| int updateStatusByUserAndCurrentStatus( | ||
| @Param("user") UserEntity user, | ||
| @Param("currentStatus") RefreshTokenStatus currentStatus, | ||
| @Param("newStatus") RefreshTokenStatus newStatus); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| package com.smartjam.smartjamapi.repository; | ||
|
|
||
| import java.util.Optional; | ||
| import java.util.UUID; | ||
|
|
||
| import com.smartjam.smartjamapi.entity.UserEntity; | ||
| import org.springframework.data.jpa.repository.JpaRepository; | ||
|
|
||
men229 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| /** | ||
| * Spring Data JPA repository for {@link UserEntity} persistence operations. | ||
| * | ||
| * <p>Provides standard CRUD operations inherited from {@link JpaRepository}, as well as derived query methods for | ||
| * email-based lookups used during authentication and registration flows. | ||
| */ | ||
| public interface UserRepository extends JpaRepository<UserEntity, UUID> { | ||
|
|
||
| /** | ||
| * Finds a user by their email address. | ||
| * | ||
| * <p> | ||
| * | ||
| * @param email the email address to search for; must not be {@code null} | ||
| * @return an {@link Optional} containing the matching {@link UserEntity}, or {@link Optional#empty()} if no user | ||
| * with the given email exists | ||
| * @throws IllegalArgumentException if {@code email} is {@code null} | ||
| * @throws org.springframework.dao.DataAccessException if a database access error occurs (e.g. connection failure, | ||
| * query execution error); this is a Spring-translated unchecked exception wrapping the underlying | ||
| * {@link jakarta.persistence.PersistenceException} | ||
| */ | ||
| Optional<UserEntity> findByEmail(String email); | ||
|
|
||
| /** | ||
| * Checks whether a user with the given email address already exists. | ||
| * | ||
| * <p>Prefer this method over {@link #findByEmail(String)} when only presence needs to be verified, as it avoids | ||
| * loading the full entity. | ||
| * | ||
| * <p> | ||
| * | ||
| * @param email the email address to check; must not be {@code null} | ||
| * @return {@code true} if a user with the specified email exists, {@code false} otherwise | ||
| * @throws IllegalArgumentException if {@code email} is {@code null} | ||
| * @throws org.springframework.dao.DataAccessException if a database access error occurs (e.g. connection failure, | ||
| * query execution error) | ||
| */ | ||
| boolean existsByEmail(String email); | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.