我们正在使用Objectify与Google App Engine for Java。我们使用提供的EnumTranslatorFactory在数据存储区中持久化各种枚举常量,Enum#name()只使用Google's traffic splitting docs存储/加载常量。这很有效。
当我们向GAE发布我们的应用程序的新版本时,新版本位于旧版本旁边,同时向客户端提供请求。 Data Migration解释了这一点。
升级到系统会引入新的Enum常量,这些常量会在加载过程中导致错误。例如:
版本1具有以下枚举:
enum Meal{BREAKFAST,LUNCH,DINNER}
版本2在枚举中添加了额外的常量来支持英国餐:
enum Meal{BREAKFAST,LUNCH,TEA,DINNER}
在测试应用版本2时,TEA将与某些实体一起保留。随后版本1将加载该实体,Objectify将尝试使用Enum#valueOf(...)将TEA转换为枚举,这将引发运行时异常。
Objectify docs为Enums解释{{3}},但它不满足上述情况。
我对如何最好地处理这种情况的建议很感兴趣。
答案 0 :(得分:2)
首先,提供一个接口,如果枚举未知,它将提供默认值。
public interface EnumWithDefault<E extends Enum<E>> {
E getDefault();
}
可能有未来添加的枚举应实现此接口:
public enum MyEnum implements EnumWithDefault<MyEnum>{
ENUM_IN_VERSION_1, FUTURE;
public MyEnum getDefault(){ return FUTURE; }
}
注册一个TranslatorFactory,如果实现,将提供默认值:
return new ValueTranslator<Enum<?>, String>(path, String.class) {
@Override
public Enum<?> loadValue(String value, LoadContext ctx) {
try{
return Enum.valueOf((Class<Enum>)type, value.toString());
}catch(Exception e){
if (EnumWithDefault.class.isAssignableFrom(enumType)) {
EnumWithDefault<E> any = (EnumWithDefault<E>) enumType.getEnumConstants()[0];
result = any.getDefault();
}else{
throw e;
}
}
}
使用新的Enum部署版本2:
public enum MyEnum implements EnumWithDefault<MyEnum>{
ENUM_IN_VERSION_1, ENUM_IN_VERSION_2, FUTURE;
public MyEnum getDefault(){ return FUTURE; }
}
当部署应用版本2并且ENUM_IN_VERSION_2存储在与某个实体相关的数据存储区中时,响应两个版本的端点时响应会有所不同。
点击第一个版本会返回值FUTURE,允许客户端显示相应的消息:
http://1.myapi.appspot.com/entities
返回:
<myEntity id='xyz' category='FUTURE' />
点击版本2提供了新的枚举:
http://2.myapi.appspot.com/entities
返回:
<myEntity id='xyz' category='ENUM_IN_VERSION_2' />
此解决方案允许在以后的版本中添加和使用其他枚举,而旧版本根据可能“未来”的合同向客户端提供值。
答案 1 :(得分:1)
一般情况下,我建议您对应用进行两次升级。首先,进行升级,只能理解新的枚举值(但绝不会写入)并在整个系统中传播。然后创建一个实际写入新值的版本。
数据迁移很难,尤其是当您想要使用流量分割时。将其分解为多个步骤和多个部署。
答案 2 :(得分:0)
编写您自己的自定义EnumTranslatorFactory,为任何尚未知道的值提供 null 。
....
return new ValueTranslator<Enum<?>, String>(path, String.class) {
@Override
public Enum<?> loadValue(String value, LoadContext ctx) {
try{
return Enum.valueOf((Class<Enum>)type, value.toString());
}catch(Exception e){
return null;
}
}
...
这并不理想,因为如果提供null,则可能需要该属性,而其他代码可能会失败。代码库中持久化枚举的所有属性必须是@Nullable。