2011-11-03 87 views
2

我有这样的实体,称为“操作”:如何在JSF 2中创建自定义转换器?

@Entity 
@Table(name="operation") 
public class Operation implements Serializable { 
    private static final long serialVersionUID = 1L; 

    @Id 
    @GeneratedValue(strategy=GenerationType.SEQUENCE) 
    private Integer id; 

    @NotNull(message="informe um tipo de operação") 
    private String operation; 

    //bi-directional many-to-one association to Product 
    @OneToMany(mappedBy="operation") 
    private List<Product> products; 

    // getter and setters 
} 

我检索操作是这样的:(?这可能是通过一个EJB实例,但只是为了保持它的地方和作为一个例子,好不好;))

public Map<String, Object> getOperations() { 
    operations = new LinkedHashMap<String, Object>(); 
    operations.put("Select an operation", new Operation()); 
    operations.put("Donation", new Operation(new Integer(1), "donation")); 
    operations.put("Exchange", new Operation(new Integer(2), "exchange")); 

    return operations; 
} 

所以我试图让这个selectOneMenu所选择的操作:

productcManagedBean具有viewScopeproductb是一个sessionScope具有product这是我的实体ManagedBean。该产品contais一个operation,所以是这样的:

(信Ç拥有控制权,其中涉及关于我的实体产品的所有操作都应该由这个bean来处理的意思,好吗?)

Product productc (ViewScope) 
-- ProductBean productb (SessionScope) 
---- Product product (Entity) 
-------- Operation operation (Entity) 

转换器是一样的@BalusC是建议前:

@ManagedBean 
@RequestScoped 
public class OperationConverter implements Converter { 

    @EJB 
    private EaoOperation operationService; 

    @Override 
    public String getAsString(FacesContext context, UIComponent component, Object value) { 
     if (!(value instanceof Operation) || ((Operation) value).getId() == null) { 
      return null; 
     } 

     return String.valueOf(((Operation) value).getId()); 
    } 

    @Override 
    public Object getAsObject(FacesContext context, UIComponent component, String value) { 
     if (value == null || !value.matches("\\d+")) { 
      return null; 
     } 

     Operation operation = operationService.find(Integer.valueOf(value)); 
     System.out.println("Getting the operation value = " + operation.getOperation()); 

     if (operation == null) { 
      throw new ConverterException(new FacesMessage("Unknown operation ID: " + value)); 
     } 

     return operation; 
    } 

的日志中显示,其中选择检索的操作:

FINE: SELECT ID, OPERATION FROM operation WHERE (ID = ?) 
    bind => [1 parameter bound] 
INFO: Getting the operation value = exchange 

所以,当我尝试提交表单给出了如下错误:

form_add_product:operation: Validation error: the value is not valid 

这究竟是为什么?

回答

2

你试图传递一个复杂的对象作为HTTP请求参数,它可能只是一个String。 JSF/EL具有用于原语及其包装的内置转换器(例如int,Integer),甚至包含枚举。但对于所有其他类型,您确实需要编写一个自定义转换器。在这种情况下,您需要编写一个在StringOperation之间转换的转换器。然后使用String作为选项值(浏览器中的打开页面,右键单击和查看源文件并注意<option value>)。然后使用Operation作为模型值。 String应该唯一标识Operation对象。你可以使用这个操作ID。

但在这种特殊情况下,使用这种硬编码映射和相对简单的模型,我认为使用enum代替它更容易。

public enum Operation { 

    DONATION("Donation"), EXCHANGE("Exchange"); 

    private String label; 

    private Operation(String label) { 
     this.label = label; 
    } 

    public string getLabel() { 
     return label; 
    } 

} 

private Operation operation; // +getter +setter 

public Operation[] getOperations() { 
    return Operation.values(); 
} 

<h:selectOneMenu value="#{bean.operation}"> 
    <f:selectItems value="#{bean.operations}" var="operation" itemValue="#{operation}" itemLabel="#{operation.label}" /> 
</h:selectOneMenu> 

但是,如果这些值有实际上从数据库中检索和其大小undefinied,那么你还真需要一个定制转换器。您可以在getAsString()返回ID,并在getAsObject()中使用操作DAO/EJB按ID获取Operation

@ManagedBean 
@RequestScoped 
public class OperationConverter implements Converter { 

    @EJB 
    private OperationService operationService; 

    @Override 
    public String getAsString(FacesContext context, UIComponent component, Object value) { 
     // Convert here Operation object to String value for use in HTML. 
     if (!(value instanceof Operation) || ((Operation) value).getId() == null) { 
      return null; 
     } 

     return String.valueOf(((Operation) value).getId()); 
    } 

    @Override 
    public Object getAsObject(FacesContext context, UIComponent component, String value) { 
     // Convert here String submitted value to Operation object. 
     if (value == null || !value.matches("\\d+")) { 
      return null; 
     } 

     Operation operation = operationService.find(Long.valueOf(value)); 

     if (operation == null) { 
      throw new ConverterException(new FacesMessage("Unknown operation ID: " + value)); 
     } 

     return operation; 
    } 

} 

如下使用它:

<h:selectOneMenu ... converter="#{operationConverter}"> 

至于为什么它是一个@ManagedBean代替@FacesConverter,请阅读本:Converting and validating GET request parameters


更新作为对Validation Error: value not valid错误,这意味着Operation类的equals()方法损坏或丢失。在验证期间,JSF通过Object#equals()将提交的值与可用值列表进行比较。如果没有任何一个列表与提交的值匹配,那么您会看到这个错误。因此,请确保equals()已正确实施。以下是通过数据库技术标识进行比较的基本示例。

public boolean equals(Object other) { 
    return (other instanceof Operation) && (id != null) 
     ? id.equals(((Operation) other).id) 
     : (other == this); 
} 

不要忘记实现hashCode()还有:

public int hashCode() { 
    return (id != null) 
     ? (getClass().hashCode() + id.hashCode()) 
     : super.hashCode(); 
}