Java - 在 MongoDB 中使用 Path 字段存储 Pojo

发布于 2025-01-12 16:42:27 字数 1333 浏览 4 评论 0原文

我正在使用 mongo-java-driver 在 MongoDB 中存储 POJO,到目前为止,我这样做没有遇到任何问题:

CodecProvider pojoCodecProvider = PojoCodecProvider.builder().automatic(true).build();
CodecRegistry pojoCodecRegistry = fromRegistries(getDefaultCodecRegistry(), fromProviders(pojoCodecProvider));

但是,当我向 POJO 添加 Path 变量时,我开始出现以下异常:

线程“Thread-2”org.bson.codecs.configuration.CodecConfigurationException中出现异常:找不到类的编解码器...

我想我会为 Path 编写一个编解码器,如下所示:

public class PathCodec implements Codec<Path> {

    @Override
    public void encode(BsonWriter writer, Path path, EncoderContext encoderContext) {
        writer.writeString(path.toString());    
    }

    @Override
    public Class<Path> getEncoderClass() {
        return Path.class;
    }

    @Override
    public Path decode(BsonReader reader, DecoderContext decoderContext) {
        return Paths.get(reader.readString());
    }   
    
}

然后像这样注册它

CodecProvider pojoCodecProvider = PojoCodecProvider.builder().automatic(true).build();
PathCodec pathCodec = new PathCodec();      
CodecRegistry pojoCodecRegistry = fromRegistries(getDefaultCodecRegistry(), fromCodecs(pathCodec), fromProviders(pojoCodecProvider));

:尽管异常继续发生,但似乎并没有达到目的。

任何正确方向的观点将不胜感激!

I'm using mongo-java-driver to store POJOs in MongoDB and so far I've had no issues, with doing just this:

CodecProvider pojoCodecProvider = PojoCodecProvider.builder().automatic(true).build();
CodecRegistry pojoCodecRegistry = fromRegistries(getDefaultCodecRegistry(), fromProviders(pojoCodecProvider));

However when I added a Path variable to my POJO I started getting the following exception:

Exception in thread "Thread-2" org.bson.codecs.configuration.CodecConfigurationException: Can't find a codec for class ...

I figured I'd write a codec for Path like this:

public class PathCodec implements Codec<Path> {

    @Override
    public void encode(BsonWriter writer, Path path, EncoderContext encoderContext) {
        writer.writeString(path.toString());    
    }

    @Override
    public Class<Path> getEncoderClass() {
        return Path.class;
    }

    @Override
    public Path decode(BsonReader reader, DecoderContext decoderContext) {
        return Paths.get(reader.readString());
    }   
    
}

and then register it like so:

CodecProvider pojoCodecProvider = PojoCodecProvider.builder().automatic(true).build();
PathCodec pathCodec = new PathCodec();      
CodecRegistry pojoCodecRegistry = fromRegistries(getDefaultCodecRegistry(), fromCodecs(pathCodec), fromProviders(pojoCodecProvider));

This doesn't seem to do the trick though i.e. the exception continues to occur.

Any point in the right direction would be much appreciated!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

柏林苍穹下 2025-01-19 16:42:27

这是我使用过的最复杂的系统之一。实现解决方案的方法有多种,每种解决方案都依赖于驱动程序版本。因此,有了这个警告,这里有一个选项供您使用类似的类:

@Document(collection = "example")
public class ExampleEntity implements Serializable {

    @Id
    private Long id;
    private DateTime date;

    public ExampleEntity() {
    }

    public ExampleEntity(Long id, DateTime date) {
        this.id = id;
        this.date = date;
    }

...
}

问题:MongoDB 不知道如何将 joda DateTime 转换为 MongoDB (BSON) 数据类型 DATE_TIME

解决方案:

public class DateTimeCodec implements Codec<DateTime> {
    @Override
    public void encode(BsonWriter writer, DateTime value, EncoderContext encoderContext) {
        writer.writeDateTime(value.getMillis()); // Take the joda DateTime field from the java class, convert to long, write to BSON
    }

    @Override
    public DateTime decode(BsonReader reader, DecoderContext decoderContext) {
        return new DateTime(reader.readDateTime());
    }

    @Override
    public Class<DateTime> getEncoderClass() {
        return DateTime.class;
    }
}

public class ExampleEntityCodec implements Codec<ExampleEntity> {
    private final CodecRegistry codecRegistry;

    public ExampleEntityCodec(CodecRegistry codecRegistry) {
        this.codecRegistry = codecRegistry;
    }

    @Override
    public void encode(BsonWriter writer, ExampleEntity value, EncoderContext encoderContext) {
        Codec<DateTime> dateTimeCodec = codecRegistry.get(DateTime.class);
        writer.writeStartDocument();
            writer.writeName("_id");
            writer.writeInt64(value.getId());
            writer.writeName("date");
            dateTimeCodec.encode(writer, value.getDate(), encoderContext); 
        writer.writeEndDocument();

    }

    @Override
    public ExampleEntity decode(BsonReader reader, DecoderContext decoderContext) {
        Codec<DateTime> dateTimeCodec = codecRegistry.get(DateTime.class);
        reader.readStartDocument();
            reader.readName();
            Long id = reader.readInt64();
            reader.readName();
            String className = reader.readString(); // ignoring this
            reader.readName();
            DateTime dateTimeFromDB = dateTimeCodec.decode(reader, decoderContext);
            ExampleEntity exampleEntity = new ExampleEntity(id, dateTimeFromDB);
        reader.readEndDocument();
        return exampleEntity;
    }

