hibernate中因双向依赖而造成的json怪相--springmvc项目
简单说一下Jackson
如果想要详细了解一下Jackson,可以去其github上的项目主页查看其版本情况以及各项功能。除此以外,需要格外提一下Jackson的版本问题。Jackson目前主流版本有两种,1.x和2.x,而这二者的核心包是不同的。在2.0以后的Jackson版本中,groupId从原来的org.codehaus.jackson.core转而变成了com.fasterxml.jackson.core。所以如果希望用到新性能的Jackson,请将原来的maven依赖改为以下的依赖。
下文中的jackson-2-version是指特定的版本,当前的稳定版本为2.8.9。想要了解最新的版本情况还请去项目主页或是maven官网上查看。但是如果是要和spring mvc配合使用的话,还要注意一下他们之间的兼容问题。目前我采用的是4.2.6版本的springmvc和2.6.6版本的jackson
com.fasterxml.jackson.core
   jackson-core
   ${jackson-2-version}
com.fasterxml.jackson.core
   jackson-annotations
   ${jackson-2-version}
com.fasterxml.jackson.core
  jackson-databind
  ${jackson-2-version}
com.fasterxml.jackson.dataformat
  jackson-dataformat-smile
  ${jackson-2-version}
com.fasterxml.jackson.jaxrs
   jackson-jaxrs-json-provider
   ${jackson-2-version}
com.fasterxml.jackson.module
  jackson-module-jaxb-annotations
  ${jackson-2-version}
" title="" data-original-title="复制">
<!-- the core, which includes Streaming API, shared low-level abstractions (but NOT data-binding) -->
 <dependency>
   <groupId>com.fasterxml.jackson.core</groupId>
   <artifactId>jackson-core</artifactId>
   <version>${jackson-2-version}</version>
 </dependency>
<!-- Just the annotations; use this dependency if you want to
attach annotations to classes without connecting them to the code. -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson-2-version}</version>
</dependency>
<!-- databinding; ObjectMapper, JsonNode and related classes are here -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson-2-version}</version>
</dependency>
<!-- smile (binary JSON). Other artifacts in this group do other formats. -->
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-smile</artifactId>
<version>${jackson-2-version}</version>
</dependency>
<!-- JAX-RS provider -->
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>${jackson-2-version}</version>
</dependency>
<!-- Support for JAX-B annotations as additional configuration -->
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-jaxb-annotations</artifactId>
<version>${jackson-2-version}</version>
</dependency>
如果是在springmvc中配置maven依赖,则需要的依赖包为core,annotation和databind
从一个bug说起
刚开始上手springmvc的时候并没有详细去了解更多的JSON操作,只是简单的了解了一下如何将对象转化为JSON数据传送回前端。但是在这时出现了一个问题,就是当两个entity之间存在双向依赖时,传回的JSON数据会出现无限的读取情况。也就是说,因为两个实体中都存在着指向对方的指针,导致序列化的时候会出现二者之间不断相互访问的情况。hibernate这种实体设计方式一直让我有些困惑,毕竟在一般代码的设计模式中是应当尽量避免出现双向依赖的。
这里举一个具体的例子说明这个情况。
假设我有一个订单,订单中有多个商品。也就是订单和商品之间是一对多的关系。订单和商品的实体类如下:
订单实体类
import javax.persistence.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
Created by rale on 7/15/17.
销售单
*/
@Entity
@Table(name="sales_order")
public class SalesOrder {@Id
@Column(name = "sales_order_id")
private Long salesOrderId;/订单创建人员/
@Column(name = "salesman_id", nullable = false)
private Long userId;@Temporal(TemporalType.TIMESTAMP)
@Column(name = "sales_order_created_at")
private Date createAt;/订单中商品列表清单/
@OneToMany(mappedBy = "salesOrder", cascade = CascadeType.ALL, orphanRemoval = true)
@OrderBy(value = "order_item_id")
@Fetch(FetchMode.JOIN)
private List salesOrderItems;public Long getSalesOrderId() {
return salesOrderId;
}public void setSalesSource(SalesSource salesSource) {
this.salesSource = salesSource;
}public Long getUserId() {
return userId;
}public void setUserId(Long userId) {
this.userId = userId;
}public Date getCreateAt() {
return createAt;
}public void setCreateAt(Date createAt) {
this.createAt = createAt;
}public List getSalesOrderItems() {
return salesOrderItems==null ? new ArrayList() : salesOrderItems;
}public void setSalesOrderItems(List salesOrderItems) {
this.salesOrderItems = salesOrderItems;
}public void addSalesOrderItem(SalesOrderItem salesOrderItem){
if (this.salesOrderItems == null) this.salesOrderItems = new ArrayList();
salesOrderItem.setSalesOrder(this);
this.salesOrderItems.add(salesOrderItem);
}
}
" title="" data-original-title="复制">
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
- Created by rale on 7/15/17.
 
