MongoDB Java驱动POJO序列化/反序列化DBRef报错的解决咨询
解决MongoDB Java驱动POJO反序列化DBRef的UnsupportedOperationException问题
问题原因
MongoDB Java驱动4.x版本自带的DBRefCodec仅支持将DBRef序列化为BSON,但不支持反向的反序列化(从BSON解码为DBRef对象),所以当POJO中定义DBRef类型字段时,查询后反序列化会抛出UnsupportedOperationException: DBRefCodec does not support decoding。
解决方案
方案一:自定义DBRef Codec(推荐,完整支持DBRef序列化/反序列化)
编写自定义的Codec实现DBRef的解码逻辑,然后注册到CodecRegistry中:
- 实现自定义DBRef Codec
import com.mongodb.DBRef; import com.mongodb.client.MongoDatabase; import org.bson.BsonDocument; import org.bson.codecs.Codec; import org.bson.codecs.DecoderContext; import org.bson.codecs.EncoderContext; import org.bson.codecs.BsonDocumentCodec; import org.bson.types.ObjectId; import org.bson.BsonReader; import org.bson.BsonWriter; public class CustomDBRefCodec implements Codec<DBRef> { private final DBRefCodec originalCodec; private final MongoDatabase database; public CustomDBRefCodec(DBRefCodec originalCodec, MongoDatabase database) { this.originalCodec = originalCodec; this.database = database; } @Override public DBRef decode(BsonReader reader, DecoderContext decoderContext) { // 读取DBRef对应的BSON文档 BsonDocument dbRefDoc = new BsonDocumentCodec().decode(reader, decoderContext); String collectionName = dbRefDoc.getString("$ref").getValue(); ObjectId refId = dbRefDoc.getObjectId("$id"); // 构造DBRef对象 return new DBRef(database.getCollection(collectionName), refId); } @Override public void encode(BsonWriter writer, DBRef value, EncoderContext encoderContext) { // 复用原生Codec的编码逻辑 originalCodec.encode(writer, value, encoderContext); } @Override public Class<DBRef> getEncoderClass() { return DBRef.class; } }
- 注册自定义Codec到POJO CodecRegistry
import com.mongodb.client.MongoClients; import com.mongodb.client.MongoClient; import com.mongodb.client.MongoDatabase; import com.mongodb.client.MongoCollection; import org.bson.codecs.configuration.CodecRegistries; import org.bson.codecs.configuration.CodecRegistry; import org.bson.codecs.pojo.PojoCodecProvider; public class MongoConfig { public static MongoCollection<Recipe> getRecipeCollection() { MongoClient mongoClient = MongoClients.create("mongodb://localhost:27017"); MongoDatabase database = mongoClient.getDatabase("your_database_name"); // 初始化原生DBRefCodec和自定义Codec DBRefCodec originalDBRefCodec = new DBRefCodec(database.getCodecRegistry()); CustomDBRefCodec customDBRefCodec = new CustomDBRefCodec(originalDBRefCodec, database); // 组合CodecRegistry:默认Codec + 自定义DBRefCodec + POJO自动映射 CodecRegistry codecRegistry = CodecRegistries.fromRegistries( MongoClientSettings.getDefaultCodecRegistry(), CodecRegistries.fromCodecs(customDBRefCodec), CodecRegistries.fromProviders(PojoCodecProvider.builder().automatic(true).build()) ); // 应用CodecRegistry到集合 return database.getCollection("recipe", Recipe.class) .withCodecRegistry(codecRegistry); } }
方案二:改用Document存储DBRef(轻量方案)
将POJO中的DBRef字段替换为Document,在业务逻辑中手动转换为DBRef:
public class Recipe implements Comparable<Recipe> { private String id; private Document author; // 用Document存储DBRef的BSON结构 // 手动转换为DBRef的方法 public DBRef getAuthorDBRef(MongoDatabase database) { if (author == null) return null; String collection = author.getString("$ref"); ObjectId id = author.getObjectId("$id"); return new DBRef(database.getCollection(collection), id); } // 设置DBRef的方法 public void setAuthorDBRef(DBRef authorRef) { this.author = new Document("$ref", authorRef.getCollectionName()) .append("$id", authorRef.getId()); } // 其余代码省略 }
这种方式不需要修改Codec配置,仅在需要使用DBRef时手动转换,适合快速适配场景。
补充说明
之前使用BasicDBObject可以正常工作,是因为BasicDBObject直接操作BSON文档:保存时将DBRef写入为标准的DBRef结构,读取时则作为普通Document返回,不会触发POJO反序列化的Codec校验,因此不会报错。
内容的提问来源于stack exchange,提问作者Tiago Godoy
相关产品推荐
相关产品推荐

