Tapestry 5.3.8 + Jetty + Hibernate 4.3.5 + XAMPP 1.8.3 - IdClass 使用(派生)实体,为啥在尝试合并时它们会分离?

Posted

技术标签:

【中文标题】Tapestry 5.3.8 + Jetty + Hibernate 4.3.5 + XAMPP 1.8.3 - IdClass 使用(派生)实体,为啥在尝试合并时它们会分离?【英文标题】:Tapestry 5.3.8 + Jetty + Hibernate 4.3.5 + XAMPP 1.8.3 - IdClass uses (derived) entities, why are they detached when trying to merge?Tapestry 5.3.8 + Jetty + Hibernate 4.3.5 + XAMPP 1.8.3 - IdClass 使用(派生)实体,为什么在尝试合并时它们会分离? 【发布时间】:2015-02-12 09:15:13 【问题描述】:

使用问题中的组合,我有以下实体:

一些任务:

@Entity
public class Task 
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

一些工人:

@Entity
public class Worker 

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @NonVisual
    private long id;


还有一些关于任务的状态:

@Entity
@IdClass(TaskStatusId.class)
public class TaskStatus 

    @Id
    @ManyToOne
    private Worker worker

    @Id
    @ManyToOne
    private Task task;


注意:我在 TaskStatus 和其他两个实体之间只有单向关系。 Task 和 Worker 都不包含 List。

现在我有一些用于 Tasks 和 Workers 的数据库条目,还有一些用于 stati 的条目。这些是在应用程序启动时创建并保留的测试对象。这一切都很好。

现在我有一个 Tapestry 页面,它可以找到一个任务和一个工作人员并将它们存储在页面属性中。

SomePage.java:

@Property
@Persist
private Task someTask;

@Property
@Persist
private Worker someWorker;

SomePage.tml:

<t:someComponent selectedTask="someTask" selectedWorker="someWorker"/>

页面中的组件接收 Task 和 Worker 作为参数,并使用我的 DAO 层来查找 TaskStatus,这也可以。

SomeComponent.java:

@Parameter(required=true)
@Property
Task selectedTask;

@Parameter(required=true)
@Property
Worker selectedWorker;

@Property
@Persist
TaskStatus someStatus;

void setupRender() 
    if (selectedTask != null && user != null) 
        if (someStatus == null || someStatus.getTask() != selectedTask) 
            someStatus = taskStatusDAO.findByWorkerAndTask(selectedWorker, selectedTask);
        
    


public void onActionFromUpdateStatus() 
    someStatus = taskStatusDAO.update(someStatus);

最后,我在组件中有一个 Actionlink,它执行以下操作:

更新 TaskStatus 对象中的日期字段 尝试使用我的 DAO 层将更改保存到对象,更具体的是我的合并方法:

界面:

@CommitAfter
public T update(T entity);

实施:

public T update(T entity) 
    return em.merge(entity);

当我尝试合并 TaskStatus 对象时,出现异常:

org.hibernate.PersistentObjectException: 
detached entity passed to persist: some.package.Task

堆栈跟踪:

