最近碰到Spring Data JPA的地雷,明明沒有呼叫CrudRepository.save()
但資料卻更新。
原因是在@Transactional
的交易中查詢的實體(entity)是被EntityManager管理的狀態,此時對實體資料的異動在離開交易後會被自動提交到資料庫。
例如資料表EMPLOYEE
現有資料如下。
+----+------+
| ID | NAME |
+----+------+
| 1 | JOHN |
+----+------+
下面的EmployeeService.queryAndUpdateName()
上掛有@Transactional
,且其中並未呼叫EmployeeRepository.save()
儲存修改,但此方法結束後employee
的異動卻仍被更新到映射的資料表中。
EmployeeService
package com.abc.demo.service;
import com.abc.demo.entity.Employee;
import com.abc.demo.repository.EmployeeRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class EmployeeService {
@Autowired
private EmployeeRepository employeeRepository;
@Transactional
public void queryAndUpdateName(String name) {
Employee employee = employeeRepository.findById(1L).orElse(null);
if (employee != null) {
employee.setName(name); // update to database table
}
}
}
測試。
EmployeeServiceTests
package com.abc.demo.service;
import com.abc.demo.entity.Employee;
import com.abc.demo.repository.EmployeeRepository;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class EmployeeServiceTests {
@Autowired
private EmployeeService employeeService;
@Autowired
private EmployeeRepository employeeRepository;
@Test
public void queryAndUpdateName_successUpdate() {
String name = "Joe";
System.out.println(employeeRepository.findById(1L).map(Employee::getName).orElse("")); // John
employeeService.queryAndUpdateName(name);
String updatedName = employeeRepository.findById(1L)
.map(Employee::getName)
.orElse("");
System.out.println(updatedName); // Joe
Assertions.assertEquals(name, updatedName);
}
}
結論是使用@Transactional
要非常清楚其作用範圍與效果。
參考github。
沒有留言:
張貼留言