Spring Boot | Spring Data JPA | Transactional
จากบทความก่อนหน้านี้ https://zengcode.medium.com/spring-boot-spring-data-jpa-pagination-and-sorting-1e1ab5b4f6f4
เราได้พูดถึงการใช้งาน Spring Data JPA with Pagination & Sorting ไปกันแล้วสำหรับตอนนี้เราจะเอา project ที่สร้างก่อนหน้านี้ เอามาพัฒนาต่อนะครับ เราจะ
เราสามารถสร้าง Transaction ง่ายๆ ด้วยแค่ เพิ่ม annotate bean ด้วย @Transactional ทั้งระดับ class หรือ method ได้เลย
ซึ่ง annotate ข้างต้นยัง supports สิ่งต่างๆ เหล่านี้ด้วย:
- Propagation Type of the transaction
- Isolation Level of the transaction
- Timeout for the operation wrapped by the transaction
- readOnly flag — a hint for the persistence provider that the transaction should be read only
- Rollback rules for the transaction
สำหรับบทความนี้เราจะเริ่มต้นกันง่ายๆ นะครับ จะใช้ค่า Default สำหรับบทก่อนเรามี
และ Repository สำหรับ CRUD มัน
ต่อไปเรามาสร้าง class Province เพื่อเก็บที่อยู่จังหวัดของ Customer เราจะยังไม่สร้างความสัมพันธ์ใดๆ ทั้งสิ้นนะครับ ตอนนี้เราจะมาดูการทำงานของ Transaction เบื้องต้นกันเท่านั้น
package com.zencode.jpa.jpaexample.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
@Data
@Entity
@Table(name = "provinces")
@NoArgsConstructor
@AllArgsConstructor
public class Province {
@Id
@GeneratedValue
private int id;
private int customerId;
private String provinceName;
}
สร้าง repository
package com.zencode.jpa.jpaexample.repository;
import com.zencode.jpa.jpaexample.entity.Province;
import org.springframework.data.jpa.repository.JpaRepository;
public interface ProvinceRepository extends JpaRepository<Province, Integer> {
}
เรามาแก้ไข CustomerService
package com.zencode.jpa.jpaexample.service;
import com.zencode.jpa.jpaexample.entity.Customer;
import com.zencode.jpa.jpaexample.entity.Province;
import com.zencode.jpa.jpaexample.repository.CustomerRepository;
import com.zencode.jpa.jpaexample.repository.ProvinceRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class CustomerService {
@Autowired
private CustomerRepository customerRepository;
@Autowired
private ProvinceRepository provinceRepository;
public void test() {
customerRepository.save(new Customer(1, "Chiwa Kantawong", 100));
if (1 ==1) {
throw new RuntimeException("Mock exception.");
}
provinceRepository.save(new Province(1, 1, "Phayao"));
}
}
แก้ controller ปล.เราจะมาทำอะไรกันง่ายๆ กันนะ ครับ
package com.zencode.jpa.jpaexample.controller;
import com.zencode.jpa.jpaexample.service.CustomerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class CustomerController {
@Autowired
private CustomerService customerService;
@GetMapping("/test")
public void test() {
customerService.test();
}
}
ลองรันกันครับ
ลองเรียก http://localhost:8080/test
ไปดูซิว่า Database จะมี Customer ไหม
มีเนอะ แต่ว่า Province ไม่ควรจะมีนะเพราะว่า Exception ก่อน
ต่อไปเราจะมาสร้าง Transaction กันคือต้อง insert ทั้งคู่สำเร็จ
package com.zencode.jpa.jpaexample.service;
import com.zencode.jpa.jpaexample.entity.Customer;
import com.zencode.jpa.jpaexample.entity.Province;
import com.zencode.jpa.jpaexample.repository.CustomerRepository;
import com.zencode.jpa.jpaexample.repository.ProvinceRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class CustomerService {
@Autowired
private CustomerRepository customerRepository;
@Autowired
private ProvinceRepository provinceRepository;
@Transactional
public void test() {
customerRepository.save(new Customer(1, "Chiwa Kantawong", 100));
if (1 ==1) {
throw new RuntimeException("Mock exception.");
}
provinceRepository.save(new Province(1, 1, "Phayao"));
}
}
เราสร้าง method test() ให้เป็น Transaction method คือทุกอย่างจะต้องสำเร็จเป็นหนึ่งเดียวเพราะฉะนั้นเราจะ expected ว่าจะไม่มีข้อมูลเกิดขึ้นทั้งใน Customer และ Province Table นะครับ
รัน application ใหม่ครับ
เข้า http://localhost:8080/test
ต่อไปดู Database, Customer Table
ไม่มีครับ ไปดู Province
ก็ไม่มีครับ เป็นอันว่าเราทำ Transaction สำเร็จแล้ว ง่ายดายไหมครับ
เอาไว้มีเวลาจะหาตัวอย่างแบบอื่นๆ มาให้ดูกันต่อนะครับ