JAXBElement:为类java.lang.Class

时间:2017-09-03 13:25:27

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

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

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

    这里需要注意的是ExtensionType包含protected List<Object> any;,允许它存储任何类的对象。就我而言,它属于名为TSD Module_Name_Here ModuleType的类,可以浏览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 包含JAXBElement,spring-data-mongodb似乎无法自行处理并抛出CodecConfigurationException org.bson.codecs.configuration.CodecConfigurationException: Can't find a codec for class java.lang.Class.

    2. 这是错误的陈述:

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

      Output in Debugger

      我尝试使用转换器为spring-data-mongodb解释here,然而,似乎我遗漏了一些东西,因为异常是关于&#34; Codecs&#34;而不是&#34;转换器&#34;。

      感谢任何帮助。

      编辑:

      为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是一个接口,所以我想我需要一个坚持这个接口的可实例化类。有什么建议吗?

1 个答案:

答案 0 :(得分: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)
    ));
}