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

Spring Boot中EntityManager无法自动注入导致空指针异常的解决

EntityManager空指针异常排查与解决

问题描述

编写了抽象仓库类AbstractDAO,使用EntityManager操作数据库,在Spring Boot中运行时出现EntityManager为空的空指针异常。已在主类添加@EnableJpaRepositories,但问题未解决。

原抽象类代码

@Getter
@NoArgsConstructor
@Transactional
@Repository
@Qualifier("personDAO")
public abstract class AbstractDAO<T> {
    public EntityManager em;
    @Transient
    protected Class clazz;

    public AbstractDAO(EntityManager em) {
        this.em = em;
    }
    public void persist(T model) {
        em.persist(model);
    }

    public void delete(T model) {
        em.remove(model);
    }

    public boolean deleteById(int id) {
        boolean result= em.createQuery("delete from "+clazz.getSimpleName()+ " o where o.id=" + id).executeUpdate() > 0;
        return result;
    }

    public List<T> findAll() {
        return em.createQuery("select o from "+clazz.getSimpleName()+" o").getResultList();}

    public T findById(int id) {
        return (T) em.find(clazz, id);
    }
}

原实现类代码

public class SimpleDAOImpl extends SimpleDAO<Person> {
    public SimpleDAOImpl(Class<?> clazz) {
           this.clazz=Person.class;
    }
}

异常信息

Exception in thread "restartedMain" java.lang.reflect.InvocationTargetException
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:116)
    at java.base/java.lang.reflect.Method.invoke(Method.java:578)
    at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49)
Caused by: java.lang.NullPointerException: Cannot invoke "jakarta.persistence.EntityManager.persist(Object)" because "this.em" is null
    at org.isoft.repo.AbstractDAO.persist(AbstractDAO.java:29)
    at org.isoft.App.main(App.java:35)
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
    ... 2 more

解决步骤

1. 正确注入EntityManager

原代码中em字段未添加@PersistenceContext注解,Spring无法自动注入。可选择两种注入方式:

方式1:字段注入

直接在em字段上添加@PersistenceContext注解:

@PersistenceContext
protected EntityManager em;

方式2:构造函数注入(推荐)

符合依赖注入规范,避免空指针风险:

protected final EntityManager em;
protected final Class<T> clazz;

public AbstractDAO(EntityManager em, Class<T> clazz) {
    this.em = em;
    this.clazz = clazz;
}

2. 让实现类被Spring管理

原实现类未添加@Repository注解,Spring无法识别为受管Bean,手动new实例时Spring不会注入依赖。修改实现类:

@Repository
public class PersonDAOImpl extends AbstractDAO<Person> {
    // 构造函数注入时传递EntityManager和泛型类
    public PersonDAOImpl(EntityManager em) {
        super(em, Person.class);
    }
}

3. 自动获取泛型类型(优化)

通过反射自动获取泛型参数,避免每个实现类硬编码clazz:

protected AbstractDAO() {
    ParameterizedType type = (ParameterizedType) getClass().getGenericSuperclass();
    this.clazz = (Class<T>) type.getActualTypeArguments()[0];
}

4. 修复SQL注入风险(可选)

原deleteById方法使用字符串拼接ID存在安全隐患,改用参数绑定:

public boolean deleteById(int id) {
    int result = em.createQuery("delete from " + clazz.getSimpleName() + " o where o.id = :id")
            .setParameter("id", id)
            .executeUpdate();
    return result > 0;
}

5. 确保Spring扫描范围覆盖DAO类

检查主类的@SpringBootApplication或@ComponentScan、@EnableJpaRepositories注解,确保DAO所在包被包含在扫描范围内。

最终修改后的抽象类示例

@Getter
@Transactional
@Repository
public abstract class AbstractDAO<T> {
    @PersistenceContext
    protected EntityManager em;
    @Transient
    protected Class<T> clazz;

    protected AbstractDAO() {
        // 自动获取泛型类型
        ParameterizedType type = (ParameterizedType) getClass().getGenericSuperclass();
        this.clazz = (Class<T>) type.getActualTypeArguments()[0];
    }

    public void persist(T model) {
        em.persist(model);
    }

    public void delete(T model) {
        em.remove(model);
    }

    public boolean deleteById(int id) {
        int result = em.createQuery("delete from " + clazz.getSimpleName() + " o where o.id = :id")
                .setParameter("id", id)
                .executeUpdate();
        return result > 0;
    }

    public List<T> findAll() {
        return em.createQuery("select o from " + clazz.getSimpleName() + " o", clazz)
                .getResultList();
    }

    public T findById(int id) {
        return em.find(clazz, id);
    }
}

最终实现类示例

@Repository
public class PersonDAOImpl extends AbstractDAO<Person> {
    // 无需额外代码,反射自动获取Person.class
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 03:40:16