[ERROR] TapestryModule.RequestExceptionHandler Processing of request failed with uncaught exception: org.hibernate.PersistentObjectException: detached entity passed to persist: some.package.entities.Task
org.apache.tapestry5.ioc.internal.OperationException: org.hibernate.PersistentObjectException: detached entity passed to persist: some.package.entities.Task [at classpath:some/package/components/affected/someComponent.tml, line 21]
    at org.apache.tapestry5.ioc.internal.OperationTrackerImpl.logAndRethrow(OperationTrackerImpl.java:121)
    at org.apache.tapestry5.ioc.internal.OperationTrackerImpl.invoke(OperationTrackerImpl.java:88)
    at org.apache.tapestry5.ioc.internal.PerThreadOperationTracker.invoke(PerThreadOperationTracker.java:87)
    at org.apache.tapestry5.ioc.internal.RegistryImpl.invoke(RegistryImpl.java:1124)
    at org.apache.tapestry5.internal.structure.ComponentPageElementResourcesImpl.invoke(ComponentPageElementResourcesImpl.java:146)
    at org.apache.tapestry5.internal.structure.ComponentPageElementImpl.triggerContextEvent(ComponentPageElementImpl.java:1058)
    at org.apache.tapestry5.internal.services.AjaxComponentEventRequestHandler.handle(AjaxComponentEventRequestHandler.java:110)
    at org.apache.tapestry5.internal.services.ajax.AjaxFormUpdateFilter.handle(AjaxFormUpdateFilter.java:56)
    at $ComponentEventRequestHandler_20d7f9a2019d1.handle(Unknown Source)
    at $ComponentEventRequestHandler_20d7f9a2019c9.handle(Unknown Source)
    at org.apache.tapestry5.internal.services.AjaxFilter.handle(AjaxFilter.java:42)
    at $ComponentEventRequestHandler_20d7f9a2019cb.handle(Unknown Source)
    at org.apache.tapestry5.upload.internal.services.UploadExceptionFilter.handle(UploadExceptionFilter.java:75)
    at $ComponentEventRequestHandler_20d7f9a2019cb.handle(Unknown Source)
    at org.apache.tapestry5.services.TapestryModule$41.handle(TapestryModule.java:2475)
    at $ComponentEventRequestHandler_20d7f9a2019cb.handle(Unknown Source)
    at $ComponentEventRequestHandler_20d7f9a2018ce.handle(Unknown Source)
    at org.apache.tapestry5.internal.services.ComponentRequestHandlerTerminator.handleComponentEvent(ComponentRequestHandlerTerminator.java:43)
    at some.package.services.PageProtectionFilter.handleComponentEvent(PageProtectionFilter.java:94)
    at $ComponentRequestFilter_20d7f9a2018cd.handleComponentEvent(Unknown Source)
    at $ComponentRequestHandler_20d7f9a2018d0.handleComponentEvent(Unknown Source)
    at org.apache.tapestry5.services.InitializeActivePageName.handleComponentEvent(InitializeActivePageName.java:39)
    at $ComponentRequestHandler_20d7f9a2018d0.handleComponentEvent(Unknown Source)
    at $ComponentRequestHandler_20d7f9a20189c.handleComponentEvent(Unknown Source)
    at org.apache.tapestry5.internal.services.ComponentEventDispatcher.dispatch(ComponentEventDispatcher.java:46)
    at $Dispatcher_20d7f9a20189f.dispatch(Unknown Source)
    at $Dispatcher_20d7f9a201898.dispatch(Unknown Source)
    at org.apache.tapestry5.services.TapestryModule$RequestHandlerTerminator.service(TapestryModule.java:302)
    at some.package.services.AppModule$1.service(AppModule.java:175)
    at $RequestFilter_20d7f9a201897.service(Unknown Source)
    at $RequestHandler_20d7f9a201899.service(Unknown Source)
    at org.apache.tapestry5.internal.services.RequestErrorFilter.service(RequestErrorFilter.java:26)
    at $RequestHandler_20d7f9a201899.service(Unknown Source)
    at org.apache.tapestry5.services.TapestryModule$3.service(TapestryModule.java:902)
    at $RequestHandler_20d7f9a201899.service(Unknown Source)
    at org.apache.tapestry5.services.TapestryModule$2.service(TapestryModule.java:892)
    at $RequestHandler_20d7f9a201899.service(Unknown Source)
    at org.apache.tapestry5.internal.services.StaticFilesFilter.service(StaticFilesFilter.java:90)
    at $RequestHandler_20d7f9a201899.service(Unknown Source)
    at org.apache.tapestry5.internal.services.CheckForUpdatesFilter$2.invoke(CheckForUpdatesFilter.java:105)
    at org.apache.tapestry5.internal.services.CheckForUpdatesFilter$2.invoke(CheckForUpdatesFilter.java:96)
    at org.apache.tapestry5.ioc.internal.util.ConcurrentBarrier.withRead(ConcurrentBarrier.java:85)
    at org.apache.tapestry5.internal.services.CheckForUpdatesFilter.service(CheckForUpdatesFilter.java:119)
    at $RequestHandler_20d7f9a201899.service(Unknown Source)
    at $RequestHandler_20d7f9a20188d.service(Unknown Source)
    at org.apache.tapestry5.services.TapestryModule$HttpServletRequestHandlerTerminator.service(TapestryModule.java:253)
    at org.got5.tapestry5.jquery.services.AjaxUploadServletRequestFilter.service(AjaxUploadServletRequestFilter.java:27)
    at $HttpServletRequestHandler_20d7f9a20188f.service(Unknown Source)
    at org.apache.tapestry5.internal.gzip.GZipFilter.service(GZipFilter.java:53)
    at $HttpServletRequestHandler_20d7f9a20188f.service(Unknown Source)
    at org.apache.tapestry5.upload.internal.services.MultipartServletRequestFilter.service(MultipartServletRequestFilter.java:44)
    at $HttpServletRequestHandler_20d7f9a20188f.service(Unknown Source)
    at org.apache.tapestry5.internal.services.IgnoredPathsFilter.service(IgnoredPathsFilter.java:62)
    at $HttpServletRequestFilter_20d7f9a20188a.service(Unknown Source)
    at $HttpServletRequestHandler_20d7f9a20188f.service(Unknown Source)
    at org.apache.tapestry5.services.TapestryModule$1.service(TapestryModule.java:852)
    at $HttpServletRequestHandler_20d7f9a20188f.service(Unknown Source)
    at $HttpServletRequestHandler_20d7f9a201888.service(Unknown Source)
    at org.apache.tapestry5.TapestryFilter.doFilter(TapestryFilter.java:171)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1148)
    at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:387)
    at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
    at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:181)
    at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)
    at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:417)
    at org.mortbay.jetty.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:230)
    at org.mortbay.jetty.handler.HandlerCollection.handle(HandlerCollection.java:114)
    at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
    at org.mortbay.jetty.Server.handle(Server.java:324)
    at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:535)
    at org.mortbay.jetty.HttpConnection$RequestHandler.content(HttpConnection.java:880)
    at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:747)
    at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:218)
    at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
    at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:409)
    at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:520)
