简单说一下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
-
<!-- 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 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);
-
}
-
}
订单商品实体类
-
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
标记,即可使该属性在序列化和解序列化的过程中不被访问。
-
@Entity
-
@Table(name="sales_order")
-
public class SalesOrder {
-
...
-
/**订单中商品列表清单**/
-
@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
-
}
这里可能会出现不适用的场景,比如说,当我希望从SalesOrderItem的方向获取SalesOrder的数据,将会出现无法被序列化的情况。
这里需要特别强调一下 不要使用transient标记属性 会报错
@JsonManagedReference and @JsonBackReference.
将@JsonManagedReference
标记在父类对子类的引用变量上,并将@JsonBackReference
标记在子类对父类的引用变量上。
-
@Entity
-
@Table(name="sales_order")
-
public class SalesOrder {
-
...
-
/**订单中商品列表清单**/
-
@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
-
}
通过这种方式确保在双向关系中只有单个反向的实例被序列化
@JsonIdentityInfo
该annotation用于标注在entity上。当entity被标注后,jackson在每一次序列化的时候都会为该实例生成专门的ID(也可以是实例自带的属性),通过这种方式辨别实例。这种方式适用于存在一个实体关联链的场景
。比如Order -> OrderLine -> User -> Order
-
@Entity
-
@Table(name="sales_order")
-
@JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="@id")
-
public class SalesOrder {
-
...
-
/**订单中商品列表清单**/
-
@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
-
}
@JsonIgnoreProperties 个人心中的全场最佳
@JsonIgnoreProperties不同于@JsonIgnore在于,你可以注明该变量中的哪个属相不被序列化。从而允许在双向访问上都不存在环或是缺失。
-
@Entity
-
@Table(name="sales_order")
-
public class SalesOrder {
-
...
-
/**订单中商品列表清单**/
-
@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
-
}
半场小结
其实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