Skip to content

13 - JpaRepository

บทนี้จะสร้าง UserRepository เพื่อให้ service ติดต่อ database ผ่าน Spring Data JPA

หลังจบบทนี้ผู้อ่านควรเข้าใจ:

  • Repository layer มีหน้าที่อะไร
  • JpaRepository<User, Long> หมายความว่าอะไร
  • method พื้นฐานที่ Spring Data JPA ให้มา
  • การเขียน query จากชื่อ method เช่น findByEmail
  • ทำไม method ที่หา record เดียวควร return Optional<User>

Repository คือ layer ที่รับผิดชอบการคุยกับ database

ในระบบที่แยก layer ชัดเจน flow จะเป็น:

Controller -> Service -> Repository -> Database

Controller ไม่ควรรู้ว่า database เป็น PostgreSQL หรือ MySQL

Service ควรรู้ว่าจะทำ business flow อะไร

Repository ควรรู้ว่าจะอ่านหรือเขียนข้อมูลอย่างไร

สร้างไฟล์:

src/main/java/com/example/backendapi/repository/UserRepository.java
package com.example.backendapi.repository;
import com.example.backendapi.model.User;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByEmail(String email);
Optional<User> findByUsername(String username);
boolean existsByEmail(String email);
boolean existsByUsername(String username);
}

ไม่ต้องเขียน implementation เอง เพราะ Spring Data JPA จะสร้าง implementation ให้ตอน application start

JpaRepository<User, Long> แปลว่าอะไร

Section titled “JpaRepository<User, Long> แปลว่าอะไร”
JpaRepository<User, Long>

แปลว่า repository นี้ทำงานกับ:

ส่วนความหมาย
Userentity ที่ repository จัดการ
Longtype ของ primary key หรือ id

เพราะใน User entity เราประกาศ:

@Id
private Long id;

ดังนั้น generic ตัวที่สองจึงเป็น Long

เมื่อ extends JpaRepository เราจะได้ method พื้นฐานจำนวนมาก เช่น:

Methodใช้ทำอะไร
findAll()ดึงข้อมูลทั้งหมด
findById(id)ดึงข้อมูลตาม id
save(entity)insert หรือ update
delete(entity)ลบ entity
deleteById(id)ลบตาม id
existsById(id)เช็กว่ามี id นี้ไหม
count()นับจำนวน record

นี่คือเหตุผลที่ Spring Data JPA ทำให้ CRUD พื้นฐานเร็วมาก

Spring Data JPA อ่านชื่อ method แล้วสร้าง query ให้

ตัวอย่าง:

Optional<User> findByEmail(String email);

Spring จะตีความว่า:

select * from users where email = ?

ตัวอย่างอื่น:

boolean existsByUsername(String username);

Spring จะตีความว่า:

select count(*) > 0 from users where username = ?

การค้นหาด้วย id, email หรือ username อาจไม่เจอข้อมูล

ดังนั้น method เหล่านี้ควร return Optional<User>:

Optional<User> findByEmail(String email);

เวลาใช้ใน service จะเขียนได้ชัดเจน:

User user = userRepository.findByEmail(email)
.orElseThrow(() -> new RuntimeException("User not found"));

ความหมายคือ ถ้าเจอ user ให้ใช้ user นั้น ถ้าไม่เจอให้โยน error

Repository ไม่ควรใส่ business logic

Section titled “Repository ไม่ควรใส่ business logic”

Repository ควรทำงานกับ database เท่านั้น เช่น:

  • หา user จาก email
  • บันทึก user
  • ลบ user
  • เช็กว่า username ซ้ำไหม

ส่วน logic แบบนี้ควรอยู่ใน service:

  • สมัครสมาชิกได้หรือไม่
  • email ซ้ำควรตอบ error อะไร
  • admin เปลี่ยน role ได้หรือไม่
  • user ที่ INACTIVE ห้าม login หรือไม่

การแยกแบบนี้ทำให้ code โตขึ้นแล้วไม่ปนกัน

หลังสร้าง repository แล้ว application ควร start ได้โดยไม่มี error

ถ้าเห็น error ประมาณนี้:

Not a managed type: class com.example.backendapi.model.User

มักหมายความว่า User ไม่ได้เป็น @Entity หรืออยู่ผิด package จน Spring Boot scan ไม่เจอ

แบบฝึกหัดท้ายบท

Section titled “แบบฝึกหัดท้ายบท”
  1. สร้าง UserRepository
  2. เพิ่ม method findByEmail
  3. เพิ่ม method existsByUsername
  4. รัน application ให้ start สำเร็จ
  5. ลองตั้งชื่อ method ผิด เช่น findByEmailAddress แล้วดู error จาก Spring Data JPA