java - 如何避免 Hibernate 拦截器审核中出现 StackOverflowException

标签 java hibernate

这是我的拦截器类

public class AuditInterceptor extends EmptyInterceptor {
    private static final long serialVersionUID = 1L;

    private Set<Auditable> inserts = new HashSet<>();
    private Set<Auditable> updates = new HashSet<>();
    private Set<Auditable> deletes = new HashSet<>();

//  @PrePersist
//  public void doPrePersist(final Auditable entity) {
//      if (entity.isAudited()) {
//          logIt("Create", entity);
//      }
//  }
//  
//  @PreUpdate
//  public void doPreUpdate(final Auditable entity) {
//      if (entity.isAudited()) {
//          logIt("Update", entity);
//      }
//  }
//  
//  @PreRemove
//  public void doPreRemove(final Auditable entity) {
//      if (entity.isAudited()) {
//          logIt("Delete", entity);
//      }
//  }
//  
    @Override
    public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types)
        throws CallbackException {

        if (entity instanceof Auditable && ((Auditable)entity).isAudited()){
            inserts.add((Auditable)entity);
        }
        return super.onSave(entity, id, state, propertyNames, types);

    }

    @Override
    public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState, String[] propertyNames, Type[] types)
        throws CallbackException {

        if (entity instanceof Auditable && ((Auditable)entity).isAudited()){
            updates.add((Auditable)entity);
        }
        return super.onFlushDirty(entity, id, currentState, previousState, propertyNames, types);

    }

    @Override
    public void onDelete(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {

        if (entity instanceof Auditable && ((Auditable)entity).isAudited()){
            deletes.add((Auditable)entity);
        }
    }

    //called before commit into database
    @Override
    public void preFlush(Iterator iterator) {
    }

    //called after committed into database
    @Override
    public void postFlush(Iterator iterator) {
        try {
            for (Auditable entity : inserts) {
                logIt("Saved", entity);
            }

            for (Auditable entity : updates) {
                logIt("Updated", entity); // This is getting called multiple times
            }

            for (Auditable entity : deletes) {
                logIt("Deleted", entity);
            }

        } finally {
            inserts.clear();
            updates.clear();
            deletes.clear();
        }
    }

    public void logIt(String action, Auditable entity) {
        if (!entity.isAudited()) {
            return;
        }
        // I read somewhere that you can't use the same session to write to the db.
        // So I get my ejb session bean (SessionHelper.getSession()).
        // Then I get the injected entitymanager session (.getSession()).
        // Then I get the SessionFactory and open a new session (.getSessionFactory().openSession()).
        Session tempSession = SessionHelper.getSession().getSession().getSessionFactory().openSession();
        Audit auditRecord = new Audit(); // This is an entity
        auditRecord.setAction(action);
//      auditRecord.setDetail(entity.toString()); 
        auditRecord.setCreatedTimestamp(new Timestamp(System.currentTimeMillis())); 
        auditRecord.setEntityPK(entity.getPrimaryKeyDisplay()); 
        auditRecord.setEntityName(entity.getClass().toString());
        tempSession.save(auditRecord);
        tempSession.flush(); // this is doing the calling the next postFlush
        // Since this is a different session, I don't see why it should cascade into an infinite loop since the Audit entity is not itself Auditable (.isAuditable() == false).
    }

}

我们所有的 EJB 实体都扩展了一个抽象类 Auditable,但它们的 .isAuditable() 标志可能会也可能不会设置为 true。如果为真,则应将其添加到适当的Set,然后在postFlush期间由Audit实体记录。但是审核是在一个单独的 session 上,并且它是一个不同的实体 Audit ,其 isAuditable 标志设置为 false,所以我不确定为什么它明显陷入了无限循环。

以下是请求的堆栈跟踪

