简体   繁体   English

MongoDB 的不可变自动生成存储库抛出“找不到接口的编解码器” CodecConfigurationException

[英]Immutables Autogenerated repository for MongoDB throws "Can't find a codec for interface" CodecConfigurationException

Problem Description.问题描述。

This is a simplified version of the example that is currently in the immutables site .这是当前在immutables 站点中的示例的简化版本。

So I have an item that I want to use with MongoDB.所以我有一个项目要与 MongoDB 一起使用。

@Value.Immutable
@Mongo.Repository("items")
public abstract class Item {
  @Mongo.Id
  public abstract long id();
  public abstract String name();
}

For simplicity I create a Junit test.为简单起见,我创建了一个 Junit 测试。 Just like the example.就像例子一样。 The problem is that this junit test, while it is presented in the example at the immutables site throws an exception:问题是这个 junit 测试,虽然它出现在immutables 站点的示例中,但抛出异常:

public class ItemTest{
@Test
public void testRepository() {
try {
 //Simple ItemRepository creation
   ItemRepository items = new ItemRepository(
     RepositorySetup.forUri("mongodb://localhost/test")
     );

// Create item Item item = ImmutableItem.builder()
    .id(1)
    .name("one")
    .build();

    //Insert is async. Returns a Future. 
    FluentFuture<Integer> future=items.insert(item);
    future.get(); // get the result (ensure it was saved)
    
   }catch(Exception e){
       e.printStackTrace();
       Assert.fail(e.getMessage());
   }
}//end test

}//end class

Running this test will result in the following Exception:运行此测试将导致以下异常:

org.bson.codecs.configuration.CodecConfigurationException: Can't find a codec for interface spyros.stackoverflow.example.Item org.bson.codecs.configuration.CodecConfigurationException:找不到接口 spyros.stackoverflow.example.Item 的编解码器

A complete stack trace is provided below.下面提供了完整的堆栈跟踪。

java.lang.AssertionError: java.util.concurrent.ExecutionException: **org.bson.codecs.configuration.CodecConfigurationException: Can't find a codec for interface spyros.stackoverflow.example.Item.**
    at com.google.common.util.concurrent.AbstractFuture.getDoneValue(AbstractFuture.java:552)
    at com.google.common.util.concurrent.AbstractFuture.get(AbstractFuture.java:533)
    at com.google.common.util.concurrent.FluentFuture$TrustedFuture.get(FluentFuture.java:82)
    at com.google.common.util.concurrent.ForwardingFuture.get(ForwardingFuture.java:62)
    at spyros.stackoverflow.example.ItemTest.testRepository(ItemTest.java:78)
    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:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: org.bson.codecs.configuration.CodecConfigurationException: Can't find a codec for interface spyros.stackoverflow.example.Item.
    at org.bson.codecs.configuration.CodecCache.getOrThrow(CodecCache.java:46)
    at org.bson.codecs.configuration.ProvidersCodecRegistry.get(ProvidersCodecRegistry.java:63)
    at org.bson.codecs.configuration.ProvidersCodecRegistry.get(ProvidersCodecRegistry.java:37)
    at com.mongodb.MongoCollectionImpl.getCodec(MongoCollectionImpl.java:591)
    at com.mongodb.MongoCollectionImpl.insertMany(MongoCollectionImpl.java:333)
    at com.mongodb.MongoCollectionImpl.insertMany(MongoCollectionImpl.java:322)
    at org.immutables.mongo.repository.Repositories$Repository$2.call(Repositories.java:130)
    at org.immutables.mongo.repository.Repositories$Repository$2.call(Repositories.java:127)
    at com.google.common.util.concurrent.TrustedListenableFutureTask$TrustedFutureInterruptibleTask.runInterruptibly(TrustedListenableFutureTask.java:125)
    at com.google.common.util.concurrent.InterruptibleTask.run(InterruptibleTask.java:69)
    at com.google.common.util.concurrent.TrustedListenableFutureTask.run(TrustedListenableFutureTask.java:78)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)


    at org.junit.Assert.fail(Assert.java:88)
    at spyros.stackoverflow.example.ItemTest.testRepository(ItemTest.java:84)

I use gradle and the build.gradle is the following.我使用 gradle 并且build.gradle如下。

plugins {
    id 'java'
    id 'java-library'
}


sourceCompatibility = 1.8
targetCompatibility = 1.8

repositories {
    mavenLocal()
    mavenCentral()
    jcenter()
}

dependencies {
    implementation 'org.apache.logging.log4j:log4j-slf4j-impl:2.7'
    implementation 'org.apache.logging.log4j:log4j-core:2.7'
    implementation  'javax.persistence:javax.persistence-api:2.2'
    implementation  'javax.xml.bind:jaxb-api:2.1'
    implementation 'com.fasterxml.jackson.core:jackson-databind:2.9.8'
    implementation 'com.google.guava:guava:27.1-jre'

    
    annotationProcessor 'org.immutables:value:2.7.4'
    implementation 'org.immutables:value:2.7.4' 
    implementation 'org.immutables:mongo:2.7.4'
    

    implementation 'org.apache.commons:commons-lang3:3.4'
    implementation 'org.apache.commons:commons-collections4:4.1'
    implementation 'org.apache.commons:commons-configuration2:2.3'

   testCompile group: 'junit', name: 'junit', version: '4.12'
}

Identified cause已确定的原因

The Immutables JSON library needs to be able to find the autogenerated GsonAdaptersItem.class that implements com.google.gson.TypeAdapterFactory . Immutables JSON 库需要能够找到implements com.google.gson.TypeAdapterFactory的自动生成的GsonAdaptersItem.class In fact for every interface or abstract class named XXX with a similar annotation as in the 'class Item' (provided above) Immutables will generate a similar GsonAdapterXXX that implements com.google.gson.TypeAdapterFactory事实上,对于每一个名为XXX interfaceabstract class ,其注释与“类项”(上面提供)类似,不可变将生成一个类似的GsonAdapterXXX ,它implements com.google.gson.TypeAdapterFactory

In order to be able to find these classes automatically RepositorySetup uses java.util.ServiceLoader to automatically locate them.为了能够自动找到这些类, RepositorySetup使用java.util.ServiceLoader来自动定位它们。 This is the related code part:这是相关的代码部分:

    GsonBuilder gsonBuilder = new GsonBuilder();
    // there are no longer auto-registed from class-path, but from here or if added manually.
    gsonBuilder.registerTypeAdapterFactory(new TypeAdapters());

    for (TypeAdapterFactory factory : ServiceLoader.load(TypeAdapterFactory.class)) {
      gsonBuilder.registerTypeAdapterFactory(factory);
    }
    return gsonBuilder.create();

The cause of the Exception thrown is that for some reason this does not seem to work and the following exception is thrown.抛出异常的原因是由于某种原因这似乎不起作用,并抛出了以下异常。

UPDATE Further research shows that the example works, but only if the jar has been compiled.更新进一步的研究表明该示例有效,但前提是 jar 已编译。 This is because Immutables produces the following file that will actually be used to find all the required GsonAdapterXXX classes.这是因为 Immutables 生成以下文件,该文件实际上将用于查找所有必需的GsonAdapterXXX类。

 build\classes\java\main\META-INF\services\com.google.gson.TypeAdapterFactory

org.bson.codecs.configuration.CodecConfigurationException: Can't find a codec for interface spyros.stackoverflow.example.Item This is a permalink to the source code. org.bson.codecs.configuration.CodecConfigurationException:找不到接口 spyros.stackoverflow.example.Item 的编解码器这是源代码的永久链接。

Work around The straight forward work arround is to specifically register the GsonAdaptersItem to the GsonBuilder .解决方法直接的工作是将GsonAdaptersItem专门注册到GsonBuilder The following Junit Test works.以下 Junit 测试有效。

   @Test
    public void testItemVerbose() {
        try {
            //repeat what the RepositorySetup#createGson does
            GsonBuilder gsonBuilder = new GsonBuilder();
            //iterating in this loop
            for (TypeAdapterFactory factory : ServiceLoader.load(TypeAdapterFactory.class)) {
                //You can see that the ServiceLoader does not find the GsonAdaptersItem
                //System.out.println("Factory:"+factory.getClass().getCanonicalName()+" "+factory.toString());
                gsonBuilder.registerTypeAdapterFactory(factory);
            }
            
            //add the GsonAdaptersItem manually
            gsonBuilder.registerTypeAdapterFactory(new GsonAdaptersItem());
            Gson gson = gsonBuilder.create();



            Item item = ImmutableItem.builder()
                    .id(1)
                    .name("one")
                    .build();
            
            final MongoClient mongo = new MongoClient( "localhost" , 27017 );
            final MongoDatabase mongoDatabase=mongo.getDatabase("test");
            ItemRepository items =new ItemRepository(
                RepositorySetup.builder()
                        .executor(MoreExecutors.newDirectExecutorService())
                        .database(mongoDatabase)
                        .gson(gson).build()
                        );
            // Insert async and get
            items.insert(item).get(); // returns future, works


        }catch (final Exception e){
             e.printStackTrace();
           Assert.fail(e.getMessage());
        }

    }

This workaround could be used in order to manually create an api that is generic and the programmer needs to provide the GsonAdapterXXX .此解决方法可用于手动创建通用的 api,并且程序员需要提供GsonAdapterXXX Eg ItemRepository itemRepository=reporitoryProvider.getRepositoryWithRegisteredGsonAdapter(new GsonAdaptersItem());例如ItemRepository itemRepository=reporitoryProvider.getRepositoryWithRegisteredGsonAdapter(new GsonAdaptersItem()); More specific XXXRepository providers can also be created.还可以创建更具体的XXXRepository提供程序。

public ItemRepository getItemRepository(){
return reporitoryProvider.getRepositoryWithRegisteredGsonAdapter(new GsonAdaptersItem());
}

However, all the above require maintenance, are more error prone and will anyway require writting boilerplate code, which is what someone using Immutables expects to avoid.但是,上述所有内容都需要维护,更容易出错,并且无论如何都需要编写样板代码,这是使用 Immutables 的人希望避免的。

Question

I expect that the example should work as shown in the immutables site .我希望该示例应该像immutables 站点中所示那样工作。 My best guess is that since I use gradle instead of maven perhaps I need to change some setting or dependency.我最好的猜测是,因为我使用 gradle 而不是 maven,所以我可能需要更改一些设置或依赖项。 The question is: What needs to be changed so that the simple ItemRepository creation (shown below) works?问题是:需要更改什么才能使简单的 ItemRepository 创建(如下所示)起作用?

 //Simple ItemRepository creation
ItemRepository items = new ItemRepository(
     RepositorySetup.forUri("mongodb://localhost/test")
     );

Is it possible to do it with Immutables and without manually writting code that registers codecs class by class or package by package?是否可以使用 Immutables 而无需手动编写代码来逐个类或逐个包地注册编解码器? I would really appreciate an explanation of why it did not work in the first place.我真的很感激解释为什么它首先不起作用。

I am revisiting this old question after some time.一段时间后,我正在重新审视这个老问题。 Currently the suggested solution would be to use the criteria functionality of immutables.目前建议的解决方案是使用不可变的标准功能。

@Value.Immutable
@Criteria.Repository
public abstract class Item {
  
  @Criteria.Id
  public abstract long id();
  public abstract String name();
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 找不到我的课程的编解码器(CodecConfigurationException) - Can't find a codec for my class (CodecConfigurationException) MongoDB Java 插入引发 org.bson.codecs.configuration.CodecConfigurationException:找不到类 io.github.ilkgunel.mongodb.Pojo 的编解码器 - MongoDB Java Inserting Throws org.bson.codecs.configuration.CodecConfigurationException: Can't find a codec for class io.github.ilkgunel.mongodb.Pojo Morphia - CodecConfigurationException:找不到类的编解码器 - 但类已注册 - Morphia - CodecConfigurationException: Can't find a codec for class - But class is registered org.bson.codecs.configuration.CodecConfigurationException:找不到类 org.hibernate.ogm.datastore.mongodb.type.GridFS 的编解码器 - org.bson.codecs.configuration.CodecConfigurationException: Can't find a codec for class org.hibernate.ogm.datastore.mongodb.type.GridFS MongoDB jodatime:org.bson.codecs.configuration.CodecConfigurationException:找不到类org.joda.time.DateTime的编解码器 - MongoDB jodatime: org.bson.codecs.configuration.CodecConfigurationException: Can't find a codec for class org.joda.time.DateTime spring-boot 2.1.0 mongo-CodecConfigurationException:找不到类java.time.Year的编解码器 - spring-boot 2.1.0 mongo - CodecConfigurationException: Can't find a codec for class java.time.Year mongo-operations spring mongo批量操作执行异常(CodecConfigurationException:找不到类的编解码器) - mongo-operations spring mongo bulk opeartion execution exception (CodecConfigurationException: Can't find a codec for class) MongoDB Java插入引发找不到类org.variabel.BsonDocument的编解码器 - MongoDB Java Inserting Throws Can't find a codec for class org.variabel.BsonDocument MongoDB Java CodecConfigurationException 找不到公共构造函数 - MongoDB Java CodecConfigurationException Cannot find a public constructor for 找不到类org.springframework.data.mongodb.core.query.GeoCommand的编解码器 - Can't find a codec for class org.springframework.data.mongodb.core.query.GeoCommand
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM