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
相关产品推荐
相关产品推荐

