Java 与计划作业合流的UnonqueObjectException

Java 与计划作业合流的UnonqueObjectException,java,hibernate,confluence,Java,Hibernate,Confluence,在Confluence的quartz工作中遇到了一个很大的问题,关于页面创建 工作类别: @ComponentImport private final SpaceManager spaceManager; @Autowired private final GeneralConfig config; @Autowired private final PageCreator pageCreator; @ComponentImport private final PageManager pageMan

在Confluence的quartz工作中遇到了一个很大的问题,关于页面创建

工作类别:

@ComponentImport
private final SpaceManager spaceManager;
@Autowired
private final GeneralConfig config;
@Autowired
private final PageCreator pageCreator;
@ComponentImport
private final PageManager pageManager;

@Autowired
public ReportingPluginJob(GeneralConfig config, SpaceManager spaceManager, PageCreator pageCreator,
        PageManager pageManager) {
    this.config = config;
    this.spaceManager = spaceManager;
    this.pageCreator = pageCreator;
    this.pageManager = pageManager;
}

private static final org.slf4j.Logger LOG = LoggerFactory.getLogger(ReportingPluginJob.class);

@Override
public void execute(JobExecutionContext jec) throws JobExecutionException {

    Collection<String> keys = spaceManager.getAllSpaceKeys(SpaceStatus.CURRENT);

    String parentPageName = config.getSchedulerWeeklyParentPageName();

    for (String key : keys) {

        Page parentPage
                = pageManager.getPage(key, parentPageName);

        if (parentPage != null) {
            LOG.debug("Creating weekly report for space " + key);
            long pageId = parentPage.getId();
            try {
                pageCreator.createEazyBiReport(key, pageId);
            } catch (ApplicationException e) {
                LOG.error("FAILED TO CREATE A REPORT FOR SPACE " + key + " with error: " + System.lineSeparator()
                        + e.getMessage());
            }

        }

    }

}
最后一个让我抓狂的例外是:

org.springframework.dao.DuplicateKeyException: A different object with the same identifier value was already associated with the session : [com.atlassian.confluence.spaces.Space#31653891]; nested exception is org.hibernate.NonUniqueObjectException: A different object with the same identifier value was already associated with the session : [com.atlassian.confluence.spaces.Space#31653891]
at org.springframework.orm.hibernate5.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:259)
at org.springframework.orm.hibernate5.HibernateTemplate.doExecute(HibernateTemplate.java:362)
at org.springframework.orm.hibernate5.HibernateTemplate.executeWithNativeSession(HibernateTemplate.java:326)
at org.springframework.orm.hibernate5.HibernateTemplate.saveOrUpdate(HibernateTemplate.java:704)
at com.atlassian.confluence.core.persistence.hibernate.HibernateObjectDao.saveRaw(HibernateObjectDao.java:207)
at com.atlassian.confluence.pages.persistence.dao.hibernate.CachingPageDao.saveRaw(CachingPageDao.java:157)
at com.atlassian.confluence.core.DefaultContentEntityManager.saveContentEntity(DefaultContentEntityManager.java:150)
at com.atlassian.confluence.pages.DefaultPageManager.saveContentEntity(DefaultPageManager.java:1388)
at sun.reflect.GeneratedMethodAccessor2132.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:302)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:281)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at com.atlassian.spring.interceptors.SpringProfilingInterceptor.invoke(SpringProfilingInterceptor.java:16)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at com.atlassian.confluence.util.profiling.ConfluenceMonitoringMethodInterceptor.invoke(ConfluenceMonitoringMethodInterceptor.java:34)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:208)
at com.sun.proxy.$Proxy105.saveContentEntity(Unknown Source)
at sun.reflect.GeneratedMethodAccessor2132.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.atlassian.plugin.util.ContextClassLoaderSettingInvocationHandler.invoke(ContextClassLoaderSettingInvocationHandler.java:26)
at com.sun.proxy.$Proxy253.saveContentEntity(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:302)
at org.eclipse.gemini.blueprint.service.importer.support.internal.aop.ServiceInvoker.doInvoke(ServiceInvoker.java:56)
at org.eclipse.gemini.blueprint.service.importer.support.internal.aop.ServiceInvoker.invoke(ServiceInvoker.java:60)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:133)
at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:121)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.eclipse.gemini.blueprint.service.util.internal.aop.ServiceTCCLInterceptor.invokeUnprivileged(ServiceTCCLInterceptor.java:70)
at org.eclipse.gemini.blueprint.service.util.internal.aop.ServiceTCCLInterceptor.invoke(ServiceTCCLInterceptor.java:53)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.eclipse.gemini.blueprint.service.importer.support.LocalBundleContextAdvice.invoke(LocalBundleContextAdvice.java:57)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:133)
at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:121)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:208)
at com.sun.proxy.$Proxy2863.saveContentEntity(Unknown Source)
at com.censored.atlassian.plugins.service.PageCreator.createEazyBiReport(PageCreator.java:128)
at com.censored.atlassian.plugins.service.ReportingPluginJob.execute(ReportingPluginJob.java:64)
at com.atlassian.confluence.plugin.descriptor.JobModuleDescriptor$DelegatingPluginJob.lambda$execute$0(JobModuleDescriptor.java:113)
at com.atlassian.confluence.impl.vcache.VCacheRequestContextManager.doInRequestContextInternal(VCacheRequestContextManager.java:87)
at com.atlassian.confluence.impl.vcache.VCacheRequestContextManager.doInRequestContext(VCacheRequestContextManager.java:71)
at com.atlassian.confluence.plugin.descriptor.JobModuleDescriptor$DelegatingPluginJob.execute(JobModuleDescriptor.java:112)
at org.quartz.core.JobRunShell.run(JobRunShell.java:223)
at com.atlassian.confluence.schedule.quartz.ConfluenceQuartzThreadPool.lambda$runInThread$0(ConfluenceQuartzThreadPool.java:16)
at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:549)
根据堆栈跟踪,我在页面保存(saveContentEntity)过程中遇到了一个问题。看起来,页面保存也需要更新与页面关联的空间。 问题来了。根据堆栈跟踪,此空间已经存在于Hibernate会话中<实际会话的代码>退出和清除无效。
有什么建议吗?如何处理此异常,或者如何从Hibernate会话中实际删除空间?