Exception in thread "Thread-42" javax.ejb.EJBTransactionRolledbackException: Transaction rolled back
at org.jboss.as.ejb3.tx.CMTTxInterceptor.handleEndTransactionException(CMTTxInterceptor.java:137)
at org.jboss.as.ejb3.tx.CMTTxInterceptor.endTransaction(CMTTxInterceptor.java:117)
at org.jboss.as.ejb3.tx.CMTTxInterceptor.invokeInOurTx(CMTTxInterceptor.java:282)
at org.jboss.as.ejb3.tx.CMTTxInterceptor.required(CMTTxInterceptor.java:330)
at org.jboss.as.ejb3.tx.CMTTxInterceptor.processInvocation(CMTTxInterceptor.java:242)
at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:288)
at org.jboss.as.ejb3.remote.EJBRemoteTransactionPropagatingInterceptor.processInvocation(EJBRemoteTransactionPropagatingInterceptor.java:79)
at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:288)
at org.jboss.as.ejb3.component.interceptors.CurrentInvocationContextInterceptor.processInvocation(CurrentInvocationContextInterceptor.java:41)
at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:288)
at org.jboss.as.ejb3.component.invocationmetrics.WaitTimeInterceptor.processInvocation(WaitTimeInterceptor.java:43)
at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:288)
at org.jboss.as.ejb3.security.SecurityContextInterceptor.processInvocation(SecurityContextInterceptor.java:89)
at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:288)
at org.jboss.as.ejb3.component.interceptors.ShutDownInterceptorFactory$1.processInvocation(ShutDownInterceptorFactory.java:64)
at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:288)
at org.jboss.as.ejb3.component.interceptors.LoggingInterceptor.processInvocation(LoggingInterceptor.java:59)
at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:288)
at org.jboss.as.ee.component.NamespaceContextInterceptor.processInvocation(NamespaceContextInterceptor.java:50)
at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:288)
at org.jboss.as.ejb3.component.interceptors.AdditionalSetupInterceptor.processInvocation(AdditionalSetupInterceptor.java:55)
at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:288)
at org.jboss.as.ee.component.TCCLInterceptor.processInvocation(TCCLInterceptor.java:45)
at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:288)
at org.jboss.invocation.ChainedInterceptor.processInvocation(ChainedInterceptor.java:61)
at org.jboss.as.ee.component.ViewService$View.invoke(ViewService.java:185)
at org.jboss.as.ejb3.remote.protocol.versionone.MethodInvocationMessageHandler.invokeMethod(MethodInvocationMessageHandler.java:319)
at org.jboss.as.ejb3.remote.protocol.versionone.MethodInvocationMessageHandler.access$100(MethodInvocationMessageHandler.java:68)
at org.jboss.as.ejb3.remote.protocol.versionone.MethodInvocationMessageHandler$1.run(MethodInvocationMessageHandler.java:201)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:473)
at java.util.concurrent.FutureTask.run(FutureTask.java:262)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1152)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:622)
at java.lang.Thread.run(Thread.java:748)
at org.jboss.threads.JBossThread.run(JBossThread.java:122)
at ...asynchronous invocation...(Unknown Source)
at org.jboss.ejb.client.remoting.InvocationExceptionResponseHandler$MethodInvocationExceptionResultProducer.getResult(InvocationExceptionResponseHandler.java:99)
at org.jboss.ejb.client.EJBClientInvocationContext.getResult(EJBClientInvocationContext.java:270)
at org.jboss.ejb.client.TransactionInterceptor.handleInvocationResult(TransactionInterceptor.java:47)
at org.jboss.ejb.client.EJBClientInvocationContext.getResult(EJBClientInvocationContext.java:272)
at org.jboss.ejb.client.ReceiverInterceptor.handleInvocationResult(ReceiverInterceptor.java:132)
at org.jboss.ejb.client.EJBClientInvocationContext.getResult(EJBClientInvocationContext.java:260)
at org.jboss.ejb.client.EJBClientInvocationContext.awaitResponse(EJBClientInvocationContext.java:399)
at org.jboss.ejb.client.EJBInvocationHandler.doInvoke(EJBInvocationHandler.java:140)
at org.jboss.ejb.client.EJBInvocationHandler.doInvoke(EJBInvocationHandler.java:121)
at org.jboss.ejb.client.EJBInvocationHandler.invoke(EJBInvocationHandler.java:104)
at com.sun.proxy.$Proxy11.updateIssue(Unknown Source)
at com.kable.newsstand.knet.issue.FrmIssue2$SavingThread.doSave(FrmIssue2.java:3810)
at com.kable.newsstand.knet.issue.FrmIssue2$SavingThread.run(FrmIssue2.java:3752)
Caused by: javax.transaction.RollbackException: ARJUNA016053: Could not commit transaction.
at com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionImple.commitAndDisassociate(TransactionImple.java:1223)
at com.arjuna.ats.internal.jta.transaction.arjunacore.BaseTransaction.commit(BaseTransaction.java:126)
at com.arjuna.ats.jbossatx.BaseTransactionManagerDelegate.commit(BaseTransactionManagerDelegate.java:75)
at org.jboss.as.ejb3.tx.CMTTxInterceptor.endTransaction(CMTTxInterceptor.java:91)
at org.jboss.as.ejb3.tx.CMTTxInterceptor.invokeInOurTx(CMTTxInterceptor.java:282)
at org.jboss.as.ejb3.tx.CMTTxInterceptor.required(CMTTxInterceptor.java:330)
at org.jboss.as.ejb3.tx.CMTTxInterceptor.processInvocation(CMTTxInterceptor.java:242)
at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:288)
at org.jboss.as.ejb3.remote.EJBRemoteTransactionPropagatingInterceptor.processInvocation(EJBRemoteTransactionPropagatingInterceptor.java:79)
at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:288)
at org.jboss.as.ejb3.component.interceptors.CurrentInvocationContextInterceptor.processInvocation(CurrentInvocationContextInterceptor.java:41)
at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:288)
at org.jboss.as.ejb3.component.invocationmetrics.WaitTimeInterceptor.processInvocation(WaitTimeInterceptor.java:43)
at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:288)
at org.jboss.as.ejb3.security.SecurityContextInterceptor.processInvocation(SecurityContextInterceptor.java:89)
at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:288)
at org.jboss.as.ejb3.component.interceptors.ShutDownInterceptorFactory$1.processInvocation(ShutDownInterceptorFactory.java:64)
at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:288)
at org.jboss.as.ejb3.component.interceptors.LoggingInterceptor.processInvocation(LoggingInterceptor.java:59)
at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:288)
at org.jboss.as.ee.component.NamespaceContextInterceptor.processInvocation(NamespaceContextInterceptor.java:50)
at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:288)
at org.jboss.as.ejb3.component.interceptors.AdditionalSetupInterceptor.processInvocation(AdditionalSetupInterceptor.java:55)
at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:288)
at org.jboss.as.ee.component.TCCLInterceptor.processInvocation(TCCLInterceptor.java:45)
at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:288)
at org.jboss.invocation.ChainedInterceptor.processInvocation(ChainedInterceptor.java:61)
at org.jboss.as.ee.component.ViewService$View.invoke(ViewService.java:185)
at org.jboss.as.ejb3.remote.protocol.versionone.MethodInvocationMessageHandler.invokeMethod(MethodInvocationMessageHandler.java:319)
at org.jboss.as.ejb3.remote.protocol.versionone.MethodInvocationMessageHandler.access$100(MethodInvocationMessageHandler.java:68)
at org.jboss.as.ejb3.remote.protocol.versionone.MethodInvocationMessageHandler$1.run(MethodInvocationMessageHandler.java:201)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:473)
at java.util.concurrent.FutureTask.run(FutureTask.java:262)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1152)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:622)
at java.lang.Thread.run(Thread.java:748)
at org.jboss.threads.JBossThread.run(JBossThread.java:122)
Caused by: java.lang.StackOverflowError
at java.security.AccessController.doPrivileged(Native Method)
at sun.reflect.ClassDefiner.defineClass(ClassDefiner.java:57)
at sun.reflect.MethodAccessorGenerator$1.run(MethodAccessorGenerator.java:399)
at sun.reflect.MethodAccessorGenerator$1.run(MethodAccessorGenerator.java:396)
at java.security.AccessController.doPrivileged(Native Method)
at sun.reflect.MethodAccessorGenerator.generate(MethodAccessorGenerator.java:395)
at sun.reflect.MethodAccessorGenerator.generateSerializationConstructor(MethodAccessorGenerator.java:113)
at sun.reflect.ReflectionFactory.generateConstructor(ReflectionFactory.java:388)
at sun.reflect.ReflectionFactory.newConstructorForSerialization(ReflectionFactory.java:351)
at org.jboss.marshalling.reflect.SerializableClass.lookupNonInitConstructor(SerializableClass.java:556)
at org.jboss.marshalling.reflect.SerializableClass.<init>(SerializableClass.java:156)
at org.jboss.marshalling.reflect.SerializableClassRegistry.lookup(SerializableClassRegistry.java:90)
at org.jboss.marshalling.cloner.SerializingCloner.clone(SerializingCloner.java:165)
at org.jboss.marshalling.cloner.SerializingCloner.clone(SerializingCloner.java:128)
at org.jboss.marshalling.cloner.SerializingCloner.cloneFields(SerializingCloner.java:393)
at org.jboss.marshalling.cloner.SerializingCloner.initSerializableClone(SerializingCloner.java:301)
at org.jboss.marshalling.cloner.SerializingCloner.initSerializableClone(SerializingCloner.java:276)
at org.jboss.marshalling.cloner.SerializingCloner.initSerializableClone(SerializingCloner.java:276)
at org.jboss.marshalling.cloner.SerializingCloner.initSerializableClone(SerializingCloner.java:276)
at org.jboss.marshalling.cloner.SerializingCloner.clone(SerializingCloner.java:251)
at org.jboss.marshalling.cloner.SerializingCloner.clone(SerializingCloner.java:128)
at org.jboss.marshalling.cloner.SerializingCloner.cloneFields(SerializingCloner.java:393)
at org.jboss.marshalling.cloner.SerializingCloner.initSerializableClone(SerializingCloner.java:301)
at org.jboss.marshalling.cloner.SerializingCloner.initSerializableClone(SerializingCloner.java:276)
at org.jboss.marshalling.cloner.SerializingCloner.clone(SerializingCloner.java:251)
at org.jboss.marshalling.cloner.SerializingCloner.clone(SerializingCloner.java:128)
at org.jboss.as.ejb3.remote.LocalEjbReceiver.clone(LocalEjbReceiver.java:313)
at org.jboss.as.ejb3.remote.LocalEjbReceiver.clone(LocalEjbReceiver.java:304)
at org.jboss.as.ejb3.remote.LocalEjbReceiver.processInvocation(LocalEjbReceiver.java:271)
at org.jboss.ejb.client.EJBClientInvocationContext.sendRequest(EJBClientInvocationContext.java:184)
at org.jboss.ejb.client.EJBObjectInterceptor.handleInvocation(EJBObjectInterceptor.java:58)
at org.jboss.ejb.client.EJBClientInvocationContext.sendRequest(EJBClientInvocationContext.java:186)
at org.jboss.ejb.client.EJBHomeInterceptor.handleInvocation(EJBHomeInterceptor.java:83)
at org.jboss.ejb.client.EJBClientInvocationContext.sendRequest(EJBClientInvocationContext.java:186)
at org.jboss.ejb.client.TransactionInterceptor.handleInvocation(TransactionInterceptor.java:42)
at org.jboss.ejb.client.EJBClientInvocationContext.sendRequest(EJBClientInvocationContext.java:186)
at org.jboss.ejb.client.ReceiverInterceptor.handleInvocation(ReceiverInterceptor.java:125)
at org.jboss.ejb.client.EJBClientInvocationContext.sendRequest(EJBClientInvocationContext.java:186)
at org.jboss.ejb.client.EJBInvocationHandler.sendRequestWithPossibleRetries(EJBInvocationHandler.java:255)
at org.jboss.ejb.client.EJBInvocationHandler.doInvoke(EJBInvocationHandler.java:200)
at org.jboss.ejb.client.EJBInvocationHandler.doInvoke(EJBInvocationHandler.java:183)
at org.jboss.ejb.client.EJBInvocationHandler.invoke(EJBInvocationHandler.java:146)
at com.sun.proxy.$Proxy317.getSession(Unknown Source)
at com.kable.newsstand.kdsejb.audit.AuditInterceptor.logIt(AuditInterceptor.java:107)
at com.kable.newsstand.kdsejb.audit.AuditInterceptor.postFlush(AuditInterceptor.java:89)
at org.hibernate.event.internal.AbstractFlushingEventListener.postPostFlush(AbstractFlushingEventListener.java:401)
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:66)
at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1195)

