我有两个由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之前使用相同类型的控制器创建,但我没有问题,因为没有外键。
我该如何解决这个问题?
答案 0 :(得分:2)
为什么不创建专用于JSON通信的对象而不是使用实体?
你不能这样做:
{
"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" }
...
}
您不必设置AName的所有子属性只有@ID就足够了,在您的控制器中,您只需对A对象执行find()并将其设置为B然后saveOrUpdate您的B对象:
b.setAName(yoursession.find(b.getAName().getAName()));
答案 1 :(得分:0)
从Jackson 1.9开始,您可以使用@JsonUnwrapped注释定义您的嵌套类字段,以向Jackson指示在序列化期间应该解包所包含的对象,即包含的对象属性应该包含在父级的属性中。
public class B implements Serializable {
...
@ManyToOne
@JoinColumn(name="AName")
@JsonUnwrapped
private AName;