PO Serialization
看起来非常顺利,那我们试着通过 @RestController
将得到的实体传给前端吧:
序列化:就是将对象转化成字节序列的过程。
反序列化:就是讲字节序列转化成对象的过程。
不好了,前端请求 API 时后端抛出了 java.lang.StackOverflowError
!
这个异常其实是 Jackson 抛出的。由于 Student
里有 Tuition
,Tuition
里有 Student
,这样无限套娃,Jackson 的序列化就无限地递归处理下去直到崩溃了。
要处理这个问题也好办,有两种方法:
方法一
@JsonBackReference
@JsonManagedReference
在 owning side 使用
@JsonBackReference
,在 non-owning side 使用@JsonManagedReference
注解。序列化结果:
/all/student
我们发现,方法一序列化
Student
时,成功将其内部的tuition
序列化,并且内部tuition
的student
域被忽略,消除了循环引用。/all/tuition
很遗憾,
tuition
的其他域虽然被成功序列化,其student
域无法被实例化。
总结:使用
@JsonBackReference
@JsonManagedReference
确实可以解决循环引用的问题,但是序列化只有 non-owning side 会序列化内部的引用,并且随着数据库关系变得复杂,这种解决方法渐渐变得无法维护,思考谁是 owning side 容易把人绕晕。方法二
@JsonIdentityInfo
方法二非常简单,只需要无脑地对所有实体加上
@JsonIdentityInfo
注解即可。序列化结果:
/all/student
/all/tuition
可以看到,无论是 owning side 还是 non-owning side 序列化,都能完整地展现全部信息。并且,序列化得到的 JSON 中,不仅包含了原来的域,还新包含了
@id
域。每一次序列化,Jackson 用不同的 UUID 标记每个对象,这样就可以在遇到已经序列化的对象时停止序列化,而将那个域的值设置为引用的 UUID。方法三
为什么要直接向前端返回 PO 呢?
我们应该创建合适的 VO,将 PO 在 service 层转化为 VO 再返回给前端,这样不就好了。
最后更新于