Spring Data JPA的org.springframework.orm.jpa.JpaSystemException: identifier of an instance of Entity was altered from x to y
或
Hibernate的org.hibernate.HibernateException: identifier of an instance of Entity was altered from x to y
錯誤原因如下。
當修改實體類的@Id
欄位並呼叫Repository
儲存時就會出現此錯誤。
例如下面是實體類Employee
。
Employee
@Entity
public class Employee implements Serializable {
private static final Long serialVersionUID = 1L;
@Id
private long id;
private String name;
// getters and setters
}
執行下面程式查詢出資料並修改Employee.id
的值並呼叫EmployeeRepository.save()
儲存就會發生org.springframework.orm.jpa.JpaSystemException: identifier of an instance of com.abc.demo.entity.Employee was altered from x to y
錯誤。
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 Employee alterId() {
Employee employee = employeeRepository.findById(1L).orElse(null);
if (employee != null) {
employee.setId(2L);
return employeeRepository.save(employee); // 拋錯
}
return null;
}
...
}
Spring Data JPA / Hibernate不應該去修改實體類的@Id
欄位即主鍵(primary key)的值。如果真的有需要用JPA修改則可將原本的資料先刪除後再新增。或是使用原生sql修改。
EmployeeService
package com.abc.demo.service;
import com.abc.demo.entity.Employee;
import com.abc.demo.repository.EmployeeRepository;
import org.springframework.beans.BeanUtils;
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 Employee alterIdByCreateNew() {
Employee employee = employeeRepository.findById(1L).orElse(null);
if (employee != null) {
Employee employeeNew = new Employee();
BeanUtils.copyProperties(employee, employeeNew);
employeeNew.setId(2L);
employeeRepository.delete(employee);
return employeeRepository.save(employeeNew);
}
return null;
}
}
測試。
EmployeeServiceTests
package com.abc.demo.service;
import com.abc.demo.entity.Employee;
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;
import org.springframework.orm.jpa.JpaSystemException;
@SpringBootTest
public class EmployeeServiceTests {
@Autowired
private EmployeeService employeeService;
@Test
public void alterId_throwJpaSystemException() {
Assertions.assertThrows(JpaSystemException.class, () -> employeeService.alterId());
}
@Test
public void alterIdByCreateNew_correct() {
Employee employee = employeeService.alterIdByCreateNew();
Assertions.assertEquals(2L, employee.getId());
}
}
參考github。
沒有留言:
張貼留言