MyBatisのLazy Load + Jacksonでシリアライズエラーになる件の回避方法
MyBatisのLazy Loadが適用されているオブジェクト(MyBatis内蔵のJavassistのProxyオブジェクト)を、Jacksonを使ってJSONにシリアライズしようとすると以下のようなエラーが発生することが報告されています。(https://github.com/mybatis/mybatis-3/issues/570)
com.fasterxml.jackson.databind.JsonMappingException: No serializer found for class org.apache.ibatis.executor.loader.javassist.JavassistProxyFactory$EnhancedResultObjectProxyImpl and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: org.apache.ibatis.submitted.javassist.User_$$_jvst15a_0["handler"])
at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:284)
at com.fasterxml.jackson.databind.SerializerProvider.mappingException(SerializerProvider.java:1110)
...
Note:
MyBatisは、JavassistをMyBatisのJarの中に内蔵するスタイルを採用しており、パッケージもMyBatis配下のパッケージ(
org.apache.ibatis.javassist
)にリパッケージしています。なので・・・本投稿の中で出てくるJavassistのインタフェースのパッケージはorg.apache.ibatis.javassist
であるという点に注意してください!!
これは、Proxyオブジェクトの中で保持しているMethodHandler
型のhandler
プロパティ(Proxyのメソッドを呼び出した時のハンドリングを行うためのオブジェクト=Lazy Load処理を制御するオブジェクト)を、JSONにシリアライズしようとして発生しています。
原因はスタックトレースにも出力されている通り、MethodHandler
の実装クラス(org.apache.ibatis.executor.loader.javassist.JavassistProxyFactory$EnhancedResultObjectProxyImpl
)をシリアライズするためのクラスが見つからなかったためです。
解決方法はいくつかありそうですが・・・本投稿では、
-
@JsonIgnore
を付与したインタフェースを作り -
@JsonIgnore
を付与したインタフェースをJavassistのProxy
インタフェースにMix-inさせる
ことで、シリアライズが不要なhandler
プロパティをシリアライズ対象から除外する方法を紹介したいと思います。
動作検証バージョン
- MyBatis 3.4.2
- Javassist 3.21.0-GA (MyBatis内蔵のJavassist)
- Jackson 2.8.5 (2.4.4+が必須)
@JsonIgnore
を付与したインタフェースを作る
@JsonIgnore
を付与したインタフェースを作る
まず、@JsonIgnore
を付与したインタフェースを作ります。
public interface MyBatisJavassistProxyMixIn {
@JsonIgnore MethodHandler getHandler(); // @JsonIgnoreを付与してhandlerプロパティをシリアライズ対象外に指定する
}
Note:
メソッドの返り値の型は、実はあまり意味がなく・・・
Object
やvoid
でも動作します。ここでは実体にあわせてMethodHandler
にしています。
Proxy
インタフェースに@JsonIgnore
を付与したインタフェースをMix-inする
次に、@JsonIgnore
を付与したインタフェースをMyBatis内蔵のJavassistのProxy
インタフェースにMix-inします。
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.addMixIn(Proxy.class, MyBatisJavassistProxyMixIn.class); // Mix-in !!
こうすると・・・・
MyBatis内蔵のJavassistのProxy
インタフェースを実装したクラスのhandler
プロパティを、シリアライズ対象から除外することができます。
まとめ
特にないのですが・・・
Jacksonに限らず・・・MyBatisのLazy Loadが適用されているオブジェクトをJSONにシリアライズする場合は、同様の問題が起こる可能性がありそうな気がするので、特定のプロパティをシリアライズ対象から除外する方法を事前に調べておくとハマらなくてすみそうです。
あと・・・MyBatisはCGLIBを使ったProxyもサポートしてるけど、今回は未検証です(あしからず・・・)。
参考サイト
Author And Source
この問題について(MyBatisのLazy Load + Jacksonでシリアライズエラーになる件の回避方法), 我々は、より多くの情報をここで見つけました https://qiita.com/kazuki43zoo/items/a1dfe394e46c7fb82af4著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .