How create a custom converter in JSF 2?

2019-05-12 15:04发布

我有这样的实体,称为“操作”:

@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是具有一个ManagedBean sessionScope其中有一个product是我的实体。 该产品contais一个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

这究竟是为什么?

Answer 1:

你试图通过一个复杂的对象周围作为只能是一个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>

但是,如果这些值实际上已经从数据库中检索和其大小undefinied,那么你还是真的需要自定义转换。 你可以在getAsString()返回的ID和getAsObject()使用操作DAO / EJB得到一个Operation由ID。

@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 ,请阅读本: 转换和验证GET请求参数 。


作为更新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();
}


文章来源: How create a custom converter in JSF 2?