基于JPA、H2内存库的Wildfly部署Java Rest API:PUT/POST事务问题
问题分析与解决方案
你的核心问题是容器管理事务与手动资源本地事务的混用,导致事务控制逻辑冲突,触发了Wildfly连接池的autocommit限制错误。下面分步骤给出修复方案:
一、核心问题拆解
- 你在
UserService中用@PersistenceContext注入了容器管理的EntityManager,但又手动调用em.getTransaction().begin()/commit(),这在Java EE容器(Wildfly)中是冲突的——容器注入的EntityManager默认绑定容器的JTA事务,不允许手动管理事务。 - 资源类
UserResource的方法上加了@Transactional,这会让容器自动开启JTA事务,此时你手动操作EntityManager的事务会直接触发连接池的autocommit错误。 - 实体类上的
@TransactionManagement(TransactionManagementType.BEAN)是EJB专属注解,实体类不需要这个配置,完全无效。 - 手动调用
em.close()是错误的——容器注入的EntityManager生命周期由容器管理,不需要手动关闭。
二、推荐修复方案(适配Wildfly容器,使用JTA事务)
1. 修改persistence.xml配置
将事务类型改为JTA(Wildfly原生支持,更适配容器环境),移除冲突的autocommit配置:
<persistence xmlns="https://jakarta.ee/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence https://jakarta.ee/xml/ns/persistence/persistence_3_1.xsd" version="3.0"> <persistence-unit name="test" transaction-type="JTA"> <!-- Entity classes --> <class>com.xxxx.business.User</class> <class>com.xxxx.business.Gender</class> <properties> <property name="javax.persistence.jdbc.driver" value="org.h2.Driver"/> <property name="javax.persistence.jdbc.url" value="jdbc:h2:mem:test;INIT=RUNSCRIPT FROM 'src/main/resources/h2init.sql';DB_CLOSE_DELAY=-1;"/> <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/> <property name="hibernate.show_sql" value="true"/> <!-- 移除autocommit配置,由容器事务管理接管 --> <property name="javax.persistence.schema-generation.database.action" value="drop-and-create" /> <property name="javax.persistence.sql-load-script-source" value="/h2init.sql" /> </properties> </persistence-unit> </persistence>
2. 修正实体类User
移除无效的EJB事务注解:
@Entity @Model @Table(name = "UserApi") public class User implements Serializable { // 保留原有属性、构造方法、getter/setter,移除@TransactionManagement注解 }
3. 重构UserService(容器管理事务)
移除手动事务管理代码,让容器通过@Transactional注解处理事务:
import jakarta.persistence.PersistenceContext; import jakarta.persistence.EntityManager; import jakarta.transaction.Transactional; import jakarta.ws.rs.WebApplicationException; import java.util.List; public class UserService { private static final String getAllUsers = "select u from User u"; private static final String getUsersByFirstNameSql = "select u from User u where u.firstName=:firstName"; private static final String getUserByIdSql = "select u from User u where id=:id"; @PersistenceContext EntityManager em; @Transactional public void createUser(User user) { em.persist(user); } @Transactional public void updateUser(User user) { User u = getUserById(user.getId()); u.setFirstName(user.getFirstName()); u.setLastName(user.getLastName()); u.setBirthday(user.getBirthday()); u.setEmail(user.getEmail()); u.setPassword(user.getPassword()); // 容器管理下,修改托管实体无需手动persist,事务提交时自动同步 } @Transactional public void deleteUser(Long id) { User user = getUserById(id); em.remove(user); } public User getUserById(Long id) { User user = em.find(User.class, id); if (user == null) { throw new WebApplicationException("User with id of " + id + " does not exist.", 404); } return user; } public List<User> getAllUsers() { return em.createQuery(getAllUsers, User.class).getResultList(); } public List<User> getUsersByFirstName(String firstName){ return em.createQuery(getUsersByFirstNameSql, User.class) .setParameter("firstName", firstName).getResultList(); } }
4. 调整UserResource
可以保留@Transactional,但更合理的做法是把事务注解放在服务层(上面的UserService已经加了),所以可以移除资源类的@Transactional:
@Path("/users") @Produces("application/json") @Consumes("application/json") public class UserResource { @Inject UserService userService; @GET @Produces(MediaType.APPLICATION_JSON) public List<User> getAllUsers() { return userService.getAllUsers(); } @GET @Path("/sf/{firstName}") @Produces(MediaType.APPLICATION_JSON) public List<User> getUsersByFirstName(@PathParam("firstName")String firstName) { if(!StringUtils.isAllEmpty(firstName)){ return userService.getUsersByFirstName(firstName); } return userService.getAllUsers(); } @GET @Path("/si/{id}") @Produces(MediaType.APPLICATION_JSON) public User getUser(@PathParam("id")long id) { return userService.getUserById(id); } @PUT @Path("/u") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) public Response updateCustomer(User user) { userService.updateUser(user); return Response.status(204).build(); } @POST @Path("/c") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) public Response create(User user) { userService.createUser(user); return Response.status(201).build(); } }
三、如果坚持使用RESOURCE_LOCAL事务(不推荐,容器环境下适配性差)
如果一定要用RESOURCE_LOCAL,需要放弃容器注入的EntityManager,手动从EntityManagerFactory创建:
1. 修改UserService
import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; import jakarta.persistence.PersistenceUnit; import jakarta.ws.rs.WebApplicationException; import java.util.List; public class UserService { private static final String getAllUsers = "select u from User u"; private static final String getUsersByFirstNameSql = "select u from User u where u.firstName=:firstName"; private static final String getUserByIdSql = "select u from User u where id=:id"; @PersistenceUnit(unitName = "test") EntityManagerFactory emf; public void createUser(User user) { EntityManager em = null; try { em = emf.createEntityManager(); em.getTransaction().begin(); em.persist(user); em.getTransaction().commit(); } catch (Exception e) { if (em != null && em.getTransaction().isActive()) { em.getTransaction().rollback(); } throw new RuntimeException("Failed to create user", e); } finally { if (em != null) { em.close(); } } } // updateUser、deleteUser方法同理,都要手动创建/关闭EntityManager,手动管理事务 // 查询方法可以简化,不需要事务 public User getUserById(Long id) { try (EntityManager em = emf.createEntityManager()) { User user = em.find(User.class, id); if (user == null) { throw new WebApplicationException("User with id of " + id + " does not exist.", 404); } return user; } } public List<User> getAllUsers() { try (EntityManager em = emf.createEntityManager()) { return em.createQuery(getAllUsers, User.class).getResultList(); } } }
2. 移除资源类的@Transactional注解
因为RESOURCE_LOCAL不支持容器事务管理,必须完全手动控制。
四、错误原因解释
当你在资源类加@Transactional时,Wildfly会自动开启一个JTA事务,并将注入的EntityManager绑定到该事务上。此时你手动调用em.getTransaction(),会尝试切换到资源本地事务模式,而容器已经接管了连接的autocommit设置,导致抛出IJ031017: You cannot set autocommit during a managed transaction错误——本质是两种事务模型的冲突。
内容的提问来源于stack exchange,提问作者Scrat
相关产品推荐
相关产品推荐

