Hibernate双向一对多关联添加Player实体报错求助
嘿,我帮你排查了下问题,从报错堆栈来看,你遇到的是NullPointerException,问题出在Application.addPlayers()方法里的this.players.add(player)这一行——原因很简单:你在Application类里声明了List<Player> players,但没有初始化这个集合,调用add方法的时候它还是null,自然就报错了。
下面是具体的解决步骤和优化建议:
1. 先解决核心的空指针问题:初始化集合
在Application类里,直接把players集合初始化,推荐用ArrayList,这样调用add方法时集合就不会是null了:
public class Application { @Id @GeneratedValue(strategy = GenerationType.AUTO) private int Id; @Column(name = "has_paid") private boolean paid; @Column(name = "is_accepted") private boolean accepted; @OneToOne @JoinColumn(name = "team_id") private Team team; @OneToMany(mappedBy = "application", cascade = CascadeType.ALL) // 这里直接初始化集合 private List<Player> players = new ArrayList<>(); // 其他getter/setter方法 public void addPlayers(Player player){ System.out.println("---------------1"); player.setApplication(this); System.out.println("---------------2"); this.players.add(player); // 现在不会再空指针了 System.out.println("---------------3"); } }
或者你也可以在Application的无参构造方法里初始化集合,效果是一样的。
2. 优化关联维护逻辑(可选但推荐)
你现在的addPlayers方法名其实不太准确(每次只加一个Player),而且可以加一些判空和去重逻辑,避免不必要的错误:
public void addPlayer(Player player){ if (player == null) { return; } // 维护双向关联的另一方 player.setApplication(this); // 避免重复添加同个Player if (!this.players.contains(player)) { this.players.add(player); } }
3. 检查Service层的DTO转实体逻辑
你在Service里直接把DTO中的Player对象传给addPlayers,这里要注意:DTO里的Player是数据传输对象,不是JPA管理的实体实例,你需要把它转换成Player实体对象再添加,否则Hibernate可能无法正确持久化:
@Override @ResponseStatus(HttpStatus.CREATED) @Transactional(rollbackFor = Exception.class) public Object create(ApplicationsPostDTO applicationsRequest){ if(Optional.ofNullable(applicationsRequest).isPresent()){ System.out.println(applicationsRequest.toString()); Application application = new Application(); application.setTeam(teamsRepositories.findById(applicationsRequest.getTeam_id()).orElseThrow(BadRequestException::new)); application.setAccepted(applicationsRequest.isAccepted()); application.setPaid(applicationsRequest.isPaid()); System.out.println(application.toString()); // 把DTO中的Player转换成实体对象 applicationsRequest.getPlayers().forEach(it -> { System.out.println(it.toString()); Player player = new Player(); player.setName(it.getName()); player.setRole(it.getRole()); player.setSize(it.getSize()); // 调用优化后的addPlayer方法 application.addPlayer(player); }); System.out.println(application.toString()); return applicationsRepositories.save(application); }else{ throw new BadRequestException(); } }
补充说明
双向一对多关联的关键是同时维护关联的两边:你已经在添加Player时设置了player.setApplication(this),这部分是正确的,它确保了关联关系在Player端也被维护。加上集合初始化后,整个关联逻辑就完整了,再配合cascade = CascadeType.ALL,保存Application的时候,关联的Player也会被自动持久化到数据库中。
内容的提问来源于stack exchange,提问作者Mikart Aerang

