1

変更された Bean を mongodb に永続化した後、読み取り変換が失敗するという非常に奇妙な状況があります。java.lang.ClassCastException: java.util.LinkedHashMap をkam.albert.lab.TestConversion $Person にキャストできません。

したがって、エラーのあるシナリオは次のとおりです。

  1. SimpleObject を作成し、そのマップに Person を設定します
  2. simpleObject を mongo に保存する
  3. simpleObject を見つけてマップから Person インスタンスを取得すると、java.lang.ClassCastException: java.util.LinkedHashMap を kam.albert.lab.TestConversion$Person にキャストできません。

したがって、最初の挿入が行われたときに書き込み変換は成功しましたが、マップ内から Person を読み取るときに、Person の読み取り変換が失敗したように思われます。書き込みコンバーターで定義されているデータ フィールドが正しいことを mongo で確認できます。また、別の実験では、マップに Person を配置しませんでしたが、すべての読み取りと書き込みの変換は正常に機能しました。このインスタンスをマップに配置した場合にのみ読み取り変換を行うと、問題が発生するようです。ソースを以下に示します。

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");
    }
}

そして、test-conversion-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>
4

2 に答える 2

1

これは設計どおりに機能します。カスタムConverter実装を登録する場合は、完全なDBObject. 私たちのマッピング インフラストラクチャはそれ以上処理しません。

つまり、カスタム コンバーターを提供する場合は、DBObjectcreated に必要な型情報を装備する必要があります。そうしないと、ポリモーフィック シナリオで型を解決して作成することができなくなります (そのため、呼び出す必要のある潜在的なカスタム コンバーターを知ることができません)。

書き込みコンバーターに型情報を入力させると、ポリモーフィック シナリオが機能することを示すサンプル テスト ケースを作成しました。DBObject

于 2013-07-16T10:10:41.550 に答える