2010-03-25 31 views
17

Tôi đang làm việc với EJB và JPA trên máy chủ ứng dụng Glassfish v3. Tôi có một lớp Entity, nơi tôi buộc một trong các trường là duy nhất với chú thích @Column.Xử lý các vi phạm ràng buộc một cách tao nhã trong môi trường EJB/JPA?

@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; 
    } 
} 

Khi tôi cố gắng duy trì một đối tượng với trường này được đặt thành giá trị không duy nhất, tôi nhận được ngoại lệ (như mong đợi) khi giao dịch do vùng chứa EJB quản lý.

Tôi có hai vấn đề tôi muốn giải quyết:

1) Trường hợp ngoại lệ tôi nhận được là vô ích "javax.ejb.EJBException: Giao dịch bị hủy bỏ". Nếu tôi đệ quy gọi getCause() đủ lần, cuối cùng tôi đã đạt được "java.sql.SQLIntegrityConstraintViolationException" hữu ích hơn, nhưng ngoại lệ này là một phần của việc thực hiện EclipseLink và tôi không thực sự thoải mái dựa vào sự tồn tại của nó.

Có cách nào tốt hơn để nhận thông tin lỗi chi tiết với JPA không?

2) Thùng chứa EJB khăng khăng ghi nhật ký lỗi này mặc dù tôi nắm bắt và xử lý lỗi này.

Có cách nào tốt hơn để xử lý lỗi này, điều này sẽ ngăn Glassfish không làm lộn xộn nhật ký của tôi với thông tin ngoại lệ vô dụng không?

Cảm ơn.

Trả lời

22

Ngoại lệ tôi nhận được là "javax.ejb.EJBException: Giao dịch bị hủy" vô ích. (...)

Tôi đã thử nghiệm bên cạnh mình (với GFv3 và EclipseLink) và tôi xác nhận hành vi này. Stacktrace đầy đủ là:

 
javax.ejb.EJBException: Transaction aborted 
    at com.sun.ejb.containers.BaseContainer.completeNewTx(BaseContainer.java:4997) 
    at com.sun.ejb.containers.BaseContainer.postInvokeTx(BaseContainer.java:4756) 
    at com.sun.ejb.containers.BaseContainer.postInvoke(BaseContainer.java:1955) 
    at com.sun.ejb.containers.BaseContainer.postInvoke(BaseContainer.java:1906) 
    at com.sun.ejb.containers.EJBLocalObjectInvocationHandler.invoke(EJBLocalObjectInvocationHandler.java:198) 
    at com.sun.ejb.containers.EJBLocalObjectInvocationHandlerDelegate.invoke(EJBLocalObjectInvocationHandlerDelegate.java:84) 
    at $Proxy218.myBusinessMethod(Unknown Source) 
    at com.stackoverflow.q2522643.__EJB31_Generated__MyEJB__Intf____Bean__.myBusinessMethod(Unknown Source) 
    at com.stackoverflow.q2522643.MyServlet.doGet(MyServlet.java:28) 
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:734) 
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:847) 
    at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1523) 
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:279) 
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:188) 
    at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:641) 
    at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:97) 
    at com.sun.enterprise.web.PESessionLockingStandardPipeline.invoke(PESessionLockingStandardPipeline.java:85) 
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:185) 
    at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:332) 
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:233) 
    at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:165) 
    at com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:791) 
    at com.sun.grizzly.http.ProcessorTask.doProcess(ProcessorTask.java:693) 
    at com.sun.grizzly.http.ProcessorTask.process(ProcessorTask.java:954) 
    at com.sun.grizzly.http.DefaultProtocolFilter.execute(DefaultProtocolFilter.java:170) 
    at com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:135) 
    at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:102) 
    at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:88) 
    at com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:76) 
    at com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:53) 
    at com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:57) 
    at com.sun.grizzly.ContextTask.run(ContextTask.java:69) 
    at com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:330) 
    at com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:309) 
    at java.lang.Thread.run(Thread.java:619) 
Caused by: javax.transaction.RollbackException: Transaction marked for rollback. 
    at com.sun.enterprise.transaction.JavaEETransactionImpl.commit(JavaEETransactionImpl.java:450) 
    at com.sun.enterprise.transaction.JavaEETransactionManagerSimplified.commit(JavaEETransactionManagerSimplified.java:837) 
    at com.sun.ejb.containers.BaseContainer.completeNewTx(BaseContainer.java:4991) 
    ... 34 more 
Caused by: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.0.0.v20091127-r5931): org.eclipse.persistence.exceptions.DatabaseException 
Internal Exception: java.sql.SQLIntegrityConstraintViolationException: The statement was aborted because it would have caused a duplicate key value in a unique or primary key constraint or unique index identified by 'SQL100326111558470' defined on 'MYENTITY'. 
Error Code: -1 
Call: INSERT INTO MYENTITY (ID, NAME) VALUES (?, ?) 
    bind => [2, Duke!] 