- 销售单
*/
@Entity
@Table(name="sales_order")
public class SalesOrder {
@Id
@Column(name = "sales_order_id")
private Long salesOrderId;
/订单创建人员/
@Column(name = "salesman_id", nullable = false)
private Long userId;
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "sales_order_created_at")
private Date createAt;
/订单中商品列表清单/
@OneToMany(mappedBy = "salesOrder", cascade = CascadeType.ALL, orphanRemoval = true)
@OrderBy(value = "order_item_id")
@Fetch(FetchMode.JOIN)
private List<SalesOrderItem> salesOrderItems;
public Long getSalesOrderId() {
return salesOrderId;
}
public void setSalesSource(SalesSource salesSource) {
this.salesSource = salesSource;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public Date getCreateAt() {
return createAt;
}
public void setCreateAt(Date createAt) {
this.createAt = createAt;
}
public List<SalesOrderItem> getSalesOrderItems() {
return salesOrderItems==null ? new ArrayList<SalesOrderItem>() : salesOrderItems;
}
public void setSalesOrderItems(List<SalesOrderItem> salesOrderItems) {
this.salesOrderItems = salesOrderItems;
}
public void addSalesOrderItem(SalesOrderItem salesOrderItem){
if (this.salesOrderItems == null) this.salesOrderItems = new ArrayList<SalesOrderItem>();
salesOrderItem.setSalesOrder(this);
this.salesOrderItems.add(salesOrderItem);
} 
}
订单商品实体类
/**
Created by rale on 7/15/17.
销售清单中的单品和数量
*/
@Entity
@Table(name = "sales_order_item")
public class SalesOrderItem {
@Id
@GeneratedValue
@Column(name = "order_item_id")
private Long salesOrderItemId;@Column(name = "order_item_quantity")
private int quantity;@Column(name = "order_item_price")
private double salesPrice;//对应的销售单实体类
@ManyToOne
@JoinColumn(name = "order_id")
private SalesOrder salesOrder;public SalesOrderItem() {
}public SalesOrderItem(Long salesOrderItemId){
this.salesOrderItemId = salesOrderItemId;
}public Long getSalesOrderItemId() {
return salesOrderItemId;
}public void setSalesOrderItemId(Long salesOrderItemId) {
this.salesOrderItemId = salesOrderItemId;
}public int getQuantity() {
return quantity;
}public void setQuantity(int quantity) {
this.quantity = quantity;
}public double getSalesPrice() {
return salesPrice;
}public void setSalesPrice(double salesPrice) {
this.salesPrice = salesPrice;
}public SalesOrder getSalesOrder() {
return salesOrder;
}public void setSalesOrder(SalesOrder salesOrder) {
this.salesOrder = salesOrder;
}
}
" title="" data-original-title="复制">
import javax.persistence.*;
/**
- Created by rale on 7/15/17.
 
- 销售清单中的单品和数量
*/
@Entity
@Table(name = "sales_order_item")
public class SalesOrderItem {
@Id
@GeneratedValue
@Column(name = "order_item_id")
private Long salesOrderItemId;
@Column(name = "order_item_quantity")
private int quantity;
@Column(name = "order_item_price")
private double salesPrice;
//对应的销售单实体类
@ManyToOne
@JoinColumn(name = "order_id")
private SalesOrder salesOrder;
public SalesOrderItem() {
}
public SalesOrderItem(Long salesOrderItemId){
this.salesOrderItemId = salesOrderItemId;
}
public Long getSalesOrderItemId() {
return salesOrderItemId;
}
public void setSalesOrderItemId(Long salesOrderItemId) {
this.salesOrderItemId = salesOrderItemId;
}
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
public double getSalesPrice() {
return salesPrice;
}
public void setSalesPrice(double salesPrice) {
this.salesPrice = salesPrice;
}
public SalesOrder getSalesOrder() {
return salesOrder;
}
public void setSalesOrder(SalesOrder salesOrder) {
this.salesOrder = salesOrder;
}
}
 
