Spring Boot删除用户触发InvalidDataAccessApiUsageException问题排查
问题解决与优化方案
1. DELETE请求异常修复
你遇到的InvalidDataAccessApiUsageException: Unable to locate persister: java.lang.Integer异常,核心原因是**entityManager.remove()方法要求传入实体对象,而非主键ID值**。之前的deleteUser方法直接传了Integer类型的ID,导致JPA无法识别要操作的实体类。
修复代码示例
原错误实现:
public void deleteUser(Integer id) { entityManager.remove(id); }
正确实现(先查询实体再删除):
@Transactional public void deleteUser(Integer id) { User user = entityManager.find(User.class, id); if (user != null) { entityManager.remove(user); } }
如果使用Spring Data JPA,更推荐直接继承JpaRepository,自带deleteById方法,无需手动实现:
public interface UserDao extends JpaRepository<User, Integer> { // 无需自定义deleteUser方法,直接用deleteById(Integer id) }
2. 代码优化建议
实体类规范:
- 类名改为单数形式(
Users→User),符合Java命名惯例 - 关联映射补充完整:比如
User与Expense的一对多关系,添加mappedBy属性避免双向关联的冗余表 - 字段注解优化:用
@Column明确字段名、非空约束等,比如@Column(nullable = false)
- 类名改为单数形式(
DAO层简化:
- 放弃手动管理
EntityManager,改用Spring Data JPA的Repository接口,减少重复代码,同时自带CRUD、分页等常用功能
- 放弃手动管理
事务管理:
- 在DAO或Service层的修改/删除方法上添加
@Transactional注解,确保操作的原子性
- 在DAO或Service层的修改/删除方法上添加
Controller层优化:
- 统一异常处理:用
@RestControllerAdvice捕获JPA异常,返回友好的JSON错误信息 - 参数校验:对请求参数添加
@NotNull、@Min等注解,配合@Valid做入参校验
- 统一异常处理:用
3. 关联保存费用(无需手动传user_id)
通过JPA的关联映射,实现自动关联用户与费用,无需手动在JSON中填写user_id:
步骤1:配置实体关联
User实体(一对多):
@Entity public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; private String name; // 一对多关联Expense,mappedBy指定由Expense的user字段维护关联 @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true) private List<Expense> expenses = new ArrayList<>(); // getter、setter }
Expense实体(多对一):
@Entity public class Expense { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; private Double amount; private String description; // 多对一关联User,nullable=false确保费用必须属于某个用户 @ManyToOne @JoinColumn(name = "user_id", nullable = false) private User user; // getter、setter }
步骤2:Controller层实现关联保存
通过URL路径参数获取用户ID,查询到User实体后设置给Expense,再保存:
@RestController @RequestMapping("/expenses") public class ExpenseController { @Autowired private ExpenseDao expenseDao; @Autowired private UserDao userDao; @PostMapping("/user/{userId}") public Expense saveExpense(@PathVariable Integer userId, @RequestBody Expense expense) { User user = userDao.findById(userId) .orElseThrow(() -> new RuntimeException("用户不存在")); expense.setUser(user); return expenseDao.save(expense); } }
此时前端只需发送费用的基本信息JSON(无需user_id),请求URL为POST /expenses/user/1即可完成关联保存。
内容的提问来源于stack exchange,提问作者Musab Shaikh
相关产品推荐
相关产品推荐

