2

我们得到了这样的设置:一个文档有一个元素列表,这些元素是 Element 的子类,在这个例子中是 Link。链接与资源具有@OneToOne 关系。只要我们一步创建整个对象图,一切都会奏效。

我们现在更改了代码以创建 Resource 实例,将其持久化并将其传递给我们的客户端(Web,序列化为 JSON 的对象)。客户端创建 Document 的实例并添加与现有资源的链接。之后,我们尝试持久化新文档。

此时操作失败,因为 JPA 尝试使用相同的 id 持久化已经存在的 Resource。我认为发生这种情况是因为 Resource 已分离,JPA 无法确定该实体是否必须合并或持久化。当我删除 CascadeType.ALL 并用 CascadeType.MERGE 替换它时,一切都很好,除了资源根本没有更新。

基本上工作流程如下:

  • 创建和持久化资源
  • 离开交易
  • 开始交易
  • 使用链接和(分离的)资源创建文档
  • 错误

我们的 JPA 配置是否存在问题(查看我们的实体)?如果你能给我一个提示,我很想给你一杯啤酒……:-) TYIA。

我们的设置是:EclipseLink 2.4.1、Spring Data JPA 1.3.0(使用@Repository)和 PostgreSQL 9.2。

文档:

@Entity
public class Document implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
    @OrderColumn
    @JoinColumn(name = "document_id")
    private List<Element> elements = new ArrayList<Element>();

    // getters and setters
}

元素:

@Entity
@Inheritance(strategy = InheritanceType.
public class Element implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "document_id", insertable = false, updatable = false)
private Document document;

    // getters and setters
}

关联:

@Entity
public class Link extends Element {

    @Column
    private String appearance;

    @Column
    private String referenceType;

    @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
    private Resource resource;

    // getters and setters
}

资源:

@Entity
public class Resource {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    @Column
    private String uri;

    // getters and setters
}
4

2 回答 2

1

CascadeType.MERGE 旨在级联合并操作,例如:

Document document = getDetachedDocumentSomehow();
em.merge(document)

将整个对象图合并到持久性上下文。

您需要做的是将分离的 Resource 实例合并到第二个事务的持久性上下文中。

Resource resource = getDetachedResourceSomehow();
mergedResource = em.merge(resource)
link.setResource(mergedResource)

编辑: 不使用实体管理器:

Resource resource = getDetachedResourceSomehow();
managedResource = resourceDao.findOne(resource.getId)
link.setResource(managedResource)

要点是您必须设置资源的托管实例。

于 2013-04-16T14:29:20.400 回答
0

我想我在 EclipseLink 实现中触发了一个错误。对于偶然发现相同问题的人,这里有一些有用的链接:

http://chandanpandey.com/2012/12/22/persisting-a-detached-entity-in-jpa https://bugs.eclipse.org/bugs/show_bug.cgi?id=324941

解决方法是禁止 CascadeType.ALL

@OneToOne(cascade = CascadeType.ALL, orphanRemoval = true)

而是使用

@OneToOne(cascade = {CascadeType.MERGE, CascadeType.REFRESH, CascadeType.REMOVE, CascadeType.DETACH}, orphanRemoval = false)

并禁用 orphanRemoval。这肯定有一些缺点,我希望这将在 EclipseLink 的未来版本中得到修复(我对此表示怀疑,因为错误报告来自 2010 年)。

希望这对任何人都有帮助。

于 2013-04-18T07:02:32.457 回答