JPAとJacksonで。無限ループ。

先日書いてたStackOverflowErrorの件。

外部キーを持つテーブルからエンティティを生成してJSONで返すと循環参照を引き起こす。
@ManyToOneだの@OneToManyで指定している参照先のオブジェクトを引っ張ってきて、さらにその参照先オブジェクトが逆方向のListだのオブジェクトを持っていて、それを引っ張り、そのオブジェクトも・・・・・が延々と続く。
実際に呼び出してみると1件しか返ってないJsonが入れ子でズラズラッと出力されててエラーログを見るとこんなのが出ています。

[2013-12-16T22:06:17.364+0900] [glassfish 4.0] [SEVERE] [] [org.glassfish.jersey.server.ServerRuntime$Responder] [tid: _ThreadID=68 _ThreadName=http-listener-1(5)] [timeMillis: 1387199177364] [levelValue: 1000] [[
An I/O error has occurred while writing a response message entity to the container output stream.
org.glassfish.jersey.server.internal.process.MappableException: org.codehaus.jackson.map.JsonMappingException: Infinite recursion (StackOverflowError) (through reference chain: org.apache.openjpa.enhance.model$.........

この循環参照を止めるには外部キーになってるオブジェクトをシリアライズさせなければよいわけで、調べるとこんなのが出てきました。

http://qiita.com/rch850/items/30825e6ff0811fdc011d

これを外部キーオブジェクトにつけてやることで止まるんですね。

ただ、これだとまるっきり出てこなくなるわけで、DB側で外部キーを付けてる意味がなくなるんじゃないかなー。とか考えたりしてもうちょっとヨサゲなのを探してみました。

http://keenformatics.blogspot.it/2013/08/how-to-solve-json-infinite-recursion.html

そのものズバリですな。

これは便利そう。

早速実装。

User.java

@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name="Inhouseuser")
@Entity
public class User implements Serializable {
	private static final long serialVersionUID = 1L;

	@Temporal(TemporalType.DATE)
	@JsonSerialize(using = DateSerializer.class)
	@XmlElement(name="InputDate",required=true)
	private Date InputDate;

	@XmlElement(name = "IsValid", required = true)
	private byte IsValid;

	@Temporal(TemporalType.DATE)
	@JsonSerialize(using = DateSerializer.class)
	@XmlElement(name="UpdateDate",required=true)
	private Date UpdateDate;

	@XmlElement(name = "UserNameFirst", required = true)
	private String UserNameFirst;

	@XmlElement(name = "UserNameLast", required = true)
	private String UserNameLast;

	@Id
	@XmlElement(name = "UserID", required = true)
	@XmlJavaTypeAdapter(CollapsedStringAdapter.class)
	@JsonSerialize(using = NumericSerializer.class)
	@XmlSchemaType(name = "ID")
	private BigInteger UserID;

	//bi-directional many-to-one association to Unit
	@ManyToOne(fetch = FetchType.LAZY)
	@JsonBackReference("Unit")
	@JoinColumn(name="Unit_UnitID", referencedColumnName="UnitID")
	private Unit unit;

	//bi-directional many-to-one association to Systemuserbind
	@OneToMany(mappedBy="user",fetch = FetchType.LAZY)
	@JsonManagedReference("user")
	private List<Systemuserbind> systemuserbinds;

1対多になる側(軸のテーブル)に

@Entity
@XmlAccessorType(XmlAccessType.FIELD)
public class Unit implements Serializable {
	private static final long serialVersionUID = 1L;

	@Temporal(TemporalType.DATE)
	@JsonSerialize(using = DateSerializer.class)
	@XmlElement(name = "InputDate", required = true)
	private Date inputDate;

	@XmlElement(name = "UnitName", required = true)
	private String UnitName;

	@Temporal(TemporalType.DATE)
	@JsonSerialize(using = DateSerializer.class)
	@XmlElement(name = "UpdateDate", required = true)
	private Date updateDate;

	@Id
	@XmlElement(name = "UnitID", required = true)
	@XmlJavaTypeAdapter(CollapsedStringAdapter.class)
	@JsonSerialize(using = NumericSerializer.class)
	@XmlSchemaType(name = "ID")
	private int UnitID;

	//bi-directional many-to-one association to user
	@OneToMany(mappedBy = "unit", fetch = FetchType.LAZY)
	@JsonManagedReference("Unit")
	private List<user> user;

@JsonBackReference で後方、つまり親を探す厨子王のような循環参照を停止し、 @JsonManagedReference で鬼子母神のように子を求める循環参照を止めるんですな。
めでたしめでたし。

広告
カテゴリー: プログラミング タグ: , パーマリンク

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中