简单说一下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 = &quot;salesOrder&quot;, cascade = CascadeType.ALL, orphanRemoval = true)
@OrderBy(value = &quot;order_item_id&quot;)
@Fetch(FetchMode.JOIN)
private List<SalesOrderItem> salesOrderItems;

...
//getters and setters

}

@Entity

@Table(name = "sales_order_item")

public class SalesOrderItem {

...

//对应的销售单实体类
@ManyToOne
@JoinColumn(name = &quot;order_id&quot;)
@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&lt;SalesOrderItem&gt; 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 = &quot;salesOrder&quot;, cascade = CascadeType.ALL, orphanRemoval = true)
@OrderBy(value = &quot;order_item_id&quot;)
@Fetch(FetchMode.JOIN)
@JsonManagedReference
private List<SalesOrderItem> salesOrderItems;

...
//getters and setters

}

@Entity

@Table(name = "sales_order_item")

public class SalesOrderItem {

...

//对应的销售单实体类
@ManyToOne
@JoinColumn(name = &quot;order_id&quot;)
@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&lt;SalesOrderItem&gt; 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 = &quot;salesOrder&quot;, cascade = CascadeType.ALL, orphanRemoval = true)
@OrderBy(value = &quot;order_item_id&quot;)
@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 = &quot;order_id&quot;)
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&lt;SalesOrderItem&gt; 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 = &quot;salesOrder&quot;, cascade = CascadeType.ALL, orphanRemoval = true)
@OrderBy(value = &quot;order_item_id&quot;)
@Fetch(FetchMode.JOIN)
@JsonIgnoreProperties(&quot;salesOrder&quot;)
private List<SalesOrderItem> salesOrderItems;

...
//getters and setters

}

@Entity

@Table(name = "sales_order_item")

public class SalesOrderItem {

...

//对应的销售单实体类
@ManyToOne
@JoinColumn(name = &quot;order_id&quot;)
@JsonIgnoreProperties(&quot;salesOrderItems&quot;)
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&lt;SalesOrderItem&gt; 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项目的更多相关文章

  1. Hibernate中双向的一对多关系

    何为双向,双向的意思就是你我之间可以互相通信(customer(1)和order(n)) 也就是说customer可以访问order,order也可以访问customer 二者构成了双向的关系 在Hi ...

  2. Hibernate中双向多对多的两种配置方式

    Hibernate中双向多对多的两种配置方式 1.建立多对多双向关联关系 package cn.happy.entitys; import java.util.HashSet; import java ...

  3. MySql和Hibernate中关于cascade的用法

    数据库里的cascade的用法,Mysql和Hibernate里面是不相同. 在数据库里,进行增加.修改.删除记录的时候,经常会涉及到父子关系的表. 例如:有省份表和城市表,其中城市表有一个外键pro ...

  4. hibernate 中文文档

    转载:http://blog.csdn.net/kevon_sun/article/details/42850387 Hibernate Annotations 参考文档 3.2.0 CR1 目录 前 ...

  5. hibernate多对一双向关联

    关联是类(类的实例)之间的关系,表示有意义和值得关注的连接. 本系列将介绍Hibernate中主要的几种关联映射 Hibernate一对一主键单向关联Hibernate一对一主键双向关联Hiberna ...

  6. hibernate(七) hibernate中查询方式详解

    序言 之前对hibernate中的查询总是搞混淆,不明白里面具体有哪些东西.就是因为缺少总结.在看这篇文章之前,你应该知道的是数据库的一些查询操作,多表查询等,如果不明白,可以先去看一下 MySQL数 ...

  7. Hibernate框架之双向多对多关系映射

    昨天跟大家分享了Hibernate中单向的一对多.单向多对一.双向一对多的映射关系,今天跟大家分享下在Hibernate中双向的多对多的映射关系 这次我们以项目和员工举个栗子,因为大家可以想象得到,在 ...

  8. Hibernate中一级缓存和二级缓存使用详解

    一.一级缓存二级缓存的概念解释 (1)一级缓存就是Session级别的缓存,一个Session做了一个查询操作,它会把这个操作的结果放在一级缓存中,如果短时间内这个 session(一定要同一个ses ...

  9. hibernate中@Entity和@Table的区别

    Java Persistence API定义了一种定义,可以将常规的普通Java对象(有时被称作POJO)映射到数据库.这些普通Java对象被称作Entity Bean.除了是用Java Persis ...

随机推荐

  1. The 16th UESTC Programming Contest Final 游记

    心情不好来写博客. 为了满足ykk想要气球的愿望,NicoDafaGood.Achen和我成功去神大耍了一圈. 因为队名一开始是LargeDumpling应援会,然后队名被和谐,变成了学校的名字,顿时 ...

  2. word Stock Market Indices

    Stock Market Indices USA Africa Asia and Pacific Canada Europe Middle East South America Internation ...

  3. Spring_Bean的生命周期

    init-method="init" destroy-method="destory" 指定初始化和销毁方法 创建Bean后置处理器 <!-- 实现Bea ...

  4. kubernetes1.5新特性跟踪

    Kubernetes发布历史回顾 Kubernetes 1.0 - 2015年7月发布 Kubernetes 1.1 - 2015年11月发布 Kubernetes 1.2 - 2016年3月发布 K ...

  5. mkdir、touch、rm和rmdir命令

    一.mkdir命令 mkdir命令用来创建目录.该命令创建由dirname命名的目录.如果在目录名的前面没有加任何路径名,则在当前目录下创建由dirname指定的目录:如果给出了一个已经存在的路径,将 ...

  6. 【itsdangerous】的加密解密原理(易懂版)

    from itsdangerous import TimedJSONWebSignatureSerializer import time from itsdangerous import Signat ...

  7. IntelliJ IDEA 添加项目后编译显示包不存在的解决方案

    File -> Project Structure -> Modules 看看是否有多个项目,删掉无用的.或者调整一下项目,重新 Mark as 一下,指定成 Sources

  8. oracle dbms_repcat_admin能带来什么安全隐患

    如果一个用户能执行dbms_repcat_admin包,将获得极大的系统权限. 以下情况可能获得该包的执行权限: 1.在sys下grant execute on dbms_repcat_admin t ...

  9. 【C++】判断一个图是否有环 无向图 有向图(转载)

    没有找到原文出处,请参考一下链接: http://www.cnblogs.com/hiside/archive/2010/12/01/1893878.html http://topic.csdn.ne ...

  10. 尖峰7月线上技术分享--Hadoop、MySQL

      7月2号晚20:30-22:30 东大博士Dasight分享主题<大数据与Hadoop漫谈> 7月5号晚20:30-22:30  原支付宝MySQL首席DBA分享主题<MySQL ...