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

时间:2019-06-20 09:14:06

标签: java mongodb gradle immutables-library

问题描述。

此后,我使用immutables site中当前示例的简化版本。

所以我有一个要与MongoDB一起使用的项目。

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

为简单起见,我创建了一个Junit测试。就像例子一样。 我的问题是,尽管在immutables site的示例中展示了这个junit测试,但它抛出了一个异常:

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

运行此测试将导致以下异常:

  

org.bson.codecs.configuration.CodecConfigurationException:找不到   接口spyros.stackoverflow.example.Item

的编解码器

下面提供了完整的堆栈跟踪。

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)

我使用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'
}

已查明原因

Immutables JSON库需要能够找到自动生成的GsonAdaptersItem.class implements com.google.gson.TypeAdapterFactory。实际上,对于每个名为interface的{​​{1}}或abstract class,其注释都与“类项”中的注释类似(上面提供),不可变对象会生成与{{1 }}

为了能够自动找到这些类,XXX使用GsonAdapterXXX自动找到它们。 这是相关的代码部分:

implements com.google.gson.TypeAdapterFactory

引发异常的原因是由于某种原因该异常似乎不起作用,并且引发了以下异常。

更新 进一步的研究表明,该示例有效,但前提是该jar已编译。这是因为Immutables生成以下文件,该文件实际上将用于查找所有必需的RepositorySetup类。

java.util.ServiceLoader
  

org.bson.codecs.configuration.CodecConfigurationException:找不到   接口spyros.stackoverflow.example.Item的编解码器   This is a permalink to the source code.

变通 直接的工作方式是将 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(); 专门注册到GsonAdapterXXX。 以下Junit测试有效。

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

可以使用此替代方法来手动创建通用的api,程序员需要提供GsonAdaptersItem。例如。 GsonBuilder 还可以创建更具体的 @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()); } } 提供者。

GsonAdapterXXX

但是,以上所有方面都需要维护,而且更容易出错,并且无论如何都需要编写样板代码,这是使用Immutables的人希望避免的事情。

问题

我希望该示例能够如immutables site所示工作。我最好的猜测是,由于我使用gradle而不是maven,也许我需要更改一些设置或依赖项。 问题是: 为了使简单的ItemRepository创建(如下所示)有效,需要更改什么?

ItemRepository itemRepository=reporitoryProvider.getRepositoryWithRegisteredGsonAdapter(new GsonAdaptersItem());

是否可以使用Immutables进行操作,而无需手动编写逐级注册或逐包注册编解码器的代码? 我真的很感谢一个解释,为什么它首先不起作用。

1 个答案:

答案 0 :(得分:0)

我在一段时间后重新审视这个老问题。 目前建议的解决方案是使用不可变的 criteria 功能。

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