5

我已经实现了一个基于 JPA 和 JAXB 的 REST API。

我有一个大致像这样的课程(非常简化):

@Entity
@XmlRootElement
...
public class Thing {
    @Id
    @GeneratedValue
    ...
    @XmlAttribute
    @XmlID
    @XmlJavaTypeAdapter(JAXBLongAdapter.class)
    private Long id;
    ...
}

Hibernate(我当前的 JPA 提供程序)生成数字作为 id 值,但它们自然只对一种类型是唯一的,在本例中为 Thing。

现在 XSD 说 xsd:id (@XmlID) 是一个不能是普通数字的 NCString,所以我在 JAXBLongAdapter 中的数字前添加了一个“_”。- 像'_1'

现在模式验证器抱怨:

[org.xml.sax.SAXParseException: cvc-id.2: There are multiple occurrences of ID value '_1'.]

如果我理解正确,则 xsd:ID 元素必须具有在 xml 文档中全局唯一的(字符串)值。但这与在数据库中使用 ID 的常见方式完全相反。

现在我该怎么做?我想到了三件事:

  • 为具有特定类型前缀的每种类型创建一个 JAXBLongAdapter?
  • 使用另一个 JPA id 生成器,也许是 UUID?- 但是哪一个?
  • 停止使用@XmlID 和@XmlIDREF,这会造成冗余和一般混乱。

看来我现在必须更改数据库模式以使用不同的 ID。- 但如果 ID 保持简短就更好了,因为它们出现在 URL 中。

我的问题:是否有一个速度相当快且全球唯一的 ID 生成器?还是有另一种解决方法?

编辑:

这个 hack 有点工作,让 JPA ID 保持不变。

@XmlID
@XmlAttribute(name="id")
private String getXmlID(){
    return String.format("%s-%s", this.getClass().getSimpleName(), this.getId().toString());
}

private void setXmlID(String xmlid){
    String prefix = String.format("%s-", this.getClass().getSimpleName());
    if(xmlid.startsWith(prefix)){
        this.id = Long.parseLong(xmlid.substring(prefix.length()));
    }else{
        throw new IllegalArgumentException(xmlid+" does not look like "+prefix+"###");
    }
}

通过将 JAXB 注释从字段移动到 XmlID 的专用私有 getter/setter。

4

2 回答 2

2

注意: 我是EclipseLink JAXB (MOXy)负责人,也是JAXB (JSR-222)专家组的成员。

答案应该是什么

答案应该是在和属性@XmlSchemaType上都使用注释。不幸的是,JAXB RI 没有利用这种组合,而 EclipseLink MOXy 仅将它用于. 我输入了以下 MOXy 错误,如果您对此方法感兴趣,我们可以修复它:@XmlID@XmlIDREF@XmlID

员工

package forum11791735;

import java.util.List;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Employee {

    @XmlID
    @XmlJavaTypeAdapter(JAXBLongAdapter.class)
    @XmlSchemaType(name="long")
    private Long id;

    @XmlIDREF
    @XmlSchemaType(name="long")
    private Employee manager;

    @XmlElement(name="report")
    private List<Employee> reports;

}

解决方法

您看到的错误似乎是由于架构验证。您是否可以禁用模式验证或设置 aValidationEventHandlerUnmarshaller忽略这些错误?

备择方案

如果您使用@XmlID/@XmlIDREF来映射双向关系,那么您可能对 MOXy 的@XmlInverseReference扩展感兴趣:

于 2012-08-03T13:22:46.137 回答
2

这正是我一段时间以来所做的。

@XmlID你可以问自己,这个域对象在编组时实际上是什么?

我曾经想过@XmlID并且@XmlIDREF可以解决 JAXB 中的循环问题。

下面是我对 JPA 实体以及 JAXB 注释所做的事情。

不要放弃simple JPA @Id。这就是 JPA 的核心。

@XmlRootElement
public class Parent {

    @Id
    @XmlAttribute
    private Long id;

    @OneToMany
    @XmlElement(name = "child")
    @XmlElementWrapper
    private Collection<Child> children;
}


@XmlRootElement
public class Child {

    @XmlAttribute
    private Long getParentId() {
        return parent.getId();
    }

    @Id
    @XmlAttribute
    private Long id;

    @ManyToOne
    @XmlTransient // for preventing infinite circular problem
    private Parent parent;
}
于 2012-08-03T12:34:15.860 回答