最后一部分不断重复

at com.kable.newsstand.kdsejb.audit.AuditInterceptor.logIt(AuditInterceptor.java:115)
at com.kable.newsstand.kdsejb.audit.AuditInterceptor.postFlush(AuditInterceptor.java:89)
at org.hibernate.event.internal.AbstractFlushingEventListener.postPostFlush(AbstractFlushingEventListener.java:401)
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:66)
at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1195)

最佳答案

所以我想出的解决方案是在 logIt() 之前从集合中删除实体。

public void postFlush(Iterator iterator) {
    try {
        for (Iterator<Auditable> iter = inserts.iterator(); iter.hasNext();) {
//          for (Auditable entity : inserts) {
            Auditable entity = iter.next();
            iter.remove();
            logIt("Saved", entity);
        }

        for (Iterator<Auditable> iter = updates.iterator(); iter.hasNext();) {
//          for (Auditable entity : updates) {
            Auditable entity = iter.next();
            iter.remove();
            logIt("Updated", entity);
        }

        for (Iterator<Auditable> iter = deletes.iterator(); iter.hasNext();) {
//          for (Auditable entity : deletes) {
            Auditable entity = iter.next();
            iter.remove();
            logIt("Deleted", entity);
        }

    } finally {
        inserts.clear();
        updates.clear();
        deletes.clear();
    }
}

关于java - 如何避免 Hibernate 拦截器审核中出现 StackOverflowException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50687022/

相关文章:

java - 从 Recycler View 中删除条目时出现问题

java - 通过java代码查看jar文件

java - 获取文件路径 - Hibernate SessionFactory

java - Spring Security BadCredentialsException 异常

java - Selenium、Firefox 和 GeckoDriver

Java 并发实践 : 3. 5.4 有效不可变对象(immutable对象):我们是否需要线程安全集合容器来存储有效不可变对象(immutable对象)

java - Hibernate 级联删除未按预期工作

java - Hibernate:使用集中的属性值创建 HQL 查询

java - Criteria 在 order by 子句中使用聚合函数

java - 使用 Hibernate POJO 的 SQL 外键错误