    @Override
    public Class<ExampleEntity> getEncoderClass() {
        return ExampleEntity.class;
    }
}

告诉 Java 何时需要使用您的编解码器:

public class ExampleEntityCodecProvider implements CodecProvider {
    @Override
    public <T> Codec<T> get(Class<T> clazz, CodecRegistry registry) {
        if(clazz == ExampleEntity.class) {
            return (Codec<T>) new ExampleEntityCodec(registry);
        }
        return null;
    }
}

构建编解码器:

...
    static CodecRegistry codecRegistry = CodecRegistries.fromRegistries(
            MongoClientSettings.getDefaultCodecRegistry(),  // Provides Codecs for Java's native datatypes
            CodecRegistries.fromCodecs(
                    new DateTimeCodec() // Required to convert BsonType DATE_TIME to joda.time.DateTime
            ),
            CodecRegistries.fromProviders(
                    new ExampleEntityCodecProvider(),
                    PojoCodecProvider.builder()
                            .automatic(true).build()
            )
    );
...

第一个编解码器 Codec 仅用于单个字段,而 Codec用于强制编写整个类的键/值对。请记住,当您编码时,您是在告诉 Mongo 您要使用哪种 BSON 数据类型。

在您的情况下,您似乎正在尝试对一个类进行字符串化,然后通过读取字符串再次构造它。您应该将 Java 类的键和值编码为要写入数据库的相应键和值,然后在解码时执行相反的转换。

This is one of the most complex systems I've ever worked with. There are multiple ways to implement a solution, and each solution is driver version dependent. So with that caveat, here is an option for you using similar classes:

@Document(collection = "example")
public class ExampleEntity implements Serializable {

    @Id
    private Long id;
    private DateTime date;

    public ExampleEntity() {
    }

    public ExampleEntity(Long id, DateTime date) {
        this.id = id;
        this.date = date;
    }

...
}

Problem: MongoDB doesn't know how to convert joda DateTime to/from the MongoDB (BSON) data type DATE_TIME

Solution:

public class DateTimeCodec implements Codec<DateTime> {
    @Override
    public void encode(BsonWriter writer, DateTime value, EncoderContext encoderContext) {
        writer.writeDateTime(value.getMillis()); // Take the joda DateTime field from the java class, convert to long, write to BSON
    }

    @Override
    public DateTime decode(BsonReader reader, DecoderContext decoderContext) {
        return new DateTime(reader.readDateTime());
    }

    @Override
    public Class<DateTime> getEncoderClass() {
        return DateTime.class;
    }
}

public class ExampleEntityCodec implements Codec<ExampleEntity> {
    private final CodecRegistry codecRegistry;

    public ExampleEntityCodec(CodecRegistry codecRegistry) {
        this.codecRegistry = codecRegistry;
    }

    @Override
    public void encode(BsonWriter writer, ExampleEntity value, EncoderContext encoderContext) {
        Codec<DateTime> dateTimeCodec = codecRegistry.get(DateTime.class);
        writer.writeStartDocument();
            writer.writeName("_id");
            writer.writeInt64(value.getId());
            writer.writeName("date");
            dateTimeCodec.encode(writer, value.getDate(), encoderContext); 
        writer.writeEndDocument();

    }

    @Override
    public ExampleEntity decode(BsonReader reader, DecoderContext decoderContext) {
        Codec<DateTime> dateTimeCodec = codecRegistry.get(DateTime.class);
        reader.readStartDocument();
            reader.readName();
            Long id = reader.readInt64();
            reader.readName();
            String className = reader.readString(); // ignoring this
            reader.readName();
            DateTime dateTimeFromDB = dateTimeCodec.decode(reader, decoderContext);
            ExampleEntity exampleEntity = new ExampleEntity(id, dateTimeFromDB);
        reader.readEndDocument();
        return exampleEntity;
    }

    @Override
    public Class<ExampleEntity> getEncoderClass() {
        return ExampleEntity.class;
    }
}

Tell Java when it is time to use your Codecs:

public class ExampleEntityCodecProvider implements CodecProvider {
    @Override
    public <T> Codec<T> get(Class<T> clazz, CodecRegistry registry) {
        if(clazz == ExampleEntity.class) {
            return (Codec<T>) new ExampleEntityCodec(registry);
        }
        return null;
    }
}

Build the codecs:

...
    static CodecRegistry codecRegistry = CodecRegistries.fromRegistries(
            MongoClientSettings.getDefaultCodecRegistry(),  // Provides Codecs for Java's native datatypes
            CodecRegistries.fromCodecs(
                    new DateTimeCodec() // Required to convert BsonType DATE_TIME to joda.time.DateTime
            ),
            CodecRegistries.fromProviders(
                    new ExampleEntityCodecProvider(),
                    PojoCodecProvider.builder()
                            .automatic(true).build()
            )
    );
...

The first Codec Codec<DateTime> is only used on a single field, whereas the Codec<ExampleEntity> is used to imperatively write the key/value pairs for an entire class. Remember, when you're encoding you're telling Mongo which BSON data types you want to use.

In your case, it looks like you're trying to Stringify a class and then construct it again from reading the string. You should instead encode the keys and values of your Java class into the appropriate keys and values you want written to the database, and then do the opposite conversion when decoding.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文