Caused by: org.apache.tapestry5.runtime.ComponentEventException: org.hibernate.PersistentObjectException: detached entity passed to persist: some.package.entities.Task [at classpath:some/package/components/affected/someComponent.tml, line 21]
    at org.apache.tapestry5.internal.structure.ComponentPageElementImpl.processEventTriggering(ComponentPageElementImpl.java:1141)
    at org.apache.tapestry5.internal.structure.ComponentPageElementImpl.access$3100(ComponentPageElementImpl.java:61)
    at org.apache.tapestry5.internal.structure.ComponentPageElementImpl$5.invoke(ComponentPageElementImpl.java:1062)
    at org.apache.tapestry5.internal.structure.ComponentPageElementImpl$5.invoke(ComponentPageElementImpl.java:1060)
    at org.apache.tapestry5.ioc.internal.OperationTrackerImpl.invoke(OperationTrackerImpl.java:74)
    ... 74 more
Caused by: javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: some.package.entities.Task
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1763)
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1677)
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1683)
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.merge(AbstractEntityManagerImpl.java:1206)
    at $EntityManager_14b7d075216.merge($EntityManager_14b7d075216.java)
    at some.package.dao.AbstractDAOImpl.update(AbstractDAOImpl.java:34)
    at $TaskStatusDAO_20d7f9a2019e4.update(Unknown Source)
    at $TaskStatusDAO_20d7f9a2019e5.advised$update_20d7f9a2019eb(Unknown Source)
    at $TaskStatusDAO_20d7f9a2019e5$Invocation_update_20d7f9a2019ea.proceedToAdvisedMethod(Unknown Source)
    at org.apache.tapestry5.internal.plastic.AbstractMethodInvocation.proceed(AbstractMethodInvocation.java:84)
    at org.apache.tapestry5.internal.jpa.CommitAfterMethodAdvice.advise(CommitAfterMethodAdvice.java:48)
    at org.apache.tapestry5.internal.plastic.AbstractMethodInvocation.proceed(AbstractMethodInvocation.java:86)
    at $TaskStatusDAO_20d7f9a2019e5.update(Unknown Source)
    at $TaskStatusDAO_20d7f9a201879.update(Unknown Source)
    at some.package.components.affected.someComponent.onActionFromSendConfirmation(someComponent.java:59)
    at some.package.components.affected.someComponent.dispatchComponentEvent(someComponent.java)
    at org.apache.tapestry5.internal.structure.ComponentPageElementImpl.dispatchEvent(ComponentPageElementImpl.java:932)
    at org.apache.tapestry5.internal.structure.ComponentPageElementImpl.processEventTriggering(ComponentPageElementImpl.java:1117)
    ... 78 more
