Jpa 未捕获到UnuniquereSultException

Jpa 未捕获到UnuniquereSultException,jpa,jakarta-ee,exception-handling,ejb,Jpa,Jakarta Ee,Exception Handling,Ejb,从我的一个bean的方法中捕获ununiqueresultexception时遇到问题 这里是方法。它位于@Statefulbean中 public Field findByTitle(String title) { if (title == null) return null; try { return entityManager .createQuery("SELECT f FROM Field f WHERE

从我的一个bean的方法中捕获
ununiqueresultexception
时遇到问题

这里是方法。它位于
@Stateful
bean中

public Field findByTitle(String title) {
    if (title == null)
        return null;

    try {
        return entityManager
                .createQuery("SELECT f FROM Field f WHERE f.title = ?1", Field.class)
                .setParameter(1, title)
                .getSingleResult();
    } catch (NoResultException e) {
        return null;
    } 
}
简单地说,我想从调用此方法的另一个bean捕获未经检查的
ununiqueresultexception

下面是调用bean的try/catch:

try {
    Field field = fieldService.findByTitle(title);
} catch (NonUniqueResultException e) {
    LogManager.logError("Oh no!");
}
我的期望是“哦,不!”会打印到控制台上。相反,将打印
ununiqueresultexception
的整个堆栈跟踪。接电话从来没有碰过

堆栈跟踪:(参考此行:
.getSingleResult();

我做错了什么

更新:尝试再次抛出原因;同样的结果

public Field findByTitle(String title) {
    if (title == null)
        return null;

    try {
        return entityManager
                .createQuery("SELECT f FROM Field f WHERE f.title = ?1", Field.class)
                .setParameter(1, title)
                .getSingleResult();
    } catch (NoResultException e) {
        return null;
    } catch (NonUniqueResultException e) {
        throw e;
    }
}
更新:这是工作。但这不是我想要的工作方式

try {
    Field field = fieldService.findByTitle(title);
} catch (EJBException e) {
    if (e.getCause() instanceof NonUniqueResultException) {
        LogManager.logInfo("Oh no!");
    }
    return;
}

您可以尝试通过在查询末尾添加限制来修复它

public Field findByTitle(String title) {
    if (title == null)
        return null;

    try {
        return entityManager
                .createQuery("SELECT f FROM Field f WHERE f.title = ?1 LIMIT 1,1", Field.class)
                .setParameter(1, title)
                .getSingleResult();
    } catch (NoResultException e) {
        return null;
    }
}

引发的实际异常是
javax.ejb.EJBException
,因此您必须捕获它,然后重试原因:

try {
    ....
} catch (EJBException ex) {
    if (ex.getCause() instanceof NonUniqueResultException) {
        throw (NonUniqueResultException)ex.getCause();
    }
}

事实证明,因为我是作为代理访问bean,所以引发的实际异常是
javax.ejb.EJBException
。请参见堆栈跟踪中的这一行:

javax.ejb.EJBException: javax.persistence.NonUniqueResultException: result returns more than one elements
因此,(丑陋的)解决方案是捕获
EJBException
而不是
ununiqueresultexception
,然后检查原因以确保它是我所期望的:

try {
    Field field = fieldService.findByTitle(title);
} catch (EJBException e) {
    if (e.getCause() instanceof NonUniqueResultException) {
        LogManager.logInfo("Oh no!");
    }
    return;
}
这是可行的,但看起来很糟糕,你可以看出这只是一个糟糕的设计


因此,我选择使用的建议解决方案返回结果的
集合
,而不仅仅是一个结果。这样,我可以简单地检查集合的大小。如果大于1,结果显然不是唯一的。

当从带注释的容器管理bean调用时,您将收到一个
EJBException
,因此您需要捕获该异常(如Dmitry Ginzburg的回答中所述)

但是,问题的一个更一致的方法(解决方案)是更改方法签名和实现,如下所示:

public List<Field> findByTitle(String title) {
    if (title == null)
        return null;

    try {
        TypedQuery<Field> query =  entityManager
                .createTypedQuery("SELECT f FROM Field f WHERE f.title = ?1", Field.class);
        query.setParameter(1, title)
        return query.getResultList();
    } catch (NoResultException e) {
        return null;
    } 
}
公共列表findbytle(字符串标题){
if(title==null)
返回null;
试一试{
TypedQuery=entityManager
.createTypedQuery(“从f字段中选择f,其中f.title=?1”,Field.class);
query.setParameter(1,标题)
返回query.getResultList();
}捕获(noresulte异常){
返回null;
} 
}

引发/接受异常处理几乎从来都不是有效的方法。始终选择适当设计的界面,以反映您试图实现的情况

不幸的是,JPQL不支持查询中的
限制
。但是,如果数据库中有多行匹配,则添加
.setMaxResults(1)
可以防止引发异常。我仍然想知道为什么没有捕获到这一点。这不是问题的有效答案,而是解决问题的方法。请提供您遇到的格式正确的堆栈跟踪(或相关部分)。我有一个想法,但需要通过读取堆栈跟踪来验证它。@MWiesner完成。如果您需要跟踪的上半部分,请告诉我。我只包括了“原因”部分。顺便说一下:显然,方法签名应该是
公共集合findbytle(字符串标题)
。这对我来说是合乎逻辑的,因为您在这里触发的查询往往会返回多个结果。因此,这个方法应该返回一个集合。(在“title”没有唯一约束的情况下)它不是引发的实际异常:
由javax.persistence.ununiqueresultexception引起:result返回多个元素
。注意引起的by@DmitryGinzburg哦哎呀。我刚刚注意到顶部写着
javax.ejb.EJBException
。我从来没有想过由引起的
会提示抛出另一个异常。没有办法只得到
ununiqueresultexception
?不起作用-它只是围绕问题的原因。事实上,如果代码应该只返回一个值,那么如果我们发现多个值,我们应该抛出一个异常,这就是方法。你是正确的-从本质上说-但是这是非常糟糕的设计(思考)接受这样的粘合代码来修补错误设计的持久层接口。显然,他的数据库允许
title
有多个唯一值。实际上,
EJBException
被抛出bean之外,因为它是一个代理。方法内部会抛出
ununiqueresultexception
。在调用方法的外部,我正在访问代理,因此得到一个
EJBException
。因此,如果我在调用的位置捕捉到了
EJBException
,然后使用
getCause
,它会正常工作。我将更新我的帖子以反映这一点。@MitchtalEdge你是对的。这是一个与容器/层相关的问题。我试图用我自己的方式来回答这个问题,不过,你会解决的;)我感谢你和德米特里花时间帮我做这件事。非常感谢!!不客气。我想补充一点,Dmitry解决这个问题的方法本身并没有错。这取决于您对持久性服务接口的需求。就个人而言,我更喜欢好的设计,而不是不必要的粘合代码。另外的提示:如果您想(仅仅)检查大小,请在持久性服务接口中添加另一个方法,例如
int getFieldCount(String title)
,并使用本机的“SELECT count(…)…”实现它查询以检查给定的
标题
是否存在多个
字段
元组。原因:不会从数据库中获取性能更好的对象(数据)(特别是在返回许多
字段
实例的情况下)。@MWiesner这是个好主意。再次感谢:)
public List<Field> findByTitle(String title) {
    if (title == null)
        return null;

    try {
        TypedQuery<Field> query =  entityManager
                .createTypedQuery("SELECT f FROM Field f WHERE f.title = ?1", Field.class);
        query.setParameter(1, title)
        return query.getResultList();
    } catch (NoResultException e) {
        return null;
    } 
}