解决双向依赖的方法如下:
@JsonIgnore
在不希望被序列化的field或property上使用@JsonIgnore标记,即可使该属性在序列化和解序列化的过程中不被访问。
...
/**订单中商品列表清单**/
@OneToMany(mappedBy = "salesOrder", cascade = CascadeType.ALL, orphanRemoval = true)
@OrderBy(value = "order_item_id")
@Fetch(FetchMode.JOIN)
private List<SalesOrderItem> salesOrderItems;
...
//getters and setters
}
@Entity
@Table(name = "sales_order_item")
public class SalesOrderItem {
...
//对应的销售单实体类
@ManyToOne
@JoinColumn(name = "order_id")
@JsonIgnore
private SalesOrder salesOrder;
...
//getters and setters
}" title="" data-original-title="复制">
@Entity
@Table(name="sales_order")
public class SalesOrder {
...
<span class="hljs-comment">/**订单中商品列表清单**/</span>
<span class="hljs-meta">@OneToMany(mappedBy = <span class="hljs-meta-string">"salesOrder"</span>, cascade = CascadeType.ALL, orphanRemoval = true)</span>
<span class="hljs-meta">@OrderBy(value = <span class="hljs-meta-string">"order_item_id"</span>)</span>
<span class="hljs-meta">@Fetch(FetchMode.JOIN)</span>
<span class="hljs-keyword">private</span> List<SalesOrderItem> salesOrderItems;
...
<span class="hljs-comment">//getters and setters</span>
}
@Entity
@Table(name = "sales_order_item")
public class SalesOrderItem {
...
<span class="hljs-comment">//对应的销售单实体类</span>
<span class="hljs-meta">@ManyToOne</span>
<span class="hljs-meta">@JoinColumn(name = <span class="hljs-meta-string">"order_id"</span>)</span>
<span class="hljs-meta">@JsonIgnore</span>
<span class="hljs-keyword">private</span> SalesOrder salesOrder;
...
<span class="hljs-comment">//getters and setters</span>
}
这里可能会出现不适用的场景,比如说,当我希望从SalesOrderItem的方向获取SalesOrder的数据,将会出现无法被序列化的情况。
这里需要特别强调一下 不要使用transient标记属性 会报错
@JsonManagedReference and @JsonBackReference.
将@JsonManagedReference标记在父类对子类的引用变量上,并将@JsonBackReference标记在子类对父类的引用变量上。
...
/**订单中商品列表清单**/
@OneToMany(mappedBy = "salesOrder", cascade = CascadeType.ALL, orphanRemoval = true)
@OrderBy(value = "order_item_id")
@Fetch(FetchMode.JOIN)
@JsonManagedReference
private List<SalesOrderItem> salesOrderItems;
...
//getters and setters
}
@Entity
@Table(name = "sales_order_item")
public class SalesOrderItem {
...
//对应的销售单实体类
@ManyToOne
@JoinColumn(name = "order_id")
@JsonBackReference
private SalesOrder salesOrder;
...
//getters and setters
}" title="" data-original-title="复制">
@Entity
@Table(name="sales_order")
public class SalesOrder {
...
<span class="hljs-comment">/**订单中商品列表清单**/</span>
<span class="hljs-meta">@OneToMany(mappedBy = <span class="hljs-meta-string">"salesOrder"</span>, cascade = CascadeType.ALL, orphanRemoval = true)</span>
<span class="hljs-meta">@OrderBy(value = <span class="hljs-meta-string">"order_item_id"</span>)</span>
<span class="hljs-meta">@Fetch(FetchMode.JOIN)</span>
<span class="hljs-meta">@JsonManagedReference</span>
<span class="hljs-keyword">private</span> List<SalesOrderItem> salesOrderItems;
...
<span class="hljs-comment">//getters and setters</span>
}
@Entity
@Table(name = "sales_order_item")
public class SalesOrderItem {
...
<span class="hljs-comment">//对应的销售单实体类</span>
<span class="hljs-meta">@ManyToOne</span>
<span class="hljs-meta">@JoinColumn(name = <span class="hljs-meta-string">"order_id"</span>)</span>
<span class="hljs-meta">@JsonBackReference</span>
<span class="hljs-keyword">private</span> SalesOrder salesOrder;
...
<span class="hljs-comment">//getters and setters</span>
}
通过这种方式确保在双向关系中只有单个反向的实例被序列化
@JsonIdentityInfo
该annotation用于标注在entity上。当entity被标注后,jackson在每一次序列化的时候都会为该实例生成专门的ID(也可以是实例自带的属性),通过这种方式辨别实例。这种方式适用于存在一个实体关联链的场景。比如Order -> OrderLine -> User -> Order
...
/**订单中商品列表清单**/
@OneToMany(mappedBy = "salesOrder", cascade = CascadeType.ALL, orphanRemoval = true)
@OrderBy(value = "order_item_id")
@Fetch(FetchMode.JOIN)
private List<SalesOrderItem> salesOrderItems;
...
//getters and setters
}
@Entity
@Table(name = "sales_order_item")
@JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="@id")
public class SalesOrderItem {
...
//对应的销售单实体类
@ManyToOne
@JoinColumn(name = "order_id")
private SalesOrder salesOrder;
...
//getters and setters
}" title="" data-original-title="复制">
@Entity
@Table(name="sales_order")
@JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="@id")
public class SalesOrder {
...
<span class="hljs-comment">/**订单中商品列表清单**/</span>
<span class="hljs-meta">@OneToMany(mappedBy = <span class="hljs-meta-string">"salesOrder"</span>, cascade = CascadeType.ALL, orphanRemoval = true)</span>
<span class="hljs-meta">@OrderBy(value = <span class="hljs-meta-string">"order_item_id"</span>)</span>
<span class="hljs-meta">@Fetch(FetchMode.JOIN)</span>
<span class="hljs-keyword">private</span> List<SalesOrderItem> salesOrderItems;
...
<span class="hljs-comment">//getters and setters</span>
}
@Entity
@Table(name = "sales_order_item")
@JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="@id")
public class SalesOrderItem {
...
<span class="hljs-comment">//对应的销售单实体类</span>
<span class="hljs-meta">@ManyToOne</span>
<span class="hljs-meta">@JoinColumn(name = <span class="hljs-meta-string">"order_id"</span>)</span>
<span class="hljs-keyword">private</span> SalesOrder salesOrder;
...
<span class="hljs-comment">//getters and setters</span>
}
@JsonIgnoreProperties 个人心中的全场最佳
@JsonIgnoreProperties不同于@JsonIgnore在于,你可以注明该变量中的哪个属相不被序列化。从而允许在双向访问上都不存在环或是缺失。
...
/**订单中商品列表清单**/
@OneToMany(mappedBy = "salesOrder", cascade = CascadeType.ALL, orphanRemoval = true)
@OrderBy(value = "order_item_id")
@Fetch(FetchMode.JOIN)
@JsonIgnoreProperties("salesOrder")
private List<SalesOrderItem> salesOrderItems;
...
//getters and setters
}
@Entity
@Table(name = "sales_order_item")
public class SalesOrderItem {
...
//对应的销售单实体类
@ManyToOne
@JoinColumn(name = "order_id")
@JsonIgnoreProperties("salesOrderItems")
private SalesOrder salesOrder;
...
//getters and setters
}" title="" data-original-title="复制">
@Entity
@Table(name="sales_order")
public class SalesOrder {
...
<span class="hljs-comment">/**订单中商品列表清单**/</span>
<span class="hljs-meta">@OneToMany(mappedBy = <span class="hljs-meta-string">"salesOrder"</span>, cascade = CascadeType.ALL, orphanRemoval = true)</span>
<span class="hljs-meta">@OrderBy(value = <span class="hljs-meta-string">"order_item_id"</span>)</span>
<span class="hljs-meta">@Fetch(FetchMode.JOIN)</span>
<span class="hljs-meta">@JsonIgnoreProperties(<span class="hljs-meta-string">"salesOrder"</span>)</span>
<span class="hljs-keyword">private</span> List<SalesOrderItem> salesOrderItems;
...
<span class="hljs-comment">//getters and setters</span>
}
@Entity
@Table(name = "sales_order_item")
public class SalesOrderItem {
...
<span class="hljs-comment">//对应的销售单实体类</span>
<span class="hljs-meta">@ManyToOne</span>
<span class="hljs-meta">@JoinColumn(name = <span class="hljs-meta-string">"order_id"</span>)</span>
<span class="hljs-meta">@JsonIgnoreProperties(<span class="hljs-meta-string">"salesOrderItems"</span>)</span>
<span class="hljs-keyword">private</span> SalesOrder salesOrder;
...
<span class="hljs-comment">//getters and setters</span>
}
半场小结
其实jackson中还有很多很实用的功能,例如如何将Date类序列化成界面展示的格式等,将在下一次更新中说明。有兴趣的可以收藏加关注哦。
其它教程传送门
springmvc + ajax 实现
http://www.mkyong.com/spring-...
jackson annotation教程
http://tutorials.jenkov.com/j...
stack overflow上相关问题回答
https://stackoverflow.com/que...
https://stackoverflow.com/que...
https://stackoverflow.com/que...
这里需要指出的是,虽然某些回答说,要将annotation标注在私有变量的get方法上,但是po主发现标注在私有变量上还是可以实现功能的。
data hiding using jsonignore and spring data jpa
https://dzone.com/articles/da...
想要了解更多开发技术,面试教程以及互联网公司内推,欢迎关注我的微信公众号!将会不定期的发放福利哦~
原文地址:https://segmentfault.com/a/1190000010246362
hibernate中因双向依赖而造成的json怪相--springmvc项目的更多相关文章
- Hibernate中双向的一对多关系
		
