3

我們使用Objectify和Google App Engine for Java。我們使用提供的EnumTranslatorFactory在數據存儲中堅持使用各種枚舉常量,它只使用Enum#name()來存儲/加載常量。這很好。Enum Addition添加對象化/ Appengine的向後兼容性

當我們將應用程序的新版本發佈到GAE時,新版本與舊版本緊鄰,它們同時向客戶端提供請求。這可以通過Google's traffic splitting docs很好地解釋。

升級到系統會引入新的Enum常量,這會在加載過程中導致錯誤。例如:

版1具有以下枚舉:

enum Meal{BREAKFAST,LUNCH,DINNER} 

版2具有添加到枚舉的附加常數以支持英國餐:

enum Meal{BREAKFAST,LUNCH,TEA,DINNER} 

在測試該應用的版本2 ,TEA將堅持與一些實體。隨後版本1將加載該實體,Objectify將嘗試使用Enum#valueOf(...)將TEA轉換爲Enum,這會引發運行時異常。

Objectify docs解釋Data Migration Enums,但它不符合上述情況。

我對如何最好地處理這種情況的建議感興趣。

回答

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; 
      } 
     } 
    } 

部署新的枚舉第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' /> 

該溶液允許添加和在以後的版本中使用而舊版本呈現的值,以每合同中的「未來」是可能的客戶機附加枚舉。

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。

1

一般來說,我會建議做兩個升級到您的應用程序。首先,進行升級,只理解新的枚舉值(但從不寫入)並將其傳播到整個系統中。然後製作一個實際寫入新值的版本。

數據遷移是很難的,尤其是當你想使用流量拆分。將其分爲多個步驟和多個部署。

+0

你的建議是有可能的情況,一個是我們正在使用的代碼維護目的枚舉的重命名的情況。然而,對於增加我們希望使用具有新功能的新枚舉,這是痛苦的建立新的功能,而不是使用它。我們希望把這些多個版本數週或數月運行並排側,以便我們的特點將通過此枚舉限制的人質。 –

+1

我真的不知道你在找什麼樣的解決方案。您想要將一些新數據寫入數據存儲區,舊版應用程序無法理解。沒有什麼魔力,你需要讓你的舊版本適度地處理新的數據,然後才能更改數據結構。這適用於任何類型的遷移,而不僅僅是枚舉。 – stickfigure