Spring Boot | Spring Data JPA | Transactional

Chiwa Kantawong (Pea)
3 min readAug 31, 2021

จากบทความก่อนหน้านี้ 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 สำเร็จแล้ว ง่ายดายไหมครับ

เอาไว้มีเวลาจะหาตัวอย่างแบบอื่นๆ มาให้ดูกันต่อนะครับ

--

--