显然,您的代码中发生了以下情况:

  • SpaceManager
    中,会话找到ID为1的
    session
    对象(假设为),并返回该对象
  • 您的方法使用此引用与另一个对象关联,即
    页面
  • 最后,您的
    PageManager
    将这些数据保存在数据库中
  • 您的问题可能是:
    从第1步到第2步,当
    SpaceManager
    返回数据时,事务结束,返回的对象变为
    分离的

    之后,当您调用
    save
    时,您试图持久化一个分离的
    会话
    ,但它已经存在于数据库中。然后你会得到一个例外

    您可以尝试使用
    merge
    方法,或从实体中的此引用中删除
    级联


    (请提供您的实体代码,以便在有任何错误或不适用于您的情况下,我可以改进我的答案。)

    我们遇到了相同的问题,解决方法是将创建页面(以及加载其他对象,如空间或父页面)的代码全部放入事务中:

    private void createNewPage(String title, String pageInput, Long parentPageId, String spaceKey) {
        transactionTemplate.execute(() -> {
            Page page = new Page();
            page.setTitle(title);
            page.setBodyAsString(pageInput);
    
            Space space = spaceManager.getSpace(spaceKey);
            page.setSpace(space);
    
            Page parentPage = pageManager.getPage(parentPageId);
            if (parentPage != null) {
                page.setParentPage(parentPage);
                parentPage.addChild(page);
            }
    
            pageManager.saveContentEntity(page, DefaultSaveContext.DEFAULT);
    
            return null;
        });
    }
    

    com.atlassian.sal.api.transaction.TransactionTemplate可以注入@ComponentImport

    您可以提供一段代码,在其中实际调用session.save()和session.execute()/session.clear()。我想知道的另一个信息是。。(因为您正在循环中调用save)您是否能够确定异常是在第一次尝试保存时引发的,还是在引发异常之前它能够保存一些对象?请提供实体类。
    private void createNewPage(String title, String pageInput, Long parentPageId, String spaceKey) {
        transactionTemplate.execute(() -> {
            Page page = new Page();
            page.setTitle(title);
            page.setBodyAsString(pageInput);
    
            Space space = spaceManager.getSpace(spaceKey);
            page.setSpace(space);
    
            Page parentPage = pageManager.getPage(parentPageId);
            if (parentPage != null) {
                page.setParentPage(parentPage);
                parentPage.addChild(page);
            }
    
            pageManager.saveContentEntity(page, DefaultSaveContext.DEFAULT);
    
            return null;
        });
    }