Query: InsertObjectQuery([email protected]) 
    at org.eclipse.persistence.exceptions.DatabaseException.sqlException(DatabaseException.java:324) 
    at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeDirectNoSelect(DatabaseAccessor.java:800) 
    at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeNoSelect(DatabaseAccessor.java:866) 
    at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.basicExecuteCall(DatabaseAccessor.java:586) 
    at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeCall(DatabaseAccessor.java:529) 
    at org.eclipse.persistence.internal.sessions.AbstractSession.executeCall(AbstractSession.java:914) 
    at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:205) 
    at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:191) 
    at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.insertObject(DatasourceCallQueryMechanism.java:334) 
    at org.eclipse.persistence.internal.queries.StatementQueryMechanism.insertObject(StatementQueryMechanism.java:162) 
    at org.eclipse.persistence.internal.queries.StatementQueryMechanism.insertObject(StatementQueryMechanism.java:177) 
    at org.eclipse.persistence.internal.queries.DatabaseQueryMechanism.insertObjectForWrite(DatabaseQueryMechanism.java:461) 
    at org.eclipse.persistence.queries.InsertObjectQuery.executeCommit(InsertObjectQuery.java:80) 
    at org.eclipse.persistence.queries.InsertObjectQuery.executeCommitWithChangeSet(InsertObjectQuery.java:90) 
    at org.eclipse.persistence.internal.queries.DatabaseQueryMechanism.executeWriteWithChangeSet(DatabaseQueryMechanism.java:286) 
    at org.eclipse.persistence.queries.WriteObjectQuery.executeDatabaseQuery(WriteObjectQuery.java:58) 
    at org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:675) 
    at org.eclipse.persistence.queries.DatabaseQuery.executeInUnitOfWork(DatabaseQuery.java:589) 
    at org.eclipse.persistence.queries.ObjectLevelModifyQuery.executeInUnitOfWorkObjectLevelModifyQuery(ObjectLevelModifyQuery.java:109) 
    at org.eclipse.persistence.queries.ObjectLevelModifyQuery.executeInUnitOfWork(ObjectLevelModifyQuery.java:86) 
    at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.internalExecuteQuery(UnitOfWorkImpl.java:2863) 
    at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1225) 
    at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1207) 
    at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1167) 
    at org.eclipse.persistence.internal.sessions.CommitManager.commitNewObjectsForClassWithChangeSet(CommitManager.java:197) 
    at org.eclipse.persistence.internal.sessions.CommitManager.commitAllObjectsWithChangeSet(CommitManager.java:103) 
    at org.eclipse.persistence.internal.sessions.AbstractSession.writeAllObjectsWithChangeSet(AbstractSession.java:3260) 
    at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabase(UnitOfWorkImpl.java:1405) 
    at org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.commitToDatabase(RepeatableWriteUnitOfWork.java:547) 
    at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabaseWithChangeSet(UnitOfWorkImpl.java:1510) 
    at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.issueSQLbeforeCompletion(UnitOfWorkImpl.java:3134) 
    at org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.issueSQLbeforeCompletion(RepeatableWriteUnitOfWork.java:268) 
    at org.eclipse.persistence.transaction.AbstractSynchronizationListener.beforeCompletion(AbstractSynchronizationListener.java:157) 
    at org.eclipse.persistence.transaction.JTASynchronizationListener.beforeCompletion(JTASynchronizationListener.java:68) 
    at com.sun.enterprise.transaction.JavaEETransactionImpl.commit(JavaEETransactionImpl.java:412) 
    ... 36 more 
Caused by: java.sql.SQLIntegrityConstraintViolationException: The statement was aborted because it would have caused a duplicate key value in a unique or primary key constraint or unique index identified by 'SQL100326111558470' defined on 'MYENTITY'. 
    at org.apache.derby.client.am.SQLExceptionFactory40.getSQLException(Unknown Source) 
    at org.apache.derby.client.am.SqlException.getSQLException(Unknown Source) 
    at org.apache.derby.client.am.PreparedStatement.executeUpdate(Unknown Source) 
    at com.sun.gjc.spi.base.PreparedStatementWrapper.executeUpdate(PreparedStatementWrapper.java:108) 
    at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeDirectNoSelect(DatabaseAccessor.java:791) 
    ... 69 more 
