简单说一下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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 <dependency > <groupId > com.fasterxml.jackson.core</groupId > <artifactId > jackson-core</artifactId > <version > ${jackson-2-version}</version > </dependency > <dependency > <groupId > com.fasterxml.jackson.core</groupId > <artifactId > jackson-annotations</artifactId > <version > ${jackson-2-version}</version > </dependency > <dependency > <groupId > com.fasterxml.jackson.core</groupId > <artifactId > jackson-databind</artifactId > <version > ${jackson-2-version}</version > </dependency > <dependency > <groupId > com.fasterxml.jackson.dataformat</groupId > <artifactId > jackson-dataformat-smile</artifactId > <version > ${jackson-2-version}</version > </dependency > <dependency > <groupId > com.fasterxml.jackson.jaxrs</groupId > <artifactId > jackson-jaxrs-json-provider</artifactId > <version > ${jackson-2-version}</version > </dependency > <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这种实体设计方式一直让我有些困惑,毕竟在一般代码的设计模式中是应当尽量避免出现双向依赖的。
这里举一个具体的例子说明这个情况。 假设我有一个订单,订单中有多个商品。也就是订单和商品之间是一对多的关系 。订单和商品的实体类如下:
订单实体类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 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;@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); } }
订单商品实体类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 import javax.persistence.*;@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标记,即可使该属性在序列化和解序列化的过程中不被访问。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 @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; ... } @Entity @Table(name = "sales_order_item") public class SalesOrderItem { ... @ManyToOne @JoinColumn(name = "order_id") @JsonIgnore private SalesOrder salesOrder; ... }
这里可能会出现不适用的场景,比如说,当我希望从SalesOrderItem的方向获取SalesOrder的数据,将会出现无法被序列化的情况。
这里需要特别强调一下 不要使用transient标记属性 会报错
@JsonManagedReference and @JsonBackReference. 将@JsonManagedReference标记在父类对子类的引用变量上,并将@JsonBackReference标记在子类对父类的引用变量上。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 @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; ... } @Entity @Table(name = "sales_order_item") public class SalesOrderItem { ... @ManyToOne @JoinColumn(name = "order_id") @JsonBackReference private SalesOrder salesOrder; ... }
通过这种方式确保在双向关系中只有单个反向的实例被序列化
@JsonIdentityInfo 该annotation用于标注在entity上。当entity被标注后,jackson在每一次序列化的时候都会为该实例生成专门的ID(也可以是实例自带的属性),通过这种方式辨别实例。这种方式适用于存在一个实体关联链的场景。比如Order -> OrderLine -> User -> Order
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 @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; ... } @Entity @Table(name = "sales_order_item") @JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="@id") public class SalesOrderItem { ... @ManyToOne @JoinColumn(name = "order_id") private SalesOrder salesOrder; ... }
@JsonIgnoreProperties 个人心中的全场最佳 @JsonIgnoreProperties不同于@JsonIgnore在于,你可以注明该变量中的哪个属相不被序列化。从而允许在双向访问上都不存在环或是缺失。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 @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; ... } @Entity @Table(name = "sales_order_item") public class SalesOrderItem { ... @ManyToOne @JoinColumn(name = "order_id") @JsonIgnoreProperties("salesOrderItems") private SalesOrder salesOrder; ... }
半场小结 其实jackson中还有很多很实用的功能,例如如何将Date类序列化成界面展示的格式等,将在下一次更新中说明。有兴趣的可以收藏加关注哦。
其它教程传送门 springmvc + ajax 实现 http://www.mkyong.com/spring-mvc/spring-4-mvc-ajax-hello-world-example/
jackson annotation教程 http://tutorials.jenkov.com/java-json/jackson-annotations.html
stack overflow上相关问题回答 https://stackoverflow.com/questions/12505141/only-using-jsonignore-during-serialization-but-not-deserialization https://stackoverflow.com/questions/3325387/infinite-recursion-with-jackson-json-and-hibernate-jpa-issue https://stackoverflow.com/questions/30589257/json-jpa-manytoone-and-onetomany-recursive
这里需要指出的是,虽然某些回答说,要将annotation标注在私有变量的get方法上,但是po主发现标注在私有变量上还是可以实现功能的。
data hiding using jsonignore and spring data jpa https://dzone.com/articles/data-hiding-using-jsonignore-and-spring-data-jpa