我有两个由OneToMany- ManyToOne关系映射的JPA实体:
第一实体:
@Entity(name = A.ENTITY_NAME)
@Table(name = A.TABLE_NAME)
@JsonIgnoreProperties(ignoreUnknown = true)
public class A implements Serializable{
private static final long serialVersionUID = -24345893264589748L;
public static final String TABLE_NAME = "A";
public static final String ENTITY_NAME = "A";
@Id
@NotNull
private String AName;
private String ADescription;
@OneToMany(fetch = FetchType.EAGER,mappedBy = "AName", cascade=CascadeType.ALL, orphanRemoval = true)
@JsonIgnore(true)
private List<B> b;
第二个实体:
@Entity(name = B.ENTITY_NAME)
@Table(name = B.TABLE_NAME)
public class B implements Serializable {
private static final long serialVersionUID = -24345893264589748L;
public static final String TABLE_NAME = "B";
public static final String ENTITY_NAME = "B";
@Id
@NotNull
private String BName;
@ManyToOne
@JoinColumn(name="AName")
private AName;
//Properties
我可以在我的Oracle数据库中创建表。
SQL> describe B;
Name Null? Type
----------------------------------------- -------- ----------------------------
BNAME NOT NULL VARCHAR2(255 CHAR)
...
ANAME VARCHAR2(255 CHAR)
现在我想在我的Spring MVC控制器中使用这些实体,像这样:
@RequestMapping(value = B_CREATION_REQUEST_MAPPING, method = RequestMethod.POST)
@ResponseBody
public ResponseEntity<JSONResponse> createB(HttpServletRequest request,
@RequestBody B b) {
BSvc.createB(b);
ResponseEntity<JSONResponse> response = new ResponseEntity<JSONResponse>(new JSONResponse(), HttpStatus.OK);
REST_LOGGER.info("[" + request.getRemoteAddr() + "] B object "+b.getBName()+ " has been created" );
return response;
}
对于A对象我有相同的控制器。
但是当我请求这个B控制器时,我必须在JSON中放入一个a对象,尽管请求无效:
{
"BName": "foo",
"AName": {"AName": "bar",
"ADescription": "too"
}
...
}
创建错误:
org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing
我只需要输入JSON:
{
"BName": "foo",
"AName": "bar",
...
}
不在JSON中创建对象A。对象A之前是用相同类型的控制器创建的,但我对它没有问题,因为没有外键。
我该如何解决这个问题?
为什么不创建一个专门用于JSON通信的对象,而不是使用Entity ?
你不能这样做:
{
"BName": "foo",
"AName": "bar",
...
}
因为它会尝试将String反序列化为a对象
创建DTO类class BDTO {
private String AName;
private String Bname
}
将其绑定到控制器BDTO而不是B
@RequestBody BDTO bDTO
那么你可以做
B b = new B();
b.setBName(bDTO.getBName());
b.setAName(yoursession.find(bDTO.getAName()));
yoursession.saveOrUpdate(b);
另一个解决方案:如果你想保持实体作为传输对象是只设置ID在您的请求:
{
"BName": "foo",
"AName": {"AName": "bar" }
...
}
你不需要设置name的所有子属性只有@ID就足够了,在你的控制器中你可以简单地对a对象执行find()并将其设置为B然后saveOrUpdate你的B对象:
b.setAName(yoursession.find(b.getAName().getAName()));
从Jackson 1.9开始,你可以用@JsonUnwrapped
注释来定义你的嵌套类字段,以指示Jackson一个包含的对象应该在序列化期间取消包装,即包含的对象属性应该包含在父对象的属性中。
public class B implements Serializable {
…
@ManyToOne
@JoinColumn(name="AName")
@JsonUnwrapped
private AName;