我们正在从Morphia 1.3.2升级到Morphia 2.3.2
在我们最近升级的微服务中,有一个包含字段messageTemplates的类:
private Map<Locale, MessageTemplate> messageTemplates;
public Map<Locale, MessageTemplate> getMessageTemplates() {
if(messageTemplates == null){
this.messageTemplates = new HashMap<>();
}
return messageTemplates;
}
public void setMessageTemplates(Map<Locale, MessageTemplate> messageTemplates) {
this.messageTemplates = messageTemplates;
}
Mongo集合中该字段的json数据如下所示:
"_id" : "55c8a25e-137c-44f8-8e75-18617aafd374",
"className" : "com.swipejobs.desktopnotifications.model.NotificationTypeDetails",
"messageTemplates" : {
"en" : {
"subject" : "",
"body" : "Your verification PIN is ${pincode}. Please do not reply to this message.",
"emailSubject" : "",
"emailBody" : ""
}
},
当从集合中检索文档时,Map中的键是String类的对象,而不是Locale类型的对象。因此,Map的键在保存到集合时被转换为String,但在检索文档时似乎不会Map回Locale。
我们的MongoClient设置如下:
import com.swipejobs.dataaccess.codecs.ZoneIdCodec;
import com.swipejobs.dataaccess.codecs.ZonedDateTimeCodec;
import dev.morphia.Datastore;
import dev.morphia.mapping.codec.LocaleCodec;
CodecRegistry codecRegistry = CodecRegistries.fromRegistries(CodecRegistries.fromCodecs(new ZonedDateTimeCodec(),
new ZoneIdCodec(),
new LocaleCodec()),
MongoClientSettings.getDefaultCodecRegistry(),
CodecRegistries.fromProviders(PojoCodecProvider.builder().automatic(true).build()));
ConnectionString connectionString = new ConnectionString(uri);
MongoClientSettings settings = MongoClientSettings.builder().codecRegistry(codecRegistry).applyConnectionString(connectionString).build();
MongoClient mongoClient = MongoClients.create(settings);
MapperOptions options = MapperOptions.builder().discriminator(DiscriminatorFunction.className()).discriminatorKey("className").collectionNaming(NamingStrategy.identity()).build();
mDataStore = Morphia.createDatastore(mMongoClient, mDbName, options);
mDataStore.ensureIndexes();
datastore.getMapper().map(NotificationTypeDetails.class);
有谁知道这个场景吗?
我们期望Map中的键是Locale类型而不是String类型。
我们添加了以下hack:
private Map<Locale, MessageTemplate> fixMap(Map<Locale, MessageTemplate> map){
return map.entrySet().stream()
.map(this::fixEntry)
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}
private Map.Entry<Locale, MessageTemplate> fixEntry(Map.Entry<Locale, MessageTemplate> entry) {
Locale key = fixKey(entry.getKey());
MessageTemplate value = entry.getValue();
return Map.of(key, value).entrySet().stream().findFirst().get();
}
private Locale fixKey(Object key){
if (key.getClass().equals(Locale.class))
return (Locale) key;
if (key.getClass().equals(String.class))
return new Locale((String) key);
return Locale.ENGLISH;
}
这解决了我们的问题,但没有解释为什么会发生。正如前面所看到的,我们已经添加了LocaleCodec,但它似乎什么也没做。
1条答案
按热度按时间nmpmafwu1#
我实际上是修复了一个错误有关的这只是昨晚,并做了一些挖掘。由于许多技术原因,Map键不受编解码器解/串行化的影响。这些键使用
Conversions
条目转换为String
s/从String
s转换。现在这并不完全是一个 * 公共 * API,但它不时被不同的用户使用。由于它不是一个公共API,它可能会在你的控制下发生变化,但它已经稳定了几年,所以我认为这种恐惧在这一点上主要是假设的。接下来,我将向您展示如何定义一个:要注册一个转换,需要调用
Conversions.regsiter()
,它具有以下签名下面是其中一个内置转换:
此转换将从目标类型为
URI
的数据库中获取String
,并调用适当的代码进行转换。在本例中,它是URI.create
(它对字符串中的转义字符进行了一些清理)。对于Locale
,您将执行类似的操作。有两点需要注意:1.此转换适用于 all
String
->Locale
转换,因此需要进行一般化。在本例中,Locale
是一个非常普通的类型,因此对特殊和特定转换的需求几乎肯定为0。1.在向数据库写入 to 时,如果没有找到转换,则所使用的Map编解码器将简单地
toString()
键,因为数据库中的文档键只能是字符串。这可能不会产生您想要的格式,或者可以简单地传递回Locale
方法以解析出的格式。如果是这样的话,你需要在应用程序代码中的某个地方做`register(Locale.class,String.class,l -> { /* convert your Locale to a String here /* })。这应该能帮你摆平如果由于某种原因这不起作用,请提交一个issue或在GitHub上打开一个discussion,我会尽力让你修复。