有时 java 枚举是一个字符串

Sometimes java enum is a String

我们 运行 Wildfly 8.0.0 服务器上的 JAVA Web 应用程序。我们有一个电子邮件模板编辑器表单,它使用 Mustache 字段将一些可变数据放入模板中。邮件还有一个枚举字段,用于指定模板的类型。实体结构是这样的:

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "DTYPE", discriminatorType = DiscriminatorType.STRING, length = 255)
@DiscriminatorOptions(force = true)
@DiscriminatorValue("MailTemplate")
@Table(name = "MailTemplate")
public abstract class MailTemplate extends MyAbstractEntity // AbstractEntity holds the common id field for all entities
{
   private String body;
   private String subject;
}

public class CustomMailTemplate extends MailTemplate implements java.io.Serializable {
    @Enumerated(EnumType.STRING)
    @Column(name = "templateType")
    private CustomTemplateType templateType;
}

public enum CustomTemplateType implements java.io.Serializable {
    OneTemplateType,
    AnotherTemplateType,
    AndAThirdOneTemplateType
}

我们使用 JPA 将我们的实体保存到 Oracle 数据库中。这是处理各种类型模板的控制器结构:

public abstract class AbstractMailTemplateEditController<T extends MailTemplate, TYPE> {
    @Getter @Setter
    protected T entity;

    @Getter @Setter
    protected String subject;

    @Getter @Setter
    protected String body;

    @Getter @Setter
    protected TYPE templateType;    
}

public class CustomMailTemplateEditController extends AbstractMailTemplateEditController<CustomMailTemplate, CustomTemplateType> implements Serializable {
    // this is in a service which injected, it's just for the code example
    @PersistenceContext
    private EntityManager em;

    public void saveTemplate(){
        // let's assume now that our entity property exists
        entity.setBody(body);
        entity.setSubject(subject);
        entity.setTemplateType(templateType); // SOMETHING WRONG HAPPENS HERE!
        em.merge(entity);
    }
}

这是非常奇怪的部分:

服务器重启后,我们可以毫无问题地保存我们的模板。 'T' 时间后我们无法保存模板,我们收到以下错误消息:

java.lang.String cannot be cast to CustomTemplateType

我开始对此进行调试,在 entity.setTemplateType(templateType); 行中我看到 templateTypeString,但这只会在服务器重启一段时间后发生。在同一个地方重新启动后,templateTypeCustomTemplateType,我可以保存模板。 它是怎么发生的,我该怎么做才能解决这个问题?

更新:根据 Tobias Liefke 的回答,我检查了我们如何在视图中使用此控制器,我发现了以下内容:

<h:selectOneMenu value="#{bean.templateType}" styleClass="form-control" id="template-type" >
    <f:selectItems value="#{bean.mailTemplateTypes}" var="item" itemLabel="#{msg[item]}" itemValue="#{item}" />
    <f:selectItem itemLabel="Special template" itemValue="#{null}" />
</h:selectOneMenu>

#{bean.mailTemplateTypes} 是一个枚举列表。那么这可能是一个 JSF 错误吗?我的意思是过了一段时间(没有服务器重启的几天、几周)JSF 开始将选定的 mailTemplateType 作为 String 传递到 #{bean.templateType} 值中。可能吗?我检查了这个问题:How to use enum values in f:selectItem(s) 并且基于 BalusC 的回答,JSF 有一个用于 enum 的内置转换器。但这是我们设置 templateType 变量的唯一部分。

问题是未绑定的 <TYPE>,它将被编译为 protected Object templateType;public void setTemplateType(Object templateType)

您的应用程序中有人正在用 String 呼叫 setTemplateType()。如果他使用反射(就像 Mustache Fields 会做的那样)或者如果他使用未绑定的 AbstractMailTemplateEditController.

,这是可能的

如果 AbstractMailTemplateEditController 的所有子 class 都使用 TYPE 的枚举,您可以将其绑定到 Enum:

public abstract class AbstractMailTemplateEditController<T extends MailTemplate, TYPE extends Enum<TYPE>> {

这将导致 class 文件中的 public void setTemplateType(Enum<?> templateType) - 一旦有人用 String.[=26 调用它,它就会抛出 ClassCastException =]

否则你可以覆盖 setTemplateTypeCustomMailTemplateEditController 并进行显式类型检查:

public class CustomMailTemplateEditController ... {
    @Override 
    public void setTemplateType(CustomTemplateType templateType) {
        super.setTemplateType(templateType);
    }
}