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

基于JPA、H2内存库的Wildfly部署Java Rest API:PUT/POST事务问题

问题分析与解决方案

你的核心问题是容器管理事务与手动资源本地事务的混用,导致事务控制逻辑冲突,触发了Wildfly连接池的autocommit限制错误。下面分步骤给出修复方案:

一、核心问题拆解

  1. 你在UserService中用@PersistenceContext注入了容器管理的EntityManager,但又手动调用em.getTransaction().begin()/commit(),这在Java EE容器(Wildfly)中是冲突的——容器注入的EntityManager默认绑定容器的JTA事务,不允许手动管理事务。
  2. 资源类UserResource的方法上加了@Transactional,这会让容器自动开启JTA事务,此时你手动操作EntityManager的事务会直接触发连接池的autocommit错误。
  3. 实体类上的@TransactionManagement(TransactionManagementType.BEAN)是EJB专属注解,实体类不需要这个配置,完全无效。
  4. 手动调用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 12:29:51