1

我的设置:

JBoss AS 7.1.1 使用 JPA、Hibernate(连接到 PostgreSQL)、RESTEasy、Jackson 1.9.2 for JSON、EclipseLink MOXy 2.4.1 for XML

我有两个实体 - GroupUser。一个组包含许多用户(一对多)。我声明了与 JPA 注释的双向关系(见下文),因此我必须通过使用@JsonManagedReference/ @JsonBackReference(对于 JSON)和@XmlInverseReference(对于 XML)来防止循环。这可以按预期工作,除非我尝试通过 REST 创建用户,否则在使用 XML 时我无法将她分配给组。使用 JSON 时,它可以工作:

Jackson JSON工作(成功添加 Jane Doe 并将她置于第 5 组):

POST request to /user
Content-Type: application/json
Accept: application/json
Body: {"name":"Jane Doe", "group":{"id":5}}

Response:

{
   "id": 12,
   "name":"Jane Doe"
}

Hibernate: insert into users (group_id, name, id) values (5, 'Jane Doe', 12)

这是添加 Jackson 用户后的 GroupEndpoint 响应:

GET request to /group
Accept: application/json

Response:

[{"id":5,"name":"My User Group","users":[{"id":12,"name":"Jane Doe"}]}

MOXy XML不起作用(Jane Doe 是孤儿):

POST request to /user
Content-Type: application/xml
Accept: application/xml
Body: <name>Jane Doe</name><group><id>5</id></group>

Response:

    <user><name>Jane Doe</name><id>12</id></user>

Hibernate: insert into users (group_id, name, id) values (null, 'Jane Doe', 12)

这是使用 MOXy 添加用户后的 GroupEndpoint 响应:

GET request to /group
Accept: application/xml

Response:

<collection>
  <group><id>5</id><name>My User Group</name></group>
</collection>

我试过了@XmlID@XmlIDREF同样的问题。 @XmlTransient同样会忽略 marshal 和 unmarshal 上的子父关系。

我的问题:

如何通过用户 REST 端点将用户添加到组,而不需要 MOXy 忽略新用户的组字段(标记为 @XmlInverseReference)?

我应该增加 REST 端点、创建 XmlAdapter 还是编写自定义序列化程序?我是否缺少具有不同注释的简单解决方案? 是否有一种完全不同的方法可以通过首选的 REST 公开关系对象? 任何帮助深表感谢。

这是组实体:

@Entity
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Group implements Serializable {
    @Id
    private Long id;

    @Column
    private String name;

    @OneToMany(mappedBy="group", cascade=CascadeType.ALL)
    @JsonManagedReference("group-user")
    private Set<User> users;

    // Omitting getters and setters etc...
}

这是用户实体:

@Entity
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class User implements Serializable {
    @Id
    private Long id;

    @Column
    private String name;

    @ManyToOne
    @JsonBackReference("group-user")
    @XmlInverseReference(mappedBy="users")
    private Group group;

    // Omitting getters and setters etc...
}

这是用户 REST 端点:

@Stateful
@Path("/user")
@TransactionAttribute
public class UserEndpoint {
    @PersistenceContext(type = PersistenceContextType.EXTENDED)
    private EntityManager em;

    @POST
    @Consumes({"application/xml","application/json"})
    public User create(User entity) {
        em.joinTransaction();
        em.persist(entity);
        return entity;
    }
}

这是 Group REST 端点(包括在内以表明需要防止循环):

@Stateful
@Path("/group")
@TransactionAttribute
public class GroupEndpoint {
    @PersistenceContext(type = PersistenceContextType.EXTENDED)
    private EntityManager em;

    @GET
    @Produces({"application/xml","application/json"})
    public List<Group> listAll() {
        final List<Group> results = em.createQuery("SELECT x FROM Group x").getResultList();
        return results;
    }
}

这是使用Jackson添加用户后的 GroupEndpoint MOXy响应,以测试周期:

GET request to /group
Accept: application/xml

Response:

<collection>
  <group>
     <id>5</id>
     <name>My User Group</name>
     <users><id>12</id><name>Jane Doe</name></users>
  </group>
</collection>
4

1 回答 1

2

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

现在已在 EclipseLink 2.5.0 中实现了对您的用例的支持。您可以从以下链接下载夜间标签(从 2013 年 3 月 1 日开始)。

JAVA模型

现在 MOXy 支持@XmlInverseReference在关系的两个方向上指定注释。单独指定时,它的处理@XmlTransient方式与编组一样,要使其可写,您需要将其与@XmlElement.

用户

import java.io.Serializable;
import javax.persistence.*;
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.XmlInverseReference;

@Entity
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class User implements Serializable {
    @Id
    private Long id;

    @Column
    private String name;

    @ManyToOne
    //@JsonBackReference("group-user")
    @XmlElement
    @XmlInverseReference(mappedBy="users")
    private Group group;

    public Group getGroup() {
        return group;
    }

    // Omitting other getters and setters etc...
}

团体

import java.io.Serializable;
import java.util.Set;
import javax.persistence.*;
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.XmlInverseReference;

@Entity
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Group implements Serializable {
    @Id
    private Long id;

    @Column
    private String name;

    @OneToMany(mappedBy="group", cascade=CascadeType.ALL)
    //@JsonManagedReference("group-user")
    @XmlElement
    @XmlInverseReference(mappedBy="group")
    private Set<User> users;

    public Set<User> getUsers() {
        return users;
    }

    // Omitting other getters and setters etc...
}

演示代码

演示

下面是一些可以运行以证明一切正常的代码。除了 XML 用例之外,MOXy 还可用于处理 JSON。

import java.io.StringReader;
import javax.xml.bind.*;
import javax.xml.transform.stream.StreamSource;
import org.eclipse.persistence.jaxb.UnmarshallerProperties;

public class Demo {

    public static void main(String[] args) throws Exception {

        JAXBContext jc = JAXBContext.newInstance(User.class);
        Unmarshaller unmarshaller = jc.createUnmarshaller();

        // XML USE CASE
        StringReader xml = new StringReader("<user><name>Jane Doe</name><group><id>5</id></group></user>");
        User userFromXML = (User) unmarshaller.unmarshal(xml);
        System.out.println(userFromXML.getGroup().getUsers());

        // JSON USE CASE
        StringReader json = new StringReader("{\"name\":\"Jane Doe\", \"group\":{\"id\":5}}");
        unmarshaller.setProperty(UnmarshallerProperties.MEDIA_TYPE, "application/json");
        unmarshaller.setProperty(UnmarshallerProperties.JSON_INCLUDE_ROOT, false);
        StreamSource jsonSource = new StreamSource(json);
        User userFromJSON = unmarshaller.unmarshal(jsonSource, User.class).getValue();
        System.out.println(userFromJSON.getGroup().getUsers());
    }

}

输出

下面是运行演示代码的输出,显示反向指针已正确设置。

[forum14844691.User@147ee929]
[forum14844691.User@685c53ff]

了解更多信息

于 2013-02-13T01:36:58.247 回答