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.