【问题标题】:Spring Data Cassandra LocalDateTime ConversionSpring Data Cassandra LocalDateTime 转换
【发布时间】:2015-04-20 18:53:33
【问题描述】:

我正在做一个项目,我们有一个实体,我们想要使用类型为 LocalDateTime 的字段进行持久化,我们知道 cassandra 没有对这种类型转换的本机支持,我们使用 Spring 的支持创建了自己的自定义转换器对于转换器,但是 Spring-Data-Cassandra 似乎无法识别它们或理解该字段应映射到列。

这就是我们向 Spring 的转换服务注册转换器的方式。

<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
        <property name="converters">
            <set>
                <!-- list of converters-->
                <bean class="com.klappo.userservice.api.persistence.converters.DateToLocalDateTimeConverter" />
                <bean class="com.klappo.userservice.api.persistence.converters.LocalDateTimeToDateConverter" />
                <bean class="com.klappo.userservice.api.persistence.converters.DateToLocalDateConverter" />
                <bean class="com.klappo.userservice.api.persistence.converters.LocalDateToDateConverter" />
            </set>
        </property>
    </bean>

最终结果是我们的应用程序启动时抛出以下异常:

    Caused by: 
org.springframework.data.cassandra.mapping.VerifierMappingExceptions: java.time.LocalDate:|Cassandra entities must have the @Table, @Persistent or @PrimaryKeyClass Annotation|
    at org.springframework.data.cassandra.mapping.BasicCassandraPersistentEntityMetadataVerifier.verify(BasicCassandraPersistentEntityMetadataVerifier.java:45)
    at org.springframework.data.cassandra.mapping.BasicCassandraPersistentEntity.verify(BasicCassandraPersistentEntity.java:198)
    at org.springframework.data.mapping.context.AbstractMappingContext.addPersistentEntity(AbstractMappingContext.java:297)
    at org.springframework.data.mapping.context.AbstractMappingContext$PersistentPropertyCreator.createAndRegisterProperty(AbstractMappingContext.java:469)
    at org.springframework.data.mapping.context.AbstractMappingContext$PersistentPropertyCreator.doWith(AbstractMappingContext.java:426)
    at org.springframework.util.ReflectionUtils.doWithFields(ReflectionUtils.java:607)
    at org.springframework.data.mapping.context.AbstractMappingContext.addPersistentEntity(AbstractMappingContext.java:294)
    at org.springframework.data.mapping.context.AbstractMappingContext$PersistentPropertyCreator.createAndRegisterProperty(AbstractMappingContext.java:469)
    at org.springframework.data.mapping.context.AbstractMappingContext$PersistentPropertyCreator.doWith(AbstractMappingContext.java:426)
    at org.springframework.util.ReflectionUtils.doWithFields(ReflectionUtils.java:607)
    at org.springframework.data.mapping.context.AbstractMappingContext.addPersistentEntity(AbstractMappingContext.java:294)
    at org.springframework.data.mapping.context.AbstractMappingContext.addPersistentEntity(AbstractMappingContext.java:256)
    at org.springframework.data.mapping.context.AbstractMappingContext.initialize(AbstractMappingContext.java:372)
    at org.springframework.data.cassandra.mapping.BasicCassandraMappingContext.initialize(BasicCassandraMappingContext.java:79)
    at org.springframework.data.mapping.context.AbstractMappingContext.afterPropertiesSet(AbstractMappingContext.java:362)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1612)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1549)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:539)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:302)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:298)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:328)
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:108)
    at org.springframework.beans.factory.support.ConstructorResolver.resolveConstructorArguments(ConstructorResolver.java:632)
    at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:140)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1114)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1017)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:504)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:302)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:298)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:328)
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:108)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1456)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1197)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:537)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:302)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:298)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:328)
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:108)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1456)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1197)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:537)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:302)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:298)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:328)
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:108)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1456)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1197)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:537)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:302)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:298)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1017)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:960)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:858)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:480)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:289)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1185)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:537)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:302)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:298)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:703)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:760)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:482)
    at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:403)
    at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:306)
    at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:106)
    at org.eclipse.jetty.server.handler.ContextHandler.callContextInitialized(ContextHandler.java:800)
    at org.eclipse.jetty.servlet.ServletContextHandler.callContextInitialized(ServletContextHandler.java:444)
    at org.eclipse.jetty.server.handler.ContextHandler.startContext(ContextHandler.java:791)
    at org.eclipse.jetty.servlet.ServletContextHandler.startContext(ServletContextHandler.java:294)
    at org.eclipse.jetty.webapp.WebAppContext.startWebapp(WebAppContext.java:1349)
    at org.eclipse.jetty.maven.plugin.JettyWebAppContext.startWebapp(JettyWebAppContext.java:296)
    at org.eclipse.jetty.webapp.WebAppContext.startContext(WebAppContext.java:1342)
    at org.eclipse.jetty.server.handler.ContextHandler.doStart(ContextHandler.java:741)
    at org.eclipse.jetty.webapp.WebAppContext.doStart(WebAppContext.java:505)
    at org.eclipse.jetty.maven.plugin.JettyWebAppContext.doStart(JettyWebAppContext.java:365)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:132)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:114)
    at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:61)
    at org.eclipse.jetty.server.handler.ContextHandlerCollection.doStart(ContextHandlerCollection.java:163)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:132)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:114)
    at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:61)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:132)
    at org.eclipse.jetty.server.Server.start(Server.java:387)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:114)
    at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:61)
    at org.eclipse.jetty.server.Server.doStart(Server.java:354)
    at org.eclipse.jetty.maven.plugin.JettyServer.doStart(JettyServer.java:73)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
    at org.eclipse.jetty.maven.plugin.AbstractJettyMojo.startJetty(AbstractJettyMojo.java:534)
    at org.eclipse.jetty.maven.plugin.AbstractJettyMojo.execute(AbstractJettyMojo.java:357)
    at org.eclipse.jetty.maven.plugin.JettyRunMojo.execute(JettyRunMojo.java:167)
    at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:132)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:208)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:153)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:145)
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:116)
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:80)
    at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build(SingleThreadedBuilder.java:51)
    at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:120)
    at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:347)
    at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:154)
    at org.apache.maven.cli.MavenCli.execute(MavenCli.java:582)
    at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:214)
    at org.apache.maven.cli.MavenCli.main(MavenCli.java:158)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:289)
    at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:229)
    at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:415)
    at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:356)
    at org.codehaus.classworlds.Launcher.main(Launcher.java:46)

Spring 在这方面的数据文档缺乏很多,所以我们想知道是否有人尝试将自定义类型转换器与 spring-data-cassandra 一起使用,如果是,我们想知道正确的做法是什么它。

提前致谢。

【问题讨论】:

    标签: java datetime converter java-8 spring-data-cassandra


    【解决方案1】:

    由于我有同样的问题,我想分享一些我的进一步调查结果:

    • spring data cassandra的验证器检查实体类的有效性,失败,因为字段类型LocalDateTime不是有效的简单类型
    • 由于 cassandra 映射的糟糕实现,用自己的实现覆盖/扩展功能非常麻烦。特别是 CassandraSimpleTypeHolder 有一些 final 和 static 方法,可以直接在其他类中使用。
    • 我尝试提供我自己的CassandraMappingContext、CassandraPersistentProperty 等的实现。这允许“验证”实体,但无法写入。这是由于使用的 datastax 驱动程序在其内部映射 TypeCode 中不知道 LocalDateTime。所以你必须提供 LocalDateTime 和 Date 之间的映射。这通常由 ConversionService 完成。但目前我没有找到任何使用它的地方。

    不过,另一种选择是等待官方解决方案:

    【讨论】:

      【解决方案2】:

      不幸的是 spring-data-cassandra 版本。 http://www.datastax.com/documentation/developer/java-driver/1.0/java-driver/reference/javaClass2Cql3Datatypes_r.html

      我遇到了同样的问题,因为想将我的类映射到 Cassandra 中的 TEXT 字段,但做不到。经过几天的尝试,我得出的结论是不可能在 Spring-Data for Cassandra 中使用自定义类型转换器(使用版本 1.2.0.M1 测试)。

      CassandraTemplate 使用了 MappingCassandraConverter,它嵌入了 DefaultConversionService,但是这个 DefaultConversionService 并没有在 spring-data-cassandra 模块的任何地方使用。它只是为了与 SpringData API 集成而引入的。

      您可以在库的代码中找到conversionService 仅在使用的库

      BeanWrapper<S> wrapper = BeanWrapper.create(instance, conversionService);
      

      只是跳过转换服务:

      @Deprecated
      public static <T> BeanWrapper<T> create(T bean, ConversionService conversionService) {
          return new BeanWrapper<T>(bean);
      }
      

      您可以在 MappingCassandraConverter.readPropertyFromRow(...) 中看到它使用 ColumnReader.get(int i) 仅支持 Cassandra 的 MAP、LIST、SET 和主要数据类型(文本、数字、UUID、java.util.Date、布尔值,字节缓冲区)。

      此外,BasicCassandraMappingContext.verifier (BasicCassandraPersistentEntityMetadataVerifier) 在尝试写入任何无法映射到支持的 Cassandra 类型或不是 @Table 的字段时抛出异常。

      这就是为什么你有例外

      // it was attempt to check conversion of an embedded field 'date' in LocalDateTime    
      org.springframework.data.cassandra.mapping.VerifierMappingExceptions: java.time.LocalDate
      

      所以解决它的唯一方法是在 spring-data-cassandra 之上使用 ConversionService 来满足您的需求。

      转换服务部分的示例代码:

      import org.springframework.data.convert.Jsr310Converters;
      
      ...    
      
      @Bean
      public ConversionService getConversionService() {
          // creates DefaultConversionService
          ConversionServiceFactoryBean bean = new ConversionServiceFactoryBean();
          bean.setConverters(new HashSet<>(getConverters()));
          bean.afterPropertiesSet();
          ConversionService service = bean.getObject();
          return service;
      }
      
      private Set<Converter<?, ?>> getConverters() {
          Set<Converter<?,?>> converters = new HashSet<Converter<?,?>>();
          converters.addAll(Jsr310Converters.getConvertersToRegister());
          converters.add(new TimeWriteConverter());
          converters.add(new TimeReadConverter());
      
          return converters;
      }
      
      public static class TimeWriteConverter implements Converter<LocalDateTime, Long> {
          @Override
          public Long convert(LocalDateTime source) {
              return source.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
          }
      }
      public static class TimeReadConverter implements Converter<Long, LocalDateTime> {
          @Override
          public LocalDateTime convert(Long source) {
              return LocalDateTime.ofInstant(Instant.ofEpochMilli(source), ZoneId.systemDefault());
          }
      }
      

      【讨论】:

      • 您是否找到任何解决方法,或者您在发现此问题后是如何实现映射的?
      • 是的,我添加了额外的服务层,它以编程方式使用 ConversionService。这对我来说很好,因为在我的项目中需要某种时间序列 API(findLatest、findRange)。看起来像docs.spring.io/spring/docs/current/spring-framework-reference/…
      【解决方案3】:

      我可以看到与我的配置有些不同。

      当你可以使用标准配置容器list时,首先为什么要使用set。

      第二个:如果不行,试试java code config看看是否可行:

      @Configuration
      public class ConversionConfiguration {
      
          @Bean
          public ConversionService getConversionService() {
              ConversionServiceFactoryBean bean = new ConversionServiceFactoryBean();
              bean.setConverters(getConverters());
              bean.afterPropertiesSet();
              ConversionService object = bean.getObject();
              return object;
          }
      
          private Set<Converter> getConverters() {
              Set<Converter> converters = new HashSet<Converter>();
      
              converters.add(new DateToLocalDateTimeConverter());
              converters.add(new LocalDateTimeToDateConverter());
              converters.add(new DateToLocalDateConverter());
              converters.add(new LocalDateToDateConverter());
      
              return converters;
          }
      }
      

      【讨论】:

      • 您能解释一下为什么在这种情况下 java config 的行为与 xml 不同吗?
      • @JörnHorstmann XML 的唯一问题是您必须等到运行时才能发现 bean 中的拼写错误或其他一些愚蠢的 boo-boo。另一方面,使用 Spring IDE 插件(或集成的 Spring Tools Suite)绝对可以帮助您解决错误并确保配置的完整性。
      • 在我看来,XML 和基于注释的配置之间没有实际区别,因为任何现代 IDE 都支持检查配置(Intelij IDEA - Spring* 插件,Eclipse - Spring Tool Suite 插件) .例如,Spring 配置中的任何 XML 通常都被 XSD 模式很好地覆盖,因此在开发的早期阶段就发现了拼写错误。
      • @DmitryBuykin 你体贴的 cmets 大大扩大了我的无知范围。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-11-10
      • 2020-12-02
      • 2018-08-23
      • 2020-10-31
      • 2020-02-19
      • 1970-01-01
      • 2018-02-12
      相关资源
      最近更新 更多