2013-07-15 24 views
1

我有一个非常奇怪的情况,在将修改的bean持久化到mongodb后,读取转换失败。它会导致java.lang.ClassCastException:java.util.LinkedHashMap中不能转换到kam.albert.lab.TestConversion $人Spring中的mongo读取转换在地图中的bean失败

所以errornous的情况是这样的:

  1. 创建SimpleObject和填充其地图一个人
  2. 商店simpleObject到蒙戈
  3. 找到simpleObject并从地图上看,这将触发java.lang.ClassCastException Person实例:java.util.LinkedHashMap中不能转换为kam.albert。 lab.TestConversion $ Person

因此,我认为,从地图内读取人员时,人员的读取转换似乎失败,尽管第一次插入发生时写入转换成功。我可以在mongo中看到数据字段在写入转换器中定义的正确。 另外在另一个实验中,我将人员放在地图中,所有读写转换都正常工作。只有当我们把这个实例放在地图上时,它才会在读取转换时出现问题。 源极提供低于:

package kam.albert.lab; 

import java.util.HashMap; 
import java.util.Map; 
import java.util.UUID; 

import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.context.ApplicationContext; 
import org.springframework.context.support.ClassPathXmlApplicationContext; 
import org.springframework.core.convert.converter.Converter; 
import org.springframework.data.mongodb.core.MongoOperations; 
import org.springframework.data.mongodb.core.query.Criteria; 
import org.springframework.data.mongodb.core.query.Query; 
import org.springframework.data.mongodb.core.query.Update; 
import org.springframework.stereotype.Component; 

import com.mongodb.BasicDBObject; 
import com.mongodb.DBObject; 

@Component 
public class TestConversion { 

    @Autowired private MongoOperations ops; 

    public static class Person { 
     private String firstName, lastName; 
     public Person(String first, String last) { 
      this.firstName = first; 
      this.lastName = last; 
     } 
    } 
    public static class PersonWriteConverter implements Converter<Person, DBObject> { 
     @Override 
     public DBObject convert(Person person) { 
      DBObject dbObject = new BasicDBObject(); 
      dbObject.put("first", person.firstName); 
      dbObject.put("last", person.lastName); 
      return dbObject; 
     } 

    } 
    public static class PersonReadConverter implements Converter<DBObject, Person> { 
     @Override 
     public Person convert(DBObject dbo) { 
      return new Person((String)dbo.get("first"), (String)dbo.get("last")); 
     } 
    } 
    public static class SimpleObject { 
     private String id = UUID.randomUUID().toString(); 
     private Map<Object, Object> map = new HashMap<>(); 
     public SimpleObject addPerson(String first, String last) { 
      this.map.put(String.valueOf(this.map.size()), new Person(first, last)); 
      return this; 
     } 
    } 

    public static void main(String[] args) { 
     ApplicationContext ctx = new ClassPathXmlApplicationContext(
      "test-conversion-context.xml" 
     ); 
     TestConversion bean = ctx.getBean(TestConversion.class); 
     bean.cleanup(); 
     String id = bean.testWrite(); 
     bean.testRead(id); 
     bean.testUpdate(id); // this causes the read below to fail 
     bean.testRead(id); 
    } 

    private void cleanup() { 
     this.ops.dropCollection("testconv"); 
    } 

    private String testWrite() { 
     SimpleObject simpleObject = new SimpleObject(); 
     simpleObject.addPerson("albert", "kam"); 
     this.ops.insert(simpleObject, "testconv"); 
     return simpleObject.id; 
    } 

    private void testRead(String id) { 
     SimpleObject simpleObject = this.ops.findById(id, SimpleObject.class, "testconv"); 
     System.out.println("read success : " + simpleObject.map); 
    } 

    private void testUpdate(String id) { 
     SimpleObject simpleObject = this.ops.findById(id, SimpleObject.class, "testconv"); 
     Person person = (Person) simpleObject.map.get("0"); // this causes exception ! 
     person.firstName = "a new first name"; 
//  simpleObject.addPerson("new", "person"); // this is fine 
     Update update = new Update().set("map", simpleObject.map); 
     this.ops.updateFirst(Query.query(Criteria.where("_id").is(id)), update, "testconv"); 
    } 
} 

并且测试转换-context.xml中

<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:aop="http://www.springframework.org/schema/aop" 
    xmlns:context="http://www.springframework.org/schema/context" 
    xmlns:lang="http://www.springframework.org/schema/lang" 
    xmlns:mongo="http://www.springframework.org/schema/data/mongo" 
    xmlns:p="http://www.springframework.org/schema/p" 
    xmlns:util="http://www.springframework.org/schema/util" 
    xmlns:task="http://www.springframework.org/schema/task" 
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd 
     http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd 
     http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd 
     http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-3.1.xsd 
     http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo-1.0.xsd 
     http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd 
     http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.1.xsd"> 

    <!-- default id = mongo, host = localhost, and port = 27017 no nested options 
     for now --> 
    <mongo:mongo> 
     <mongo:options /> 
    </mongo:mongo> 

    <!-- to translate any exceptions from @Repository annotated classes --> 
    <context:annotation-config /> 

    <mongo:db-factory dbname="glasswing" mongo-ref="mongo" /> 

    <util:constant id="writeConcern" static-field="com.mongodb.WriteConcern.SAFE" /> 
    <util:constant id="writeResultChecking" static-field="org.springframework.data.mongodb.core.WriteResultChecking.EXCEPTION" /> 

    <bean id="ops" class="org.springframework.data.mongodb.core.MongoTemplate"> 
     <constructor-arg name="mongoDbFactory" ref="mongoDbFactory" /> 
     <constructor-arg name="mongoConverter" ref="mappingConverter" /> 

     <property name="writeConcern" ref="writeConcern" /> 
     <property name="writeResultChecking" ref="writeResultChecking" /> 
    </bean> 

    <mongo:mapping-converter base-package="kam.albert.domain.converter"> 
     <mongo:custom-converters> 
      <mongo:converter><bean class="kam.albert.lab.TestConversion.PersonWriteConverter" /></mongo:converter> 
      <mongo:converter><bean class="kam.albert.lab.TestConversion.PersonReadConverter" /></mongo:converter> 
     </mongo:custom-converters> 
    </mongo:mapping-converter> 

    <context:spring-configured /> 

    <context:load-time-weaver/> 

    <bean id="testConversion" class="kam.albert.lab.TestConversion" /> 
</beans> 

回答

1

这个工作过程设计描述黑客。如果您注册了自定义的Converter实施,则必须提供完整的DBObject。我们的地图基础设施不会再对待它。

这意味着,如果您提供自定义转换器,您需要配备使用所需类型信息创建的DBObject。否则,我们将无法在多态场景中解析要创建的类型(并因此不知道需要调用的潜在自定义转换器)。

我创建了一个sample test case表明多态性情景工作如果你让书写器填充DBObject与类型信息。

+0

jira.springsource.org/browse/DATAMONGO-724 – bertie