如何在JSF 2中创建自定义转换器?

时间:2011-11-03 20:00:18

标签: jsf jsf-2 converter

我有一个名为'Operation'的实体:

@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

中获取所选操作

    

productcManagedBeanviewScopeproductb是ManagedBean,其中sessionScope的{​​{1}}是product我的实体。该产品包含一个operation,因此是这样的:

(字母 c 具有控制的含义,其中与我的实体产品相关的所有操作都应由此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

为什么会这样?

1 个答案:

答案 0 :(得分:3)

您尝试将复杂对象作为HTTP请求参数传递,该参数只能是String。 JSF / EL为基元及其包装器(例如intInteger)甚至枚举构建了内置转换器。但对于所有其他类型,您真的需要编写自定义转换器。在这种情况下,您需要编写一个在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>

但是,如果这些值实际要从数据库中检索并且其大小未确定,那么您仍然需要自定义转换器。您可以在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错误的

更新,这意味着equals()类的Operation方法已损坏或丢失。在验证期间,JSF将提交的值与Object#equals()的可用值列表进行比较。如果列表中没有一个与提交的值匹配,那么您将看到此错误。因此,请确保equals()已正确实施。这是一个基于DB技术标识进行比较的基本示例。

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