'null Converter'的转换错误设置值 - 为什么我需要JSF中的转换器?

ber*_*tie 38 jsf converter selectonemenu jsf-2

我有理解如何有效地使用JSF 2中的选择和POJO /实体.例如,我试图Warehouse通过下面的下拉列表选择一个实体:

<h:selectOneMenu value="#{bean.selectedWarehouse}">
    <f:selectItem itemLabel="Choose one .." itemValue="#{null}" />
    <f:selectItems value="#{bean.availableWarehouses}" />
</h:selectOneMenu>
Run Code Online (Sandbox Code Playgroud)

以下托管bean:

@Named
@ViewScoped
public class Bean {

    private Warehouse selectedWarehouse;
    private List<SelectItem> availableWarehouses;

    // ...

    @PostConstruct
    public void init() {
        // ...

        availableWarehouses = new ArrayList<>();

        for (Warehouse warehouse : warehouseService.listAll()) {
            availableWarehouses.add(new SelectItem(warehouse, warehouse.getName()));
        }
    }

    // ...
}
Run Code Online (Sandbox Code Playgroud)

请注意,我使用整个Warehouse实体作为值SelectItem.

当我提交表单时,会失败并显示以下消息:

'null Converter'的转换错误设置值'com.example.Warehouse@cafebabe'.

我希望Warehouse当我将它包装在一个中时,JSF可以将正确的对象设置为我的托管bean SelectItem.将我的实体包装在里面SelectItem意味着跳过Converter为我的实体创建一个.

Converter每当我想在我的实体中使用实体时,我真的必须使用<h:selectOneMenu>吗?JSF应该可以从可用项列表中提取所选项.如果我真的必须使用转换器,那么实际的方法是什么?到目前为止,我想到了这个:

  1. Converter为实体创建实现.
  2. 压倒了getAsString().我想我不需要这个,因为它的label属性SelectItem将用于显示下拉选项标签.
  3. 压倒了getAsObject().我认为这将用于返回正确的SelectItem或实体,具体取决于托管bean中定义的所选字段的类型.

getAsObject()混淆了我.有效的方法是什么?拥有字符串值,如何获取关联的实体对象?我应该根据字符串值从服务对象查询实体对象并返回实体吗?或者也许某种程度上我可以访问构成选择项的实体列表,循环它们以找到正确的实体,并返回实体?

这是什么常规方法?

Bal*_*usC 69

介绍

JSF生成HTML.HTML在Java方面基本上是一个大的String.要在HTML中表示Java对象,必须将它们转换为String.此外,提交HTML表单时,会将提交的值视为StringHTTP请求参数.在封面下,JSF从HttpServletRequest#getParameter()返回的内容中提取它们String.

要在非标准Java对象之间进行转换(即不是a String,Number或者BooleanEL具有内置转换,或者DateJSF提供内置<f:convertDateTime>标记),您必须提供自定义Converter.在SelectItem所有有没有特殊的目的.它只是JSF 1.x的遗留物,当它无法List<Warehouse>直接提供给它时<f:selectItems>.它对标签和转换也没有特殊处理.

符getAsString()

您需要以getAsString()这样的方式实现方法,即所需的Java对象以唯一的 String 表示形式表示,该表示可以用作HTTP请求参数.通常,此处使用技术ID(数据库主键).

public String getAsString(FacesContext context, UIComponent component, Object modelValue) {
    if (modelValue == null) {
        return "";
    }

    if (modelValue instanceof Warehouse) {
        return String.valueOf(((Warehouse) modelValue).getId());
    } else {
        throw new ConverterException(new FacesMessage(modelValue + " is not a valid Warehouse"));
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,在null/empty模型值的情况下返回空字符串是重要的并且是javadoc所必需的.另请参阅使用"请选择"f:在ap:selectOneMenu中使用null /空值的selectItem.

的getAsObject()

你需要实现getAsObject()以这样的方式,恰好String由返回的表示getAsString()可以转换回正好指定为同一个Java对象modelValuegetAsString().

public Object getAsObject(FacesContext context, UIComponent component, String submittedValue) {
    if (submittedValue == null || submittedValue.isEmpty()) {
        return null;
    }

    try {
        return warehouseService.find(Long.valueOf(submittedValue));
    } catch (NumberFormatException e) {
        throw new ConverterException(new FacesMessage(submittedValue + " is not a valid Warehouse ID"), e);
    }
}
Run Code Online (Sandbox Code Playgroud)

换句话说,您必须在技术上能够将返回的对象作为modelValue参数getAsString()传回,然后将获取的字符串作为无限循环中的submittedValue参数传回getAsObject().

用法

最后只是注释Converterwith @FacesConverter来挂钩有问题的对象类型,然后JSF会在Warehouse类型进入图片时自动处理转换:

@FacesConverter(forClass=Warehouse.class)
Run Code Online (Sandbox Code Playgroud)

那是"规范"的JSF方法.它毕竟不是非常有效,因为它确实也可以抓住该项目<f:selectItems>.但是最重​​要的一点Converter是它返回一个唯一的 String表示形式,因此Java对象可以通过一个简单的标识来识别,String适合在HTTP和HTML中传递.

基于toString()的通用转换器

JSF实用程序库OmniFaces有一个SelectItemsConverter基于toString()实体结果的工作.这样您就不再需要摆弄getAsObject()昂贵的业务/数据库操作了.有关具体使用示例,请参阅展示.

要使用它,只需将其注册如下:

<h:selectOneMenu ... converter="omnifaces.SelectItemsConverter">
Run Code Online (Sandbox Code Playgroud)

并确保toString()您的Warehouse实体返回实体的唯一表示形式.例如,您可以直接返回ID:

@Override
public String toString() {
    return String.valueOf(id);
}
Run Code Online (Sandbox Code Playgroud)

或者更具可读性/可重用性的东西:

@Override
public String toString() {
    return "Warehouse[id=" + id + "]";
}
Run Code Online (Sandbox Code Playgroud)

也可以看看:


此问题无关,因为JSF 2.0不再明确要求具有List<SelectItem>as <f:selectItem>值.只是一个List<Warehouse>也足够了.

<h:selectOneMenu value="#{bean.selectedWarehouse}">
    <f:selectItem itemLabel="Choose one .." itemValue="#{null}" />
    <f:selectItems value="#{bean.availableWarehouses}" var="warehouse"
        itemLabel="#{warehouse.name}" itemValue="#{warehouse}" />
</h:selectOneMenu>
Run Code Online (Sandbox Code Playgroud)
private Warehouse selectedWarehouse;
private List<Warehouse> availableWarehouses;
Run Code Online (Sandbox Code Playgroud)