Caused by: org.hibernate.PersistentObjectException: detached entity passed to persist: some.package.entities.Task
    at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:139)
    at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:75)
    at org.hibernate.tuple.entity.AbstractEntityTuplizer$IncrediblySillyJpaMapsIdMappedIdentifierValueMarshaller.getIdentifier(AbstractEntityTuplizer.java:494)
    at org.hibernate.tuple.entity.AbstractEntityTuplizer.getIdentifier(AbstractEntityTuplizer.java:342)
    at org.hibernate.persister.entity.AbstractEntityPersister.getIdentifier(AbstractEntityPersister.java:4746)
    at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:129)
    at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:76)
    at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:876)
    at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:858)
    at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:863)
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.merge(AbstractEntityManagerImpl.java:1196)
    ... 92 more

如果我创建一个单独的标识符并且不使用复合 ID,则更新/合并 TaskStatus 可以工作。但如果没有必要,我宁愿不改变它。

要使用派生 (?) 实体来使用复合 id,我需要做什么?

【问题讨论】:

【参考方案1】:

如果为每个 HTTP 请求创建了一个新的 Hibernate 会话(这很可能发生),那么您面临的问题是由于您系统地在会话中保存数据库中的数据(属性 'someTask' , 'someWorker' 和 'someStatus')。

发生了什么:

第一个 HTTP 请求:渲染页面

创建了一个新的 Hibernate 会话。 在此 HTTP 请求结束时,您的属性值将存储在会话中,同时您的 Hibernate 会话已关闭。

第二个 HTTP 请求:来自更新状态的操作

创建了一个新的 Hibernate 会话。 Tapestry 从用户会话中获取属性值,并将它们重新注入到组件的 java 属性中。但是这些值不会连接到您的新 Hibernate 会话,这会导致您在使用其中之一调用 DAO 时遇到错误。

要解决这个问题,我认为你应该:

避免使用持久属性(我认为您在这里不需要这样做?), 在每个 HTTP 请求开始时调用 DAO 的“读取”方法,以获取连接到 Hibernate 会话的新 Hibernate 实体, 然后在这个实体上做你的事情。

【讨论】:

以上是关于Tapestry 5.3.8 + Jetty + Hibernate 4.3.5 + XAMPP 1.8.3 - IdClass 使用(派生)实体,为啥在尝试合并时它们会分离?的主要内容,如果未能解决你的问题,请参考以下文章

Tapestry 5.4 未找到用于休眠的驱动程序

Tapestry教程----组件类介绍

Tapestry 子窗体中的应用程序异常 - 参数绑定为 null

Tapestry 怎样从数据库中保存的图片显示出来。

Tapestry入门

Tapestry 应用程序中的会话超时 AJAX 错误