Mybatis-3: Javassist proxies cause marshaling frameworks (like Jackson) to fail

Created on 4 Feb 2016  路  10Comments  路  Source: mybatis/mybatis-3

It looks like there was a change for 3.3.0 that causes marshaling frameworks like Jackson to fail to marshall objects that MyBatis creates that contain proxies for lazy loading to throw exceptions.

Here is the stack trace I am getting from Jackson:

java.lang.RuntimeException: 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: com.inversoft.cleanspeak.domain.system.User_$$_jvst80c_2["applications"]->java.util.HashSet[0]->com.inversoft.cleanspeak.domain.system.Application_$$_jvst80c_4["moderationConfiguration"]->com.inversoft.cleanspeak.domain.system.ModerationConfiguration_$$_jvst80c_5["handler"])
    at com.inversoft.json.ToString.toString(ToString.java:58)
    at com.inversoft.cleanspeak.domain.system.User.toString(User.java:208)
    at com.inversoft.cleanspeak.domain.system.User_$$_jvst80c_2._d15toString(User_$$_jvst80c_2.java)
    at org.apache.ibatis.executor.loader.javassist.JavassistProxyFactory$EnhancedResultObjectProxyImpl.invoke(JavassistProxyFactory.java:161)
    at com.inversoft.cleanspeak.domain.system.User_$$_jvst80c_2.toString(User_$$_jvst80c_2.java)
    at java.lang.String.valueOf(String.java:2994)
    at java.lang.StringBuilder.append(StringBuilder.java:131)
    at com.inversoft.cleanspeak.api.service.user.DefaultUserServiceTest.search(DefaultUserServiceTest.java:178)
Caused by: 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: com.inversoft.cleanspeak.domain.system.User_$$_jvst80c_2["applications"]->java.util.HashSet[0]->com.inversoft.cleanspeak.domain.system.Application_$$_jvst80c_4["moderationConfiguration"]->com.inversoft.cleanspeak.domain.system.ModerationConfiguration_$$_jvst80c_5["handler"])
    at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.failForEmpty(UnknownSerializer.java:59)
    at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.serialize(UnknownSerializer.java:26)
    at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:505)
    at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:639)
    at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:152)
    at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:505)
    at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:639)
    at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:152)
    at com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serializeContents(CollectionSerializer.java:117)
    at com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serializeContents(CollectionSerializer.java:23)
    at com.fasterxml.jackson.databind.ser.std.AsArraySerializerBase.serialize(AsArraySerializerBase.java:183)
    at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:505)
    at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:639)
    at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:152)
    at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:114)
    at com.fasterxml.jackson.databind.ObjectMapper._configAndWriteValue(ObjectMapper.java:2866)
    at com.fasterxml.jackson.databind.ObjectMapper.writeValueAsString(ObjectMapper.java:2323)
    at com.inversoft.json.ToString.toString(ToString.java:56)
    ... 36 more
... Removed 29 stack frames

I've tested with 3.2.7 and this doesn't occur. I can probably tweak my Jackson configuration to prevent this stack trace, but it would be nice to figure out what MyBatis and Javassist are generating that is causing the issue and fix that.

bug on dependency library waiting for feedback

Most helpful comment

@wuwen5
Thank you for the test case!
Adding @JsonIgnoreProperties({"handler"}) to the User class worked for me as well.

I think we should solve the problem in mybatis.

From what I understand, 'handler' is an internal property added and used by Javassist and the exception is thrown because Jackson does not know how to process this property.
So, to me, it seems like a developer's responsibility to tell Jackson how to do it because MyBatis is not aware how Jackson or any other marshaling frameworks do their task.

Having said that, I am still open to the idea of adding a low impact workaround in MyBatis if someone knows one. =)

All 10 comments

I could be wrong, but this seems to be an issue between Jackson and Javassist.
http://stackoverflow.com/questions/4362104/strange-jackson-exception-being-thrown-when-serializing-hibernate-object

I've tested with 3.2.7 and this doesn't occur.

The default proxy factory in 3.2.7 is CGLIB.
Did you configure to use Javassist when you tested?

I assumed that MyBatis was in control of the method names inside the Javassist proxy. If that isn't the case, then this is indeed a Javassist/Jackson issue. Also, I didn't configure Javassist in 3.2.7, so it was likely using CGLIB.

Hi @voidmain , Could you try again using 3.4.2-SNAPSHOT ? And If possible, please provide a repro project via GitHub.

@harawata @kazuki43zoo I reproduce this issue. I think we should solve the problem in mybatis.

If add a getter method in EnhancedResultObjectProxyImpl is can be solve this problem. But maybe it's not a good idea.

Hi @wuwen5 , Thanks for providing reproduce test case !!

I've tried workaround on current version as follows:

@Test
public void toJsonshouldNotException() throws JsonProcessingException {
    SqlSession sqlSession = sqlSessionFactory.openSession();
    try {
        Mapper mapper = sqlSession.getMapper(Mapper.class);
        User user = mapper.getUser(1);
        assertTrue(user instanceof Proxy);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.addMixIn(Proxy.class, JavassistProxyView.class); // requires Jackson 2.4.4+
        String json = objectMapper.writeValueAsString(user);
        Assert.assertThat(json, Is.is("{\"id\":1,\"name\":\"User1\",\"groups\":[{\"id\":1,\"name\":\"Group1\"}]}"));
    } finally {
        sqlSession.close();
    }
}


interface JavassistProxyView {
    @JsonIgnore MethodHandler getHandler();
}

@wuwen5
Thank you for the test case!
Adding @JsonIgnoreProperties({"handler"}) to the User class worked for me as well.

I think we should solve the problem in mybatis.

From what I understand, 'handler' is an internal property added and used by Javassist and the exception is thrown because Jackson does not know how to process this property.
So, to me, it seems like a developer's responsibility to tell Jackson how to do it because MyBatis is not aware how Jackson or any other marshaling frameworks do their task.

Having said that, I am still open to the idea of adding a low impact workaround in MyBatis if someone knows one. =)

@harawata I am thinking if it is possible to use javaassist to also adding the @JsonIgnoreProperties({"handler"}) to the generated user class?

@thiamteck ,
I'm not sure. I'm not that familiar with Javassist.

Have you tried the addMixin() solution @kazuki43zoo proposed?
It seems to be a cleaner solution to me.

@harawata , yes the addMixin from @kazuki43zoo work, and I just realize my suggestion was not suitable as it required mybatis to have dependency with jackson.

btw, just an add on, the MethodHandler for the interface shall be org.apache.ibatis.javassist.util.proxy.MethodHandler

Was this page helpful?
0 / 5 - 0 ratings