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

WildFly/JBoss:基于请求头凭证每次创建新数据库连接的正确方案

问题解决方案

核心问题诊断

当前实现的致命问题是每次请求都创建EntityManagerFactory(EMF):EMF是重量级对象,初始化时会加载持久化单元配置、连接池元数据等大量资源,重复创建会直接导致响应延迟飙升、内存持续占用,同时“PersistenceUnitUser已注册”的警告也源于重复注册同名的持久化单元。

正确实现方案

1. 全局复用EntityManagerFactory

将EMF设置为应用级单例,仅初始化一次,所有请求共享该实例。可以通过CDI的@ApplicationScoped实现:

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.Persistence;

@ApplicationScoped
public class EMFProvider {
    private final EntityManagerFactory emf;

    public EMFProvider() {
        // 仅初始化一次,复用persistence.xml中的基础配置
        this.emf = Persistence.createEntityManagerFactory("PersistenceUnitUser");
    }

    public EntityManagerFactory getEmf() {
        return emf;
    }
}

2. 动态传递请求凭证创建EntityManager

基于全局EMF,每次请求时传入动态的用户名密码创建独立的EntityManager(EM),而非重新创建EMF。每个EM对应一个请求的数据库会话,使用完毕后关闭归还连接到池。

3. 处理Oracle保留字user

SQL中的user是Oracle内置函数(返回当前会话用户),在JPQL中需通过function('user')调用以避免语法错误;原生SQL可直接使用USER。

修正后的完整代码

import jakarta.inject.Inject;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.Query;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.HttpHeaders;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import java.util.HashMap;
import java.util.Map;

@Path("/todo-list-enriched")
public class TodoResource {
    private static final String X_USER_NAME = "X-User-Name";
    private static final String X_PASSWORD = "X-Password";

    @Inject
    private EMFProvider emfProvider;

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Response getToDoListEnriched(@Context HttpHeaders httpHeaders,
                                        @QueryParam("skip") int elementNumber,
                                        @QueryParam("take") int pageSize,
                                        @QueryParam("orderby") String orderBy) {
        String userName = httpHeaders.getHeaderString(X_USER_NAME);
        String password = httpHeaders.getHeaderString(X_PASSWORD);

        EntityManager entityManager = null;

        try {
            // 构造动态连接属性,仅覆盖用户名和密码
            Map<String, String> dynamicProps = new HashMap<>();
            dynamicProps.put("hibernate.connection.username", userName);
            dynamicProps.put("hibernate.connection.password", password);

            // 复用全局EMF创建请求级EntityManager
            EntityManagerFactory emf = emfProvider.getEmf();
            entityManager = emf.createEntityManager(dynamicProps);

            // JPQL方式查询(处理Oracle user函数)
            String jpql = "SELECT t FROM Todo t WHERE t.createUsr = function('user')";
            Query query = entityManager.createQuery(jpql);
            // 设置分页参数
            query.setFirstResult(elementNumber);
            query.setMaxResults(pageSize);
            // 执行查询
            var todoList = query.getResultList();

            // 若使用原生SQL:
            // String sql = "SELECT * FROM TODO_TABLE WHERE CREATE_USR = USER";
            // Query nativeQuery = entityManager.createNativeQuery(sql, Todo.class);

            // 只读查询无需开启事务,Hibernate自动处理只读会话
            return Response.ok(todoList).build();

        } catch (Exception ex) {
            ex.printStackTrace();
            return Response.serverError().entity("查询失败:" + ex.getMessage()).build();
        } finally {
            // 强制关闭EntityManager,归还连接到连接池
            if (entityManager != null && entityManager.isOpen()) {
                entityManager.close();
            }
        }
    }
}

关键优化点说明

  • 性能提升:EMF全局复用避免了重复初始化的开销,响应延迟会显著降低,内存占用也会稳定。
  • 资源释放:每次请求的EM在finally块中关闭,确保数据库连接归还到连接池,避免连接泄漏。
  • 警告消除:仅创建一次EMF,不会重复注册持久化单元,“PersistenceUnitUser已注册”的警告会消失。
  • 事务规范:GET请求为只读操作,无需手动开启事务;若涉及DML操作(如update/insert),再开启事务并在操作后提交或回滚。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 20:25:30