当前位置: 首页 > 面试题库 >

在EJB / JPA环境中优雅地处理约束违规?

郭乐湛
2023-03-14
问题内容

我正在Glassfish v3应用服务器上使用EJB和JPA。我有一个Entity类,在其中我用@Column注释强制其中一个字段是唯一的。

@Entity
public class MyEntity implements Serializable {

    private String uniqueName;

    public MyEntity() {
    }

    @Column(unique = true, nullable = false)
    public String getUniqueName() {
        return uniqueName;
    }

    public void setUniqueName(String uniqueName) {
        this.uniqueName = uniqueName;
    }
}

当我尝试将该字段设置为非唯一值的对象持久化时,当由EJB容器管理的事务提交时,我得到了一个异常(如预期的那样)。

我有两个问题要解决:

1)我得到的异常是无用的“
javax.ejb.EJBException:事务异常中止”。如果我递归调用getCause()足够多次,那么最终我会到达更有用的“
java.sql.SQLIntegrityConstraintViolationException”,但是该异常是EclipseLink实现的一部分,我真的不愿意依靠它的存在。

是否有更好的方法通过JPA获取详细的错误信息?

2)即使我捕获并处理了该错误,EJB容器仍然坚持记录该错误。

是否有更好的方法来处理此错误,从而阻止Glassfish使用无用的异常信息来使我的日志混乱?

谢谢。


问题答案:

我得到的异常是无用的“ javax.ejb.EJBException:事务异常中止”。(…)

我进行了测试(使用GFv3和EclipseLink),并确认了这一行为。完整的堆栈跟踪为:

javax.ejb.EJBException:事务中止
    在com.sun.ejb.containers.BaseContainer.completeNewTx(BaseContainer.java:4997)
    在com.sun.ejb.containers.BaseContainer.postInvokeTx(BaseContainer.java:4756)
    在com.sun.ejb.containers.BaseContainer.postInvoke(BaseContainer.java:1955)
    在com.sun.ejb.containers.BaseContainer.postInvoke(BaseContainer.java:1906)
    在com.sun.ejb.containers.EJBLocalObjectInvocationHandler.invoke(EJBLocalObjectInvocationHandler.java:198)
    在com.sun.ejb.containers.EJBLocalObjectInvocationHandlerDelegate.invoke(EJBLocalObjectInvocationHandlerDelegate.java:84)
    在$ Proxy218.myBusinessMethod(未知来源)
    com.stackoverflow.q2522643处。__EJB31_Generated__MyEJB__Intf ____ Bean __。myBusinessMethod(未知来源)
    在com.stackoverflow.q2522643.MyServlet.doGet(MyServlet.java:28)
    在javax.servlet.http.HttpServlet.service(HttpServlet.java:734)
    在javax.servlet.http.HttpServlet.service(HttpServlet.java:847)
    在org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1523)
    在org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:279)
    在org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:188)
    在org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:641)
    在com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:97)
    在com.sun.enterprise.web.PESessionLockingStandardPipeline.invoke(PESessionLockingStandardPipeline.java:85)
    在org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:185)
    在org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:332)
    在org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:233)
    在com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:165)
    在com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:791)
    在com.sun.grizzly.http.ProcessorTask.doProcess(ProcessorTask.java:693)
    在com.sun.grizzly.http.ProcessorTask.process(ProcessorTask.java:954)
    在com.sun.grizzly.http.DefaultProtocolFilter.execute(DefaultProtocolFilter.java:170)
    在com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:135)
    在com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:102)
    在com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:88)
    在com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:76)
    在com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:53)
    在com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:57)
    在com.sun.grizzly.ContextTask.run(ContextTask.java:69)
    在com.sun.grizzly.util.AbstractThreadPool $ Worker.doWork(AbstractThreadPool.java:330)
    在com.sun.grizzly.util.AbstractThreadPool $ Worker.run(AbstractThreadPool.java:309)
    在java.lang.Thread.run(Thread.java:619)
原因:javax.transaction.RollbackException:事务标记为回滚。
    在com.sun.enterprise.transaction.JavaEETransactionImpl.commit(JavaEETransactionImpl.java:450)
    在com.sun.enterprise.transaction.JavaEETransactionManagerSimplified.commit(JavaEETransactionManagerSimplified.java:837)
    在com.sun.ejb.containers.BaseContainer.completeNewTx(BaseContainer.java:4991)
    ...另外34个
