具有ManyToOne关系的JPA实体JSON序列化

时间:2014-03-21 07:51:59

标签: json entity-framework hibernate spring-mvc jpa

我有两个由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之前使用相同类型的控制器创建,但我没有问题,因为没有外键。

我该如何解决这个问题?

2 个答案:

答案 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;