You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot @PrePersist监听器报错:this.partido为null

@PrePersist监听器NullPointerException问题排查与修复

问题现象

使用@PrePersist监听器对Partido实体做前置验证时,抛出以下异常:

java.lang.NullPointerException: Cannot invoke "es.mde.repositorios.Partido.getCampo()" because "this.partido" is null

现有代码

监听器类

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.persistence.PrePersist;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;

@Component
public class PartidoListener {

    @Autowired
    private PartidoDAO partidoDAO; 

    @PrePersist
    public void validacionPartido(Partido partido) {
        System.err.println("punto de fallo: 1");
        Long partidoCampoId = partido.getCampo().getId();
        LocalDateTime partidoCuando = partido.getCuando();
        System.err.println("datos de partido: " + partidoCampoId + ", " + partidoCuando );
        System.err.println("punto de fallo: 2");
        try {
            List<Partido> partidosCoincidentes = partidoDAO.findAll()
                    .stream()
                    .filter(p -> p.getCuando().equals(partidoCuando))
                    .filter(p -> p.getCampo().getId().equals(partidoCampoId))
                    .collect(Collectors.toList());
            System.err.println("punto de fallo: 3");
            boolean valido = partidosCoincidentes.isEmpty();
            System.err.println("punto de fallo: 4");
            System.err.println("partido es valido: " + valido);
            System.err.println("punto de fallo: 5");
        } catch (Exception e) {
            System.err.println(e);
        }
    }
}

Partido实体类

@Entity
@Table(name = "PARTIDOS")
@EntityListeners(PartidoListener.class)
public class Partido {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(unique = true)  
    private Long id;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "CAMPO")
    private Campo campo;
    private LocalDateTime cuando;
    
    @OneToMany(cascade = CascadeType.ALL, targetEntity = Puntuacion.class, mappedBy = "partido")
    private Collection<Puntuacion> puntuaciones = new ArrayList<>();

    // getter、setter省略
}

Campo实体类

@Entity
@Table(name = "CAMPOS")
public class Campo {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(unique = true)  
    private Long id;
    
    private String nombre, provincia;
    private float valorCampo, valorSlope;
    
    @OneToMany(cascade = CascadeType.ALL, targetEntity = Jugador.class, mappedBy = "campo")
    private Collection<Jugador> jugadores = new ArrayList<>();
    
    @OneToMany(cascade = CascadeType.ALL, targetEntity = Partido.class, mappedBy = "campo")
    private Collection<Partido> partidos = new ArrayList<>();
    
    // getter、setter省略
}

错误原因分析

从异常信息来看,核心问题是在调用Partido.getCampo()时,代码中引用了this.partido且该变量为null。结合代码排查,可能的原因有:

  1. Partido类的getCampo()方法写错:比如误将返回this.campo写成this.partido.getCampo(),导致引用了不存在的partido成员变量。
  2. 保存的Partido实例为null:调用partidoRepository.save(partido)时传入了null实例,导致监听器方法参数为null。
  3. Spring与JPA监听器集成问题:监听器未被Spring正确管理,导致JPA自行实例化监听器时出现异常(但此情况更多导致依赖注入失败,而非实体参数null)。

修复步骤

1. 检查并修正Partido类的getCampo()方法

确保方法返回正确的成员变量:

public Campo getCampo() {
    return this.campo; // 确认此处是campo而非partido
}

2. 确保保存的Partido实例有效

检查业务代码中调用save的逻辑,确保传入的Partido实例不为null,且campo和cuando字段已正确初始化。

3. 完善监听器的空值检查

在监听器方法开头添加空值校验,提前抛出明确异常:

@PrePersist
public void validacionPartido(Partido partido) {
    if (partido == null) {
        throw new IllegalArgumentException("待保存的Partido实例不能为null");
    }
    if (partido.getCampo() == null) {
        throw new IllegalArgumentException("Partido关联的Campo不能为null");
    }
    if (partido.getCuando() == null) {
        throw new IllegalArgumentException("Partido的时间字段cuando不能为null");
    }
    
    // 后续验证逻辑
}

4. 确保Spring正确管理监听器

使用构造注入替代字段注入,提升可靠性:

@Component
public class PartidoListener {

    private final PartidoDAO partidoDAO; 

    @Autowired
    public PartidoListener(PartidoDAO partidoDAO) {
        this.partidoDAO = partidoDAO;
    }

    // 监听器方法...
}

优化建议:替换全量查询的低效验证

当前使用findAll()全量查询后过滤的方式效率极低,推荐以下两种优化方案:

方案1:数据库层面添加联合唯一约束(最优)

直接在Partido实体上配置联合唯一约束,由数据库保证唯一性:

@Entity
@Table(name = "PARTIDOS", 
       uniqueConstraints = @UniqueConstraint(columnNames = {"CAMPO", "cuando"}))
@EntityListeners(PartidoListener.class)
public class Partido {
    // ... 其他代码
}

当存在冲突时,数据库会抛出DataIntegrityViolationException,业务代码中捕获该异常即可处理重复提交的情况。

方案2:自定义DAO查询验证

在PartidoDAO中添加针对性查询方法,仅查询必要数据:

public interface PartidoDAO extends JpaRepository<Partido, Long> {
    boolean existsByCampoAndCuando(Campo campo, LocalDateTime cuando);
}

然后在监听器中调用该方法:

@PrePersist
public void validacionPartido(Partido partido) {
    // 空值检查...
    boolean exists = partidoDAO.existsByCampoAndCuando(partido.getCampo(), partido.getCuando());
    if (exists) {
        throw new IllegalStateException("该场地在指定时间已有安排的比赛");
    }
}

内容的提问来源于stack exchange,提问作者Xavier Guerrero

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 18:54:51