由以下原因引起:异常[EclipseLink-4002](Eclipse Persistence Services-2.0.0.v20091127-r5931):org.eclipse.persistence.exceptions.DatabaseException
内部异常:java.sql.SQLIntegrityConstraintViolationException:该语句被中止,因为它会在“ MYENTITY”上定义的“ SQL100326111558470”标识的唯一或主键约束或唯一索引中导致重复的键值。
错误代码:-1
致电:插入MYENTITY(ID,NAME)值(?,?)
    绑定=> [2,公爵!]
查询:InsertObjectQuery(com.stackoverflow.q2522643.MyEntity@dba6a9)
    在org.eclipse.persistence.exceptions.DatabaseException.sqlException(DatabaseException.java:324)
    在org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeDirectNoSelect(DatabaseAccessor.java:800)
    在org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeNoSelect(DatabaseAccessor.java:866)
    在org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.basicExecuteCall(DatabaseAccessor.java:586)
    在org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeCall(DatabaseAccessor.java:529)
    在org.eclipse.persistence.internal.sessions.AbstractSession.executeCall(AbstractSession.java:914)
    在org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:205)
    在org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:191)
    在org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.insertObject(DatasourceCallQueryMechanism.java:334)
    在org.eclipse.persistence.internal.queries.StatementQueryMechanism.insertObject(StatementQueryMechanism.java:162)
    在org.eclipse.persistence.internal.queries.StatementQueryMechanism.insertObject(StatementQueryMechanism.java:177)
    在org.eclipse.persistence.internal.queries.DatabaseQueryMechanism.insertObjectForWrite(DatabaseQueryMechanism.java:461)
    在org.eclipse.persistence.queries.InsertObjectQuery.executeCommit(InsertObjectQuery.java:80)
    在org.eclipse.persistence.queries.InsertObjectQuery.executeCommitWithChangeSet(InsertObjectQuery.java:90)
    在org.eclipse.persistence.internal.queries.DatabaseQueryMechanism.executeWriteWithChangeSet(DatabaseQueryMechanism.java:286)
    在org.eclipse.persistence.queries.WriteObjectQuery.executeDatabaseQuery(WriteObjectQuery.java:58)
    在org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:675)
    在org.eclipse.persistence.queries.DatabaseQuery.executeInUnitOfWork(DatabaseQuery.java:589)
    在org.eclipse.persistence.queries.ObjectLevelModifyQuery.executeInUnitOfWorkObjectLevelModifyQuery(ObjectLevelModifyQuery.java:109)
    在org.eclipse.persistence.queries.ObjectLevelModifyQuery.executeInUnitOfWork(ObjectLevelModifyQuery.java:86)
    在org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.internalExecuteQuery(UnitOfWorkImpl.java:2863)
    在org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1225)
    在org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1207)
    在org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1167)
    在org.eclipse.persistence.internal.sessions.CommitManager.commitNewObjectsForClassWithChangeSet(CommitManager.java:197)
    在org.eclipse.persistence.internal.sessions.CommitManager.commitAllObjectsWithChangeSet(CommitManager.java:103)
    在org.eclipse.persistence.internal.sessions.AbstractSession.writeAllObjectsWithChangeSet(AbstractSession.java:3260)
    在org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabase(UnitOfWorkImpl.java:1405)
    在org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.commitToDatabase(RepeatableWriteUnitOfWork.java:547)
    在org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabaseWithChangeSet(UnitOfWorkImpl.java:1510)
    在org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.issueSQLbeforeCompletion(UnitOfWorkImpl.java:3134)处
    在org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.issueSQLbeforeCompletion(RepeatableWriteUnitOfWork.java:268)
    在org.eclipse.persistence.transaction.AbstractSynchronizationListener.beforeCompletion(AbstractSynchronizationListener.java:157)
    在org.eclipse.persistence.transaction.JTASynchronizationListener.beforeCompletion(JTASynchronizationListener.java:68)处
    在com.sun.enterprise.transaction.JavaEETransactionImpl.commit(JavaEETransactionImpl.java:412)
    ...另外36个
原因:java.sql.SQLIntegrityConstraintViolationException:该语句被中止,因为它会导致在“ MYENTITY”上定义的“ SQL100326111558470”标识的唯一或主键约束或唯一索引中重复键值。
    在org.apache.derby.client.am.SQLExceptionFactory40.getSQLException(未知来源)
    在org.apache.derby.client.am.SqlException.getSQLException(未知来源)
    在org.apache.derby.client.am.PreparedStatement.executeUpdate(未知来源)
    在com.sun.gjc.spi.base.PreparedStatementWrapper.executeUpdate(PreparedStatementWrapper.java:108)
    在org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeDirectNoSelect(DatabaseAccessor.java:791)
    ...更多69
由以下原因引起:org.apache.derby.client.am.SqlException:该语句被中止,因为它会在“ MYENTITY”上定义的“ SQL100326111558470”标识的唯一或主键约束或唯一索引中导致重复的键值。
    在org.apache.derby.client.am.Statement.completeExecute(未知来源)
    在org.apache.derby.client.net.NetStatementReply.parseEXCSQLSTTreply(未知来源)
    在org.apache.derby.client.net.NetStatementReply.readExecute(未知来源)
    在org.apache.derby.client.net.StatementReply.readExecute(未知来源)
    在org.apache.derby.client.net.NetPreparedStatement.readExecute_(未知来源)
    在org.apache.derby.client.am.PreparedStatement.readExecute(未知来源)
    在org.apache.derby.client.am.PreparedStatement.flowExecute(未知来源)
    在org.apache.derby.client.am.PreparedStatement.executeUpdateX(未知来源)
    ...另外72个

如我们所见,EclipseLink实际上抛出了一个o.e.p.e.DatabaseException然后被容器捕获的。但这是
错误的
。EclipseLink应该抛出一个PersistenceException(来自JPA)或一个(如果它是其子类),但肯定不是提供程序特定的异常。这

一个错误,您应该这样报告:https :
//glassfish.dev.java.net/servlets/ProjectIssues(在
实体持久 性子组件中)。

你是绝对正确的,你应该 赶供应商特定的例外便携性的缘故。您应该捕获一个JPA
PersistenceException或一个子类(然后看一下包装好的SQLException)。在这种特殊情况下,由于EclipseLink错误,您可能不得不(临时),但这是一种解决方法。



 类似资料:
  • 问题内容: 当违反唯一约束时,将引发a。但是可能有多种原因抛出异常。我如何才能发现违反了唯一约束? 问题答案: 我如何才能发现违反了唯一约束? 异常是链接在一起的,您必须递归调用以获取提供程序特定的异常(并可能转到),以将其转换为应用程序可以很好地为用户处理的内容。下面将打印异常链: 对于异常处理和“翻译”,您可以执行类似于Spring的操作(请参阅各种类,例如,以获取想法)。 所有这些都不是很好

  • 我有一个实体,比如说雇员 with字段名标记为唯一。 提前谢谢你

  • 我正在使用springboot和JPA(与Hibernate一起使用)。 我们可以假设在一个实体上我有一些唯一的约束(例如,以电子邮件作为约束的用户实体)。 当我将收到一个“创建”请求时,如果资源已经存在,我必须返回已经存在的资源的主键(例如ID,而不是电子邮件)。 最好的做法是什么? 我想避免2个查询(一个检查是否存在,一个创建),这可能吗?

  • 今天我遇到错误。 分析表明,最近的GC根是Logback thread: 即: 在中保存修改后,Tomcat8收到通知(我不确定用于监视fs上的更改的API是什么),然后开始重新部署应用程序。这就是之前发生的情况。 如何在容器环境中优雅地停止登录? 如何停止线程? null 更新我在中玩堆转储。在从坏的线程的引用跳转级别下: 它指的是 通过插入的日志源,我找到了changelog条目:

  • 让我们假设我们有这样一个用java编写的普通守护进程: 我们使用 来守护它,默认情况下,它会在 上发送 (TERM) 信号 假设当前执行的步骤是#2,此时我们正在发送项信号。 发生的情况是执行立即终止。 我发现我可以使用<code>addShutdownHook()</code>处理信号事件,但问题是它仍然会中断当前的执行,并将控制传递给处理程序: 所以,我的问题是——有没有可能不中断当前的执行,

  • 让我们假设我们有这样一个用python编写的琐碎守护进程: 我们使用< code>start-stop-daemon对其进行守护,默认情况下,它会在< code> - stop上发送< code > SIGTERM (< code > TERM )信号。 假设当前执行的步骤是。此时我们正在发送信号。 发生的情况是执行立即终止。 我发现我可以使用<code>signal.signal(signal.