何为双向,双向的意思就是你我之间可以互相通信(customer(1)和order(n)) 也就是说customer可以访问order,order也可以访问customer 二者构成了双向的关系 在Hi ...
 - Hibernate中双向多对多的两种配置方式
		
Hibernate中双向多对多的两种配置方式 1.建立多对多双向关联关系 package cn.happy.entitys; import java.util.HashSet; import java ...
 - MySql和Hibernate中关于cascade的用法
		
数据库里的cascade的用法,Mysql和Hibernate里面是不相同. 在数据库里,进行增加.修改.删除记录的时候,经常会涉及到父子关系的表. 例如:有省份表和城市表,其中城市表有一个外键pro ...
 - hibernate 中文文档
		
转载:http://blog.csdn.net/kevon_sun/article/details/42850387 Hibernate Annotations 参考文档 3.2.0 CR1 目录 前 ...
 - hibernate多对一双向关联
		
关联是类(类的实例)之间的关系,表示有意义和值得关注的连接. 本系列将介绍Hibernate中主要的几种关联映射 Hibernate一对一主键单向关联Hibernate一对一主键双向关联Hiberna ...
 - hibernate(七) hibernate中查询方式详解
		
序言 之前对hibernate中的查询总是搞混淆,不明白里面具体有哪些东西.就是因为缺少总结.在看这篇文章之前,你应该知道的是数据库的一些查询操作,多表查询等,如果不明白,可以先去看一下 MySQL数 ...
 - Hibernate框架之双向多对多关系映射
		
