Web 服务接收对象时缺少子对象
Child objects missing when web service receives object
我有一个 RESTful 网络服务,可以为对象执行一些持久性操作。该对象有一些包含在集合中的子对象。 Web 服务接收到对象后,子对象集合为空。
我编写的用于调用该服务的代码如下所示:
//create a Foo
Foo f = new Foo();
FooAttribute fa = new FooAttribute();
fa.setFooId(f);
Collection<FooAttribute> fAttribColl = new Arraylist<FooAttribute>();
fAttribColl.add(fa);
//Add a collection of attributes
f.setFooAttributeCollection(fAttribColl);
f.setName("fooname");
WebResource resource = new WebResource();
//send f to the service.
Foo fNew = resource.path(URI+"/createFoo")
.accept(javax.ws.rs.core.MediaType.APPLICATION_XML)
.post(Foo.class, f);
持久性网络服务看起来像这样:
@Stateless
@Path("my.application.service.path")
public class MyFooService(){
@Path("/createFoo")
@POST
@Consumes({ "application/json", "application/xml" })
@Produces({ "application/xml" })
public Foo createFoo(Foo f)
{
//Inspecting with the debugger shows that
//f.getFooAttributeCollection() returns an
//empty collection, even though it was populated in the client code.
this.getEntityManager().persist(f);
}
如果我在 createFoo
的第一行设置断点并在调试器中检查 f
,我可以看到 fooAttributeCollection
成员是空的,即使另一个 "simple" 成员(在本例中都是 String
类型)都具有正确的值。我不确定为什么集合对象是空的。
我不确定这是否重要,但是为 Foo 和 FooAttribute 生成的代码如下所示:
@Entity
@Table(name = "foo")
@XmlRootElement
public class Foo implements Serializable {
@OneToMany(cascade = CascadeType.ALL, mappedBy = "fooId")
private Collection<FooAttribute> fooAttributeCollection;
@XmlTransient
public Collection<FooAttribute> getFooAttributeCollection() {
return fooAttributeCollection;
}
public void setFooAttributeCollection(Collection<FooAttribute> fooAttributeCollection) {
this.fooAttributeCollection = fooAttributeCollection;
}
}
@Entity
@Table(name = "foo_attribute")
@XmlRootElement
public class FooAttribute implements Serializable {
@JoinColumn(name = "foo_id", referencedColumnName = "foo_id")
@ManyToOne(optional = false)
private Study fooId;
public Foo getFooId() {
return this.fooId;
}
public void setFooId(Foo f) {
this.fooId = f;
}
}
问题是@XmlTransient
。
@XmlTransient
public Collection<FooAttribute> getFooAttributeCollection() {
return fooAttributeCollection;
}
它导致 属性 无法映射。您可以通过使用 JAXB API 在发送之前打印 Foo
对象来查看
// Foo f = new Foo()
// set the collection like you did in your post
JAXBContext context = JAXBContext.newInstance(Foo.class);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
Unmarshaller unmarshaller = context.createUnmarshaller();
StringWriter writer = new StringWriter();
marshaller.marshal(f, writer);
System.out.println(writer.toString());
Result:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<foo/>
编组或解组也是如此。因此,如果您尝试发送原始 xml,xml 元素在解组时仍然不会映射。
现在我确定你有这个注释的原因是为了避免循环引用。但这就是问题所在。一种选择是只对这种类型使用 DTO。我也不使用 MOXy,但我已经看到它具有一些处理 JPA 双向关系的功能,就像你在这里所拥有的一样。
与一样,问题不在网络服务中,而在支持它的数据模型代码中。 @XmlTransient
注释是导致子对象为空的原因。然而,简单地删除 @XmlTransient
是不够的,因为我在 XML 中得到了关于父子之间无限循环的错误。解决方案是用 @XmlID
注释父对象 ID 的 getter 方法,在子对象中,我用 @XmlIDREF
注释父对象上的 getter。这样,子对象不包含完整的父对象(其中包含子对象,子对象包含无限的父对象),它只包含一个具有父对象 ID 的元素。
@Entity
@Table(name = "foo")
@XmlRootElement
public class Foo implements Serializable {
@OneToMany(cascade = CascadeType.ALL, mappedBy = "fooId")
private Collection<FooAttribute> fooAttributeCollection;
@XmlID
//If you want to use anything other than a String, you may need
//to write your own adapter
@XmlJavaTypeAdapter(value=MyCusomIntegerAdapter.class)
public Integer getFoodID(){ ... }
public Collection<FooAttribute> getFooAttributeCollection() {
return fooAttributeCollection;
}
public void setFooAttributeCollection(Collection<FooAttribute> fooAttributeCollection) {
this.fooAttributeCollection = fooAttributeCollection;
}
}
@Entity
@Table(name = "foo_attribute")
@XmlRootElement
public class FooAttribute implements Serializable {
@JoinColumn(name = "foo_id", referencedColumnName = "foo_id")
@ManyToOne(optional = false)
private Study fooId;
@XmlIDREF
public Foo getFooId() {
return this.fooId;
}
public void setFooId(Foo f) {
this.fooId = f;
}
}
Moxy 的 @XmlInvereseReference
应该也能解决此类问题,但由于某些原因它对我不起作用。
我有一个 RESTful 网络服务,可以为对象执行一些持久性操作。该对象有一些包含在集合中的子对象。 Web 服务接收到对象后,子对象集合为空。
我编写的用于调用该服务的代码如下所示:
//create a Foo
Foo f = new Foo();
FooAttribute fa = new FooAttribute();
fa.setFooId(f);
Collection<FooAttribute> fAttribColl = new Arraylist<FooAttribute>();
fAttribColl.add(fa);
//Add a collection of attributes
f.setFooAttributeCollection(fAttribColl);
f.setName("fooname");
WebResource resource = new WebResource();
//send f to the service.
Foo fNew = resource.path(URI+"/createFoo")
.accept(javax.ws.rs.core.MediaType.APPLICATION_XML)
.post(Foo.class, f);
持久性网络服务看起来像这样:
@Stateless
@Path("my.application.service.path")
public class MyFooService(){
@Path("/createFoo")
@POST
@Consumes({ "application/json", "application/xml" })
@Produces({ "application/xml" })
public Foo createFoo(Foo f)
{
//Inspecting with the debugger shows that
//f.getFooAttributeCollection() returns an
//empty collection, even though it was populated in the client code.
this.getEntityManager().persist(f);
}
如果我在 createFoo
的第一行设置断点并在调试器中检查 f
,我可以看到 fooAttributeCollection
成员是空的,即使另一个 "simple" 成员(在本例中都是 String
类型)都具有正确的值。我不确定为什么集合对象是空的。
我不确定这是否重要,但是为 Foo 和 FooAttribute 生成的代码如下所示:
@Entity
@Table(name = "foo")
@XmlRootElement
public class Foo implements Serializable {
@OneToMany(cascade = CascadeType.ALL, mappedBy = "fooId")
private Collection<FooAttribute> fooAttributeCollection;
@XmlTransient
public Collection<FooAttribute> getFooAttributeCollection() {
return fooAttributeCollection;
}
public void setFooAttributeCollection(Collection<FooAttribute> fooAttributeCollection) {
this.fooAttributeCollection = fooAttributeCollection;
}
}
@Entity
@Table(name = "foo_attribute")
@XmlRootElement
public class FooAttribute implements Serializable {
@JoinColumn(name = "foo_id", referencedColumnName = "foo_id")
@ManyToOne(optional = false)
private Study fooId;
public Foo getFooId() {
return this.fooId;
}
public void setFooId(Foo f) {
this.fooId = f;
}
}
问题是@XmlTransient
。
@XmlTransient
public Collection<FooAttribute> getFooAttributeCollection() {
return fooAttributeCollection;
}
它导致 属性 无法映射。您可以通过使用 JAXB API 在发送之前打印 Foo
对象来查看
// Foo f = new Foo()
// set the collection like you did in your post
JAXBContext context = JAXBContext.newInstance(Foo.class);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
Unmarshaller unmarshaller = context.createUnmarshaller();
StringWriter writer = new StringWriter();
marshaller.marshal(f, writer);
System.out.println(writer.toString());
Result:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<foo/>
编组或解组也是如此。因此,如果您尝试发送原始 xml,xml 元素在解组时仍然不会映射。
现在我确定你有这个注释的原因是为了避免循环引用。但这就是问题所在。一种选择是只对这种类型使用 DTO。我也不使用 MOXy,但我已经看到它具有一些处理 JPA 双向关系的功能,就像你在这里所拥有的一样。
与@XmlTransient
注释是导致子对象为空的原因。然而,简单地删除 @XmlTransient
是不够的,因为我在 XML 中得到了关于父子之间无限循环的错误。解决方案是用 @XmlID
注释父对象 ID 的 getter 方法,在子对象中,我用 @XmlIDREF
注释父对象上的 getter。这样,子对象不包含完整的父对象(其中包含子对象,子对象包含无限的父对象),它只包含一个具有父对象 ID 的元素。
@Entity
@Table(name = "foo")
@XmlRootElement
public class Foo implements Serializable {
@OneToMany(cascade = CascadeType.ALL, mappedBy = "fooId")
private Collection<FooAttribute> fooAttributeCollection;
@XmlID
//If you want to use anything other than a String, you may need
//to write your own adapter
@XmlJavaTypeAdapter(value=MyCusomIntegerAdapter.class)
public Integer getFoodID(){ ... }
public Collection<FooAttribute> getFooAttributeCollection() {
return fooAttributeCollection;
}
public void setFooAttributeCollection(Collection<FooAttribute> fooAttributeCollection) {
this.fooAttributeCollection = fooAttributeCollection;
}
}
@Entity
@Table(name = "foo_attribute")
@XmlRootElement
public class FooAttribute implements Serializable {
@JoinColumn(name = "foo_id", referencedColumnName = "foo_id")
@ManyToOne(optional = false)
private Study fooId;
@XmlIDREF
public Foo getFooId() {
return this.fooId;
}
public void setFooId(Foo f) {
this.fooId = f;
}
}
Moxy 的 @XmlInvereseReference
应该也能解决此类问题,但由于某些原因它对我不起作用。