【问题标题】:Spring mvc integration test with hibernate and jackson-hibernate-mapper: illegal attempt to associate a collection with two sessionsSpring mvc 与 hibernate 和 jackson-hibernate-mapper 的集成测试:非法尝试将集合与两个会话相关联
【发布时间】:2015-09-25 09:54:06
【问题描述】:

“非法尝试将集合与两个会话相关联”的异常是众所周知的,当在两个不同的会话中引用同一实体时会发生这种情况。我有一个 spring mvc 集成测试,它发生在哪里,需要一个建议如何使它正常工作。

另外值得注意的是,我使用的是https://github.com/FasterXML/jackson-datatype-hibernate,这有助于在将域对象转换为 json 时避免 LazyInitializationException。我的猜测是它在内部打开 Hibernate 会话以获取惰性关系。

这是一个测试:

@RunWith(SpringJunit4ClassRunner.class)
@WebAppConfiguration
@ContextHierarchy({
    @ContextConfiguration(name = "root", locations = "classpath:application.xml"),
    @ContextConfiguration(name = "servlet", locations = "classpath:servlet.xml")
})
@TransactionalConfiguration
public class IntegrationTest {
    private MockMvc mockMvc;

    //some initialization and autowiring

    @Test
    @Transactional // It is important to rollback all the db changes after the test. So, it's a common pattern to make all tests transactional.
    // So here we have HibernateSession#1
    public void testController() {
        // repository is spring-data-jpa repository
        // but you may think of it as a DAO which returns persisted 
        // hibernate entity
        ChildEntity child = new ChildEntity();
        child = childRepository.save();

        MyEntity entity = new MyEntity();
        entity.setChildEntities(Collections.singletoneList(child));
        entity = repository.save(entity);
        // entity and its child are still preserved in the HibernateSession#1

        mockMvc.perform(get("/entity/by_child_id/" + child.getId()).andExpect(status().is("200"));
    }

}

@RestController
@RequestMapping("/entity")
public class MyController {

    @RequestMapping("by_child_id/{id}")
    // My guess: Jackson hibernate mapper will open another session
    // and try to fetch children  
    // So here we have HibernateSession#2
    public MyEntity getByChildId(@PathVariable("id") childId) {
         return repository.findByChildrenId(childId);
    }

}

@Entity 
public class MyEntity {
    @OneToMany(fetch = FetchType.EAGER)
    private List<ChildEntity> children;
}

//and finally here is a piece of servlet.xml
<mvc:annotation-driven>
    <mvc:message-converters>
        <!-- Use the HibernateAware mapper instead of the default -->
        <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
            <property name="objectMapper">
                <bean class="path.to.your.HibernateAwareObjectMapper" />
            </property>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven> 

我的例外是:

Caused by org.hibernate.HibernateException: Illegal attempt to associate a collection with two open sessions: [MyEntity.children#1]
    at com.fasterxml.jackson.datatype.hibernate4.PersistentCollectionSerializer.inistializeCollection(PersistentCollectionSerializer.java:195)
    at com.fasterxml.jackson.datatype.hibernate4.PersistentCollectionSerializer.findLazyValue(PersistentCollectionSerializer.java:160)
    at com.fasterxml.jackson.datatype.hibernate4.PersistentCollectionSerializer.serialize(PersistentCollectionSerializer.java:115)
    at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:505)
    at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase:639)

jackson-databind-2.4.2.jar

那么,除了避免使用 jackson-hibernate-mapper 进行测试外,有什么方法可以避免这个问题?使用 DTO 和 Dozer 代替域对象不被视为解决方案(将来会这样做,但不是现在)。

提前致谢

【问题讨论】:

  • 只是不要在您的测试方法中进行数据设置,请在此之前进行,以便会话关闭而不是保持打开状态。或者不要让测试成为事务性的。
  • 在一些 before/after 方法中需要手动设置数据和清理数据。如果您使用不同的设置数据进行数十次测试,那么它将很快成为一场噩梦。只是使测试非事务性将导致其他测试之间共享状态。
  • 正确...但问题是您的测试是事务性的,导致 2 个打开的会话,因此您收到错误...您必须以某种方式修复设置,以便您的测试不是交易。
  • 你是对的。好的,覆盖 jsonConverter 似乎是最简单的方法。谢谢。

标签: hibernate spring-mvc jackson spring-transactions hibernate-session


【解决方案1】:

感谢 M. Deinum,我决定采用最简单的解决方案并创建了一个 test-servlet-context.xml:

<beans>

   <import resource="classpath:servlet.xml">   

   <bean id="jsonConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>

</beans>

我还更改了servlet.xml中转换器的定义:

<mvc:annotation-driven>
    <mvc:message-converters>
        <!-- Use the HibernateAware mapper instead of the default -->
        <ref bean="jsonConverter"/>
    </mvc:message-converters>
</mvc:annotation-driven> 

<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
    <property name="objectMapper">
        <bean class="path.to.your.HibernateAwareObjectMapper" />
    </property>
</bean>

现在我的测试没有使用任何杰克逊休眠魔法。它没有用,因为所有测试本身都是事务性的。

缺陷报告在这里:https://github.com/FasterXML/jackson-datatype-hibernate/issues/74

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-18
    • 2013-01-08
    • 2011-03-06
    • 2015-03-23
    • 1970-01-01
    相关资源
    最近更新 更多