Maven可运行Jar中EntityManager自动注入为空及增改失效问题求助
问题分析与解决方案
问题描述
开发一个可运行Jar的Maven项目,遇到两个JPA相关问题:
- 未手动赋值
EntityManager时,通过@PersistenceContext注入的EntityManager始终为null; - 手动赋值
EntityManager后,查询功能正常,但新增、更新操作无报错却无法持久化数据。
相关配置与代码
Persistence.xml
<persistence-unit name="GreedTechDB"> <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider> <shared-cache-mode>NONE</shared-cache-mode> <properties> <property name="jakarta.persistence.jdbc.driver" value="com.mysql.cj.jdbc.Driver"/> <property name="jakarta.persistence.jdbc.url" value="jdbc:mysql://CORRECT_URL/GreedTechV1"/> <property name="jakarta.persistence.jdbc.user" value="CORRECT_USER"/> <property name="jakarta.persistence.jdbc.password" value="CORRECT_PASSWORD"/> <property name="jakarta.persistence.schema-generation.database.action" value="create" /> <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" /> <property name="hibernate.show_sql" value="true" /> <property name="hibernate.format_sql" value="true" /> </properties> </persistence-unit>
BaseSource.java
public class BaseSource implements Serializable { @Getter @PersistenceContext protected EntityManager em; private JPAQueryFactory queryFactory = null; protected JPAQueryFactory jpaQueryFactory() { if (queryFactory == null) queryFactory = new JPAQueryFactory(em); //em.clear(); return queryFactory; } }
MainSource.java
public class MainSource extends BaseSource { public List<Streamer> getChannelsToJoin() { QStreamer qStreamer = QStreamer.streamer; return jpaQueryFactory().selectFrom(qStreamer).where(qStreamer.modules.like("%bot%")).fetch(); } @Transactional public void insertPengStats(int channel_id, int user_id, boolean success, int time_done) { PengStats stats = new PengStats(); stats.setChannelId(channel_id); stats.setUserId(user_id); stats.setKilled(success); stats.setTimeDone(time_done); stats.setTimestamp(LocalDateTime.now()); em.persist(stats); } }
问题原因与解决办法
问题1:@PersistenceContext注入EntityManager为null
原因:
可运行Jar属于Java SE环境,@PersistenceContext是Java EE规范注解,依赖Tomcat、WildFly这类Java EE容器完成注入,SE环境没有容器支持,所以注解不生效,EntityManager始终为null。
解决办法:
手动通过EntityManagerFactory创建EntityManager,替代容器注入。修改BaseSource类:
public class BaseSource implements Serializable { protected EntityManager em; private JPAQueryFactory queryFactory = null; private static EntityManagerFactory emf; // 静态初始化EntityManagerFactory,项目启动仅执行一次 static { emf = Persistence.createEntityManagerFactory("GreedTechDB"); } public BaseSource() { // 创建EntityManager实例 this.em = emf.createEntityManager(); } protected JPAQueryFactory jpaQueryFactory() { if (queryFactory == null) queryFactory = new JPAQueryFactory(em); return queryFactory; } // 项目结束时调用,释放资源 public void close() { if (em != null && em.isOpen()) { em.close(); } if (emf != null && emf.isOpen()) { emf.close(); } } }
问题2:手动赋值后增改操作无法持久化
原因:
手动创建的EntityManager没有事务管理支持,@Transactional注解在Java SE环境下默认不生效(需要Spring或Java EE容器解析并管理事务)。调用em.persist()后事务未提交,数据不会写入数据库。
解决办法:
手动管理事务,在增改操作中显式开启、提交事务,异常时回滚。修改MainSource中的insertPengStats方法:
public void insertPengStats(int channel_id, int user_id, boolean success, int time_done) { try { // 开启事务 em.getTransaction().begin(); PengStats stats = new PengStats(); stats.setChannelId(channel_id); stats.setUserId(user_id); stats.setKilled(success); stats.setTimeDone(time_done); stats.setTimestamp(LocalDateTime.now()); em.persist(stats); // 提交事务 em.getTransaction().commit(); } catch (Exception e) { // 异常时回滚事务 if (em.getTransaction().isActive()) { em.getTransaction().rollback(); } e.printStackTrace(); } }
额外注意事项
EntityManagerFactory必须单例,避免重复创建浪费资源;- 项目终止时调用
close()方法关闭EntityManager和EntityManagerFactory,释放数据库连接; - 若想简化事务管理,可引入Spring Context,通过Spring托管EntityManager和事务,让
@Transactional注解生效。
内容的提问来源于stack exchange,提问作者recklessGreed
相关产品推荐
相关产品推荐

