【问题标题】:JAXBElement: providing codec (/converter?) for class java.lang.ClassJAXBElement:为类 java.lang.Class 提供编解码器(/converter?)
【发布时间】:2018-02-11 21:55:24
【问题描述】:

我一直在评估将 spring-data-mongodb 用于project。总之,我的目标是:

  1. 使用现有的 XML 模式文件生成 Java 类。

这里要注意的是ExtensionType 包含protected List<Object> any; 允许它存储任何类的对象。就我而言,它位于名为 TSDModule_Name_HereModuleType 的类中,可以浏览 here

  1. 使用 spring-data-mongodb 作为持久化存储

    • 这是使用简单的ProductDataRepository 实现的

      @RepositoryRestResource(collectionResourceRel = "product", path = "product")
      public interface ProductDataRepository extends MongoRepository<TSDProductDataType, String> {
          TSDProductDataType queryByGtin(@Param("gtin") String gtin);
      }
      
    • 然而,未编组的 TSDProductDataType 包含 spring-data-mongodb 似乎无法自行处理的 JAXBElement 并抛出 CodecConfigurationException org.bson.codecs.configuration.CodecConfigurationException: Can't find a codec for class java.lang.Class.

      李>

这是错误的陈述:

TSDProductDataType tsdProductDataType = jaxbElement.getValue();
repository.save(tsdProductDataType);

按照here 的解释,我尝试使用用于 spring-data-mongodb 的转换器,但是,我似乎遗漏了一些东西,因为例外是关于“编解码器”而不是“转换器”。

感谢任何帮助。

编辑:

为 JAXBElement 添加转换器

注意:适用于 org.springframework.boot::spring-boot-starter-parent 的 1.5.6.RELEASE 版本。有了 2.0.0.M3 版本,地狱就崩溃了

我在之前尝试添加转换器时似乎遗漏了一些东西。所以,我像下面这样添加它进行测试:

@Component
@ReadingConverter
public class JAXBElementReadConverter implements Converter<DBObject, JAXBElement> {
    //@Autowired
    //MongoConverter converter;

    @Override
    public JAXBElement convert(DBObject dbObject) {
        Class declaredType, scope;
        QName name = qNameFromString((String)dbObject.get("name"));
        Object rawValue = dbObject.get("value");
        try {
            declaredType = Class.forName((String)dbObject.get("declaredType"));
        } catch (ClassNotFoundException e) {
            if (rawValue.getClass().isArray()) declaredType = List.class;
            else declaredType = LinkedHashMap.class;
        }
        try {
            scope = Class.forName((String) dbObject.get("scope"));
        } catch (ClassNotFoundException e) {
            scope = JAXBElement.GlobalScope.class;
        }
        //Object value = rawValue instanceof DBObject ? converter.read(declaredType, (DBObject) rawValue) : rawValue;
        Object value = "TODO";
        return new JAXBElement(name, declaredType, scope, value);
    }

    QName qNameFromString(String s) {
        String[] parts = s.split("[{}]");
        if (parts.length > 2) return new QName(parts[1], parts[2], parts[0]);
        if (parts.length == 1) return new QName(parts[0]);
        return new QName("undef");
    }
}


@Component
@WritingConverter
public class JAXBElementWriteConverter implements Converter<JAXBElement, DBObject> {
    //@Autowired
    //MongoConverter converter;

    @Override
    public DBObject convert(JAXBElement jaxbElement) {
        DBObject dbObject = new BasicDBObject();
        dbObject.put("name", qNameToString(jaxbElement.getName()));
        dbObject.put("declaredType", jaxbElement.getDeclaredType().getName());
        dbObject.put("scope", jaxbElement.getScope().getCanonicalName());
        //dbObject.put("value", converter.convertToMongoType(jaxbElement.getValue()));
        dbObject.put("value", "TODO");
        dbObject.put("_class", JAXBElement.class.getName());
        return dbObject;
    }

    public String qNameToString(QName name) {
        if (name.getNamespaceURI() == XMLConstants.NULL_NS_URI) return name.getLocalPart();
        return name.getPrefix() + '{' + name.getNamespaceURI() + '}' + name.getLocalPart();
    }
}


@SpringBootApplication
public class TsdApplication {

    public static void main(String[] args) {
        SpringApplication.run(TsdApplication.class, args);
    }

    @Bean
    public CustomConversions customConversions() {
        return new CustomConversions(Arrays.asList(
                new JAXBElementReadConverter(),
                new JAXBElementWriteConverter()
        ));
    }
}

到目前为止一切顺利。但是,如何实例化MongoConverter converter;MongoConverter 是一个接口,所以我想我需要一个可实例化的类来遵守这个接口。有什么建议吗?

【问题讨论】:

    标签: spring spring-boot spring-data spring-data-mongodb


    【解决方案1】:

    我理解能够将现有域对象映射到没有样板的数据库层的便利需求,但即使您没有遇到 JAXB 类结构问题,我仍然建议不要使用它逐字。除非这是一个简单的一次性项目,否则您几乎肯定会遇到需要更改域模型但您的持久数据需要保持现有状态的点。如果您只是直接持久化数据,则没有机制可以在较新的域模式和较旧的持久化数据方案之间进行转换。持久化数据方案的版本控制也是明智的。

    您发布的用于编写客户转换器的链接是实现此目的的一种方式,并且非常适合 Spring 生态系统。该方法还应该解决您遇到的问题(关于底层凌乱的 JAXB 数据结构没有干净地转换)。

    您无法使该方法起作用吗?确保您使用 @Component 加上自动类扫描或通过一些 Configuration 类手动将它们加载到 Spring 上下文中。

    编辑以解决您的编辑问题:

    将以下内容添加到您的每个转换器中:

    private final MongoConverter converter;
    
    public JAXBElement____Converter(MongoConverter converter) {
        this.converter = converter;
    }
    

    尝试将您的 bean 定义更改为:

    @Bean
    public CustomConversions customConversions(@Lazy MongoConverter converter) {
        return new CustomConversions(Arrays.asList(
                new JAXBElementReadConverter(converter),
                new JAXBElementWriteConverter(converter)
        ));
    }
    

    【讨论】:

    • 谢谢。目前这是一个简单的一次性项目。所以,我想保持最低限度(此外,标准化机构已经提供了 XSD 文件,因此版本方法可能效果最好)。我尝试按照我引用的 URL 再次编写转换器。看来我的问题是 spring-boot-starter 模板的 v1.5.6 和 v2.0.0 之间不兼容(URL 中提供的代码适用于 v1.5.6)。我在我的回答中更新了这个。然而,我现在被困在MongoConverter converter; 周围。关于如何实例化它的任何建议?
    • 我很确定它将由 Spring Data Mongo 实例化,并且只需要通过 bean 定义进行连接。或者,您可以将每个转换器定义为 bean 并将其自动装配,但我编辑的答案中的上述方式可能更清晰。
    • 尝试对 bean 定义的参数或方法使用延迟加载。 @Lazy @Bean@Lazy MongoConverter converter.
    • @Lazy MongoConverter converter 成功了!...春天似乎很神奇!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-15
    • 1970-01-01
    • 1970-01-01
    • 2013-01-03
    • 1970-01-01
    相关资源
    最近更新 更多