Caused by: org.apache.derby.client.am.SqlException: The statement was aborted because it would have caused a duplicate key value in a unique or primary key constraint or unique index identified by 'SQL100326111558470' defined on 'MYENTITY'. 
    at org.apache.derby.client.am.Statement.completeExecute(Unknown Source) 
    at org.apache.derby.client.net.NetStatementReply.parseEXCSQLSTTreply(Unknown Source) 
    at org.apache.derby.client.net.NetStatementReply.readExecute(Unknown Source) 
    at org.apache.derby.client.net.StatementReply.readExecute(Unknown Source) 
    at org.apache.derby.client.net.NetPreparedStatement.readExecute_(Unknown Source) 
    at org.apache.derby.client.am.PreparedStatement.readExecute(Unknown Source) 
    at org.apache.derby.client.am.PreparedStatement.flowExecute(Unknown Source) 
    at org.apache.derby.client.am.PreparedStatement.executeUpdateX(Unknown Source) 
    ... 72 more 

Như chúng ta có thể thấy, EclipseLink thực sự ném một hộp chứa. Nhưng đây là WRONG. EclipseLink nên ném một PersistenceException (từ JPA) hoặc một nếu phân lớp của nó nhưng chắc chắn không phải là một ngoại lệ cụ thể của nhà cung cấp. Điều này một lỗi và bạn nên báo cáo lỗi như vậy: https://glassfish.dev.java.net/servlets/ProjectIssues (trong số thực thể kiên trì thành phần phụ).

Và bạn hoàn toàn đúng, bạn nên KHÔNG bắt ngoại lệ dành riêng cho nhà cung cấp vì lợi ích của tính di động. Bạn nên bắt một JPA PersistenceException hoặc một phân lớp (và sau đó có thể nhìn vào gói SQLException). Bạn có thể phải (tạm thời) trong trường hợp cụ thể này vì lỗi EclipseLink, nhưng đây là một giải pháp thay thế.

+0

@hallidave Ghi chú rằng một 'PersistenceException' sẽ làm mất hiệu lực bối cảnh giao dịch và rollback giao dịch, * không quan trọng nếu bạn nắm bắt nó hay không *. Các javadoc nói "Tất cả các trường hợp của PersistenceException ngoại trừ trường hợp của NoResultException, NonUniqueResultException, LockTimeoutException, và QueryTimeoutException sẽ gây ra các giao dịch hiện tại, nếu một trong những hoạt động, để được đánh dấu cho rollback.". Đó là một sự khác biệt lớn với JDBC đơn giản là bạn có thể nuốt một ngoại lệ (tùy thuộc vào những gì bạn làm) và vẫn cam kết. – ewernli

+0

@ewernli Vâng, tôi hiểu rằng giao dịch được khôi phục bất kể điều gì - nó thực sự chỉ là nhật ký đầy đủ các stacktraces mà tôi thấy khó chịu. Lý tưởng nhất, tôi muốn nắm bắt ngoại lệ bên trong bối cảnh của EJB, nhưng nó không xảy ra cho đến khi container bắt đầu giao dịch. Tôi đang nghĩ đến việc tự quản lý giao dịch để tôi có quyền kiểm soát nhiều hơn. – hallidave

+0

Tại sao nó sẽ tốt hơn, nếu EclipseLink ném một 'PersistenceException'? Bạn không có cơ hội để tìm ra những gì địa ngục xảy ra (lĩnh vực nào vi phạm một ràng buộc duy nhất (bạn có thể có nhiều lĩnh vực duy nhất trong một thực thể)) – pihentagy

5

Tôi không biết cách phát hiện vi phạm ràng buộc duy nhất theo cách di động, tốt nhất tôi đã nghĩ ra là chỉ xử lý PersistenceException. Nếu ai đó có thể trả lời rằng tôi cũng muốn được quan tâm.

Tôi có thể trợ giúp với sự cố đăng nhập.

Bên trong đơn vị kiên trì của bạn trong persistence.xml bạn thêm dòng sau:

<properties> 
    <property name="eclipselink.logging.level" value="SEVERE"/> 
</properties> 

Điều đó sẽ thoát khỏi một số trường hợp ngoại lệ. Bạn sẽ vẫn thấy các dấu vết ngăn xếp nơi vùng chứa nhìn thấy ngoại lệ tại thời gian cam kết của CMT.Bạn phải nuốt những thứ này trước khi container nhìn thấy chúng. Bạn có thể làm như sau.

1) Tạo ngoại lệ dành riêng cho ứng dụng để biểu thị sự cố liên tục. Tôi gọi là DataStoreException của tôi.

2) Không sử dụng bean chế độ xem không có giao diện. Thêm DataStoreException vào mệnh đề ném của chữ ký phương thức trong giao diện biz.

3) Thêm phương pháp sau đây để EJB của bạn:

@AroundInvoke 
public Object interceptor(InvocationContext ic) throws Exception { 
    Object o = null; 
    try { 
     o = ic.proceed(); 
     if (!sessionContext.getRollbackOnly()) { 
      entityManager.flush(); 
     } 
    } catch (PersistenceException ex) { 
     throw new DataStoreException(ex); 
    } 
    return o; 
} 
Các vấn đề liên quan