昨天跟大家分享了Hibernate中单向的一对多.单向多对一.双向一对多的映射关系,今天跟大家分享下在Hibernate中双向的多对多的映射关系 这次我们以项目和员工举个栗子,因为大家可以想象得到,在 ...
 - Hibernate中一级缓存和二级缓存使用详解
		
一.一级缓存二级缓存的概念解释 (1)一级缓存就是Session级别的缓存,一个Session做了一个查询操作,它会把这个操作的结果放在一级缓存中,如果短时间内这个 session(一定要同一个ses ...
 - hibernate中@Entity和@Table的区别
		
Java Persistence API定义了一种定义,可以将常规的普通Java对象(有时被称作POJO)映射到数据库.这些普通Java对象被称作Entity Bean.除了是用Java Persis ...
 
随机推荐
- The 16th UESTC Programming Contest Final 游记
			
心情不好来写博客. 为了满足ykk想要气球的愿望,NicoDafaGood.Achen和我成功去神大耍了一圈. 因为队名一开始是LargeDumpling应援会,然后队名被和谐,变成了学校的名字,顿时 ...
 - word Stock Market Indices
			
Stock Market Indices USA Africa Asia and Pacific Canada Europe Middle East South America Internation ...
 - Spring_Bean的生命周期
			
init-method="init" destroy-method="destory" 指定初始化和销毁方法 创建Bean后置处理器 <!-- 实现Bea ...
 - kubernetes1.5新特性跟踪
			
Kubernetes发布历史回顾 Kubernetes 1.0 - 2015年7月发布 Kubernetes 1.1 - 2015年11月发布 Kubernetes 1.2 - 2016年3月发布 K ...
 - mkdir、touch、rm和rmdir命令
			
一.mkdir命令 mkdir命令用来创建目录.该命令创建由dirname命名的目录.如果在目录名的前面没有加任何路径名,则在当前目录下创建由dirname指定的目录:如果给出了一个已经存在的路径,将 ...
 - 【itsdangerous】的加密解密原理(易懂版)
			
from itsdangerous import TimedJSONWebSignatureSerializer import time from itsdangerous import Signat ...
 - IntelliJ IDEA 添加项目后编译显示包不存在的解决方案
			
File -> Project Structure -> Modules 看看是否有多个项目,删掉无用的.或者调整一下项目,重新 Mark as 一下,指定成 Sources
 - oracle dbms_repcat_admin能带来什么安全隐患
			
如果一个用户能执行dbms_repcat_admin包,将获得极大的系统权限. 以下情况可能获得该包的执行权限: 1.在sys下grant execute on dbms_repcat_admin t ...
 - 【C++】判断一个图是否有环 无向图 有向图(转载)
			
没有找到原文出处,请参考一下链接: http://www.cnblogs.com/hiside/archive/2010/12/01/1893878.html http://topic.csdn.ne ...
 - 尖峰7月线上技术分享--Hadoop、MySQL
			
7月2号晚20:30-22:30 东大博士Dasight分享主题<大数据与Hadoop漫谈> 7月5号晚20:30-22:30 原支付宝MySQL首席DBA分享主题<MySQL ...