MongoDB 多态性 Java 对象无法确定具体类

huangapple go评论137阅读模式
英文:

Mongodb polymorphism Java object unable to determine concrete class

问题

我正在使用Micronaut与Mongodb,并且在读取多态Java对象时遇到错误,因为Mongodb无法通过其类型确定具体的实现类。

myPet: { "_id" : ObjectId("5f96f4633dbd690c548c2a38"), pets: [{"type" : "Cat" }, {"type" : "Dog" }]}

public class MyPet {
  private List<Animal> pets = new ArrayList<Animal>(0);

  public List<Animal> getPets() {
    return pets;
  }

  public void setPets(List<Animal> pets) {
    this.pets = pets;
  }
}

public class Cat implements Animal {
  private String type = "Cat";

  public String getType() {
    return type;
  }

  public void setType(String type) {
    this.type = type;
  }
}

public class Dog implements Animal {
  private String type = "Dog";

  public String getType() {
    return type;
  }

  public void setType(String type) {
    this.type = type;
  }
}

public interface Animal {
  String getType();
}

我尝试了以下代码:

MongoCollection<MyPet> myPetColl = db.getCollection("myPet", MyPet.class);
myPetColl.find()

但是出现以下错误:

意外错误使用AutomaticPojoCodec进行解码时发生异常
解码为'MyPet'失败异常如下

解码'content'时出错找不到'Animal'的公共构造函数

可能需要显式配置和注册自定义的Codec或PojoCodec来处理此类型
org.bson.codecs.configuration.CodecConfigurationException使用AutomaticPojoCodec进行解码时发生异常
解码为'MyPet'失败异常如下

解码'content'时出错找不到'Animal'的公共构造函数

可能需要显式配置和注册自定义的Codec或PojoCodec来处理此类型
        at org.bson.codecs.pojo.AutomaticPojoCodec.decode(AutomaticPojoCodec.java:40)
        at com.mongodb.internal.operation.CommandResultArrayCodec.decode(CommandResultArrayCodec.java:52)
        ...

是否有办法使用if条件返回正确的类类型:

如果 (obj.type == "Dog") 返回 Dog.class,否则返回 Cat.class

就像在Jackson中一样:

@JsonTypeInfo(
        use = JsonTypeInfo.Id.NAME,
        include = JsonTypeInfo.As.PROPERTY,
        property = "type")
@JsonSubTypes({
        @JsonSubTypes.Type(value = Dog.class, name = "Dog"),
        @JsonSubTypes.Type(value = Cat.class, name = "Cat"),
})
英文:

I am using Mongodb with Micronaut and reading polymorphic Java object gives me error because Mongodb is unable to determine the concrete implementation class by its type.

myPet: { &quot;_id&quot; : ObjectId(&quot;5f96f4633dbd690c548c2a38&quot;), pets: [{&quot;type&quot; : &quot;Cat&quot; }, {&quot;type&quot; : &quot;Dog&quot; }]}

public class MyPet {
  private List&lt;Animal&gt; pets = new ArrayList&lt;Animal&gt;(0);

  public List&lt;Animal&gt; getPets() {
    return pets;
  }

  public void setPets(List&lt;Animal&gt; pets) {
    this.pets = pets;
  }
}

public class Cat implements Animal {
  private String type = &quot;Cat&quot;;

  public String getType() {
    return type;
  }

  public void setType(String type) {
    this.type = type;
  }
}

public class Dog implements Animal {
  private String type = &quot;Dog&quot;;

  public String getType() {
    return type;
  }

  public void setType(String type) {
    this.type = type;
  }
}

public interface Animal {
  String getType();
}

I tried

MongoCollection&lt;MyPet&gt; myPetColl = db.getCollection(&quot;myPet&quot;, MyPet.class);
myPetColl.find()

but get below error

Unexpected error occurred: An exception occurred when decoding using the AutomaticPojoCodec.
Decoding into a &#39;MyPet&#39; failed with the following exception:

Failed to decode &#39;MyPet&#39;. Decoding &#39;content&#39; errored with: Cannot find a public constructor for &#39;Animal&#39;.

A custom Codec or PojoCodec may need to be explicitly configured and registered to handle this type.
org.bson.codecs.configuration.CodecConfigurationException: An exception occurred when decoding using the AutomaticPojoCodec.
Decoding into a &#39;MyPet&#39; failed with the following exception:

Failed to decode &#39;MyPet&#39;. Decoding &#39;content&#39; errored with: Cannot find a public constructor for &#39;Animal&#39;.

A custom Codec or PojoCodec may need to be explicitly configured and registered to handle this type.
        at org.bson.codecs.pojo.AutomaticPojoCodec.decode(AutomaticPojoCodec.java:40)
        at com.mongodb.internal.operation.CommandResultArrayCodec.decode(CommandResultArrayCodec.java:52)
        at com.mongodb.internal.operation.CommandResultDocumentCodec.readValue(CommandResultDocumentCodec.java:60)
        at org.bson.codecs.BsonDocumentCodec.decode(BsonDocumentCodec.java:84)
        at org.bson.codecs.BsonDocumentCodec.decode(BsonDocumentCodec.java:41)
        at org.bson.internal.LazyCodec.decode(LazyCodec.java:48)
        at org.bson.codecs.BsonDocumentCodec.readValue(BsonDocumentCodec.java:101)
        at com.mongodb.internal.operation.CommandResultDocumentCodec.readValue(CommandResultDocumentCodec.java:63)
        at org.bson.codecs.BsonDocumentCodec.decode(BsonDocumentCodec.java:84)
        at org.bson.codecs.BsonDocumentCodec.decode(BsonDocumentCodec.java:41)
        at com.mongodb.internal.connection.ReplyMessage.&lt;init&gt;(ReplyMessage.java:51)
        at com.mongodb.internal.connection.InternalStreamConnection.getCommandResult(InternalStreamConnection.java:412)
        at com.mongodb.internal.connection.InternalStreamConnection.access$900(InternalStreamConnection.java:75)
        at com.mongodb.internal.connection.InternalStreamConnection$2$1.onResult(InternalStreamConnection.java:397)
        at com.mongodb.internal.connection.InternalStreamConnection$2$1.onResult(InternalStreamConnection.java:375)
        at com.mongodb.internal.connection.InternalStreamConnection$MessageHeaderCallback$MessageCallback.onResult(InternalStreamConnection.java:676)
        at com.mongodb.internal.connection.InternalStreamConnection$MessageHeaderCallback$MessageCallback.onResult(InternalStreamConnection.java:643)
        at com.mongodb.internal.connection.InternalStreamConnection$5.completed(InternalStreamConnection.java:513)
        at com.mongodb.internal.connection.InternalStreamConnection$5.completed(InternalStreamConnection.java:510)
        at com.mongodb.internal.connection.AsynchronousChannelStream$BasicCompletionHandler.completed(AsynchronousChannelStream.java:230)
        at com.mongodb.internal.connection.AsynchronousChannelStream$BasicCompletionHandler.completed(AsynchronousChannelStream.java:213)
        at java.base/sun.nio.ch.Invoker.invokeUnchecked(Invoker.java:127)
        at java.base/sun.nio.ch.Invoker.invokeDirect(Invoker.java:158)
        at java.base/sun.nio.ch.UnixAsynchronousSocketChannelImpl.implRead(UnixAsynchronousSocketChannelImpl.java:560)
        at java.base/sun.nio.ch.AsynchronousSocketChannelImpl.read(AsynchronousSocketChannelImpl.java:277)
        at java.base/sun.nio.ch.AsynchronousSocketChannelImpl.read(AsynchronousSocketChannelImpl.java:298)
        at com.mongodb.internal.connection.AsynchronousSocketChannelStream$AsynchronousSocketChannelAdapter.read(AsynchronousSocketChannelStream.java:136)
        at com.mongodb.internal.connection.AsynchronousChannelStream.readAsync(AsynchronousChannelStream.java:109)
        at com.mongodb.internal.connection.InternalStreamConnection.readAsync(InternalStreamConnection.java:510)
        at com.mongodb.internal.connection.InternalStreamConnection.access$1000(InternalStreamConnection.java:75)
        at com.mongodb.internal.connection.InternalStreamConnection$MessageHeaderCallback.onResult(InternalStreamConnection.java:633)
        at com.mongodb.internal.connection.InternalStreamConnection$MessageHeaderCallback.onResult(InternalStreamConnection.java:618)
        at com.mongodb.internal.connection.InternalStreamConnection$5.completed(InternalStreamConnection.java:513)
        at com.mongodb.internal.connection.InternalStreamConnection$5.completed(InternalStreamConnection.java:510)
        at com.mongodb.internal.connection.AsynchronousChannelStream$BasicCompletionHandler.completed(AsynchronousChannelStream.java:230)
        at com.mongodb.internal.connection.AsynchronousChannelStream$BasicCompletionHandler.completed(AsynchronousChannelStream.java:213)
        at java.base/sun.nio.ch.Invoker.invokeUnchecked(Invoker.java:127)
        at java.base/sun.nio.ch.UnixAsynchronousSocketChannelImpl.finishRead(UnixAsynchronousSocketChannelImpl.java:437)
        at java.base/sun.nio.ch.UnixAsynchronousSocketChannelImpl.finish(UnixAsynchronousSocketChannelImpl.java:191)
        at java.base/sun.nio.ch.UnixAsynchronousSocketChannelImpl.onEvent(UnixAsynchronousSocketChannelImpl.java:213)
        at java.base/sun.nio.ch.KQueuePort$EventHandlerTask.run(KQueuePort.java:312)
        at java.base/sun.nio.ch.AsynchronousChannelGroupImpl$1.run(AsynchronousChannelGroupImpl.java:112)
        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
        at java.base/java.lang.Thread.run(Thread.java:834)

Is there a way to return the correct class type with if-condition:

if (obj.type == "Dog") return Dog.class else Cat.class

like in Jackson

@JsonTypeInfo(
        use = JsonTypeInfo.Id.NAME,
        include = JsonTypeInfo.As.PROPERTY,
        property = &quot;type&quot;)
@JsonSubTypes({
        @JsonSubTypes.Type(value = Dog.class, name = &quot;Dog&quot;),
        @JsonSubTypes.Type(value = Cat.class, name = &quot;Cat&quot;),
})

答案1

得分: 2

你可以使用 @BsonDiscriminator 注解来告诉 MongoDB 驱动程序哪个字段被用作鉴别键。这里有一个可用的示例:

import org.bson.codecs.pojo.annotations.BsonDiscriminator;

@BsonDiscriminator(key = Animal.DISCRIMINATOR_KEY)
public interface Animal {
    String DISCRIMINATOR_KEY = "type";

    String getType();
}

public class Cat implements Animal {
    @Override
    public String getType() {
        return Cat.class.getName();
    }
}

public class Dog implements Animal {
    @Override
    public String getType() {
        return Dog.class.getName();
    }
}

正如你所见,我在 type 字段的值中使用了完整的类名。不幸的是,自定义的鉴别值(与完整类名不同)在 Micronaut MongoDB Client 中目前无法工作。这是因为这个问题,目前还没有解决:https://github.com/micronaut-projects/micronaut-mongodb/issues/10

英文:

You can use @BsonDiscriminator annotation to tell MongoDB driver which field is used as a discriminator key. Here is a working example:

import org.bson.codecs.pojo.annotations.BsonDiscriminator;

@BsonDiscriminator(key = Animal.DISCRIMINATOR_KEY)
public interface Animal {
    String DISCRIMINATOR_KEY = &quot;type&quot;;

    String getType();
}

public class Cat implements Animal {
    @Override
    public String getType() {
        return Cat.class.getName();
    }
}

public class Dog implements Animal {
    @Override
    public String getType() {
        return Dog.class.getName();
    }
}

As you can see, I'm using full class name as a value for type field. Unfortunately custom discriminator values (different then full class name) does not work yet in Micronaut MongoDB Client now. It is because of this issue, which still not solved: https://github.com/micronaut-projects/micronaut-mongodb/issues/10

huangapple
  • 本文由 发表于 2020年10月27日 06:57:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/64546106.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定