ป้ายกำกับ: Many-to-Many

Spring Security: EntitySpring Security: Entity

เพื่อให้สอดคล้องกับโครงสร้างตารางแบบ Laravel Jetstream + Spatie ที่เราวางแผนไว้ใน Flyway Migration ไฟล์ Entity ใน Spring Boot จะต้องใช้การ Mapping แบบ Many-to-Many โดยมีรายละเอียดดังนี้ครับ


Permission Entity

ตัวนี้จะเก็บสิทธิ์ย่อย ๆ เช่น post-create, user-delete
src/main/java/com/example/demo/entity/Permission.java

package com.example.demo.entity;

import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
import java.util.Set;

@Entity
@Table(name = "permissions")
@Getter @Setter
public class Permission {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(unique = true, nullable = false)
    private String name;

    @Column(name = "guard_name")
    private String guardName = "web";

    @ManyToMany(mappedBy = "permissions")
    private Set<Role> roles;
}

Role Entity

ตัวนี้จะเชื่อมต่อไปยัง Permission แบบ Many-to-Many ( เหมือน role_has_permissions ใน Laravel )
src/main/java/com/example/demo/entity/Role.java

package com.example.demo.entity;

import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
import java.util.Set;

@Entity
@Table(name = "roles")
@Getter @Setter
public class Role {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(unique = true, nullable = false)
    private String name;

    @Column(name = "guard_name")
    private String guardName = "web";

    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(
        name = "role_has_permissions",
        joinColumns = @JoinColumn(name = "role_id"),
        inverseJoinColumns = @JoinColumn(name = "permission_id")
    )
    private Set<Permission> permissions;
}

User Entity

ตัวนี้จะเก็บข้อมูลผู้ใช้และเชื่อมกับ Role ( เหมือน model_has_roles ใน Laravel )
src/main/java/com/example/demo/entity/User.java

package com.example.demo.entity;

import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDateTime;
import java.util.Set;

@Entity
@Table(name = "users")
@Getter @Setter
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(unique = true, nullable = false)
    private String username;

    @Column(nullable = false)
    private String password;

    @Column(unique = true)
    private String email;

    @Column(name = "full_name", columnDefinition = "nvarchar(255)")
    private String fullName;

    @Column(name = "profile_photo_path", columnDefinition = "nvarchar(2048)")
    private String profilePhotoPath;

    @Column(name = "created_at")
    private LocalDateTime createdAt;

    @Column(name = "updated_at")
    private LocalDateTime updatedAt;

    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(
        name = "model_has_roles",
        joinColumns = @JoinColumn(name = "model_id"),
        inverseJoinColumns = @JoinColumn(name = "role_id")
    )
    private Set<Role> roles;

    @PrePersist
    protected void onCreate() {
        createdAt = LocalDateTime.now();
    }

    @PreUpdate
    protected void onUpdate() {
        updatedAt = LocalDateTime.now();
    }
}

ข้อควรทราบเพิ่มเติม

  • เปลี่ยน com.example.demo เป็นชื่อ Package จริงที่คุณตั้งไว้ในโปรเจกต์
  • Lombok: ผมใช้ @Getter และ @Setter จาก Library Lombok เพื่อให้ Code ดูสะอาด ( ถ้าโปรเจกต์คุณไม่ได้ลงไว้ ให้ใช้การ Generate Getter/Setter ปกติแทนครับ )
  • Naming Strategy: ใน Java มักใช้ camelCase ( เช่น fullName ) แต่ใน Database SQL Server ผมตั้งชื่อให้เป็น snake_case ( เช่น full_name ) ตามสไตล์ Laravel โดยใช้ @Column(name = "...") เป็นตัวเชื่อม
  • FetchType.EAGER: ตั้งค่าไว้ที่ Roles เพื่อให้เวลาโหลด User มาแล้ว สามารถดึงสิทธิ์มาเช็คใน Security Context ได้ทันที ( คล้ายกับ $user->load('roles') ใน Laravel )