“空转换器"的转换错误设置值 - 为什么我需要在 JSF 中使用转换器? [英] Conversion Error setting value for 'null Converter' - Why do I need a Converter in JSF?

查看:33
本文介绍了“空转换器"的转换错误设置值 - 为什么我需要在 JSF 中使用转换器?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在理解如何有效地使用带有 POJO/entity 的 JSF 2 中的选择时遇到问题.例如,我试图通过下面的下拉菜单选择一个 Warehouse 实体:

I have problems understanding how to use selection in JSF 2 with POJO/entity effectively. For example, I'm trying to select a Warehouse entity via the below dropdown:

<h:selectOneMenu value="#{bean.selectedWarehouse}">
    <f:selectItem itemLabel="Choose one .." itemValue="#{null}" />
    <f:selectItems value="#{bean.availableWarehouses}" />
</h:selectOneMenu>

以及下面的托管 bean:

And the below managed 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()));
        }
    }

    // ...
}

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

Notice that I use the whole Warehouse entity as the value of SelectItem.

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

When I submit the form, this fails with the following faces message:

空转换器"的转换错误设置值com.example.Warehouse@cafebabe".

Conversion Error setting value 'com.example.Warehouse@cafebabe' for 'null Converter'.

我希望 JSF 可以在我将其包装在 SelectItem 中时将正确的 Warehouse 对象设置为我的托管 bean.将我的实体包装在 SelectItem 中是为了跳过为我的实体创建 Converter.

I was hoping that JSF could just set the correct Warehouse object to my managed bean when I wrap it in a SelectItem. Wrapping my entity inside the SelectItem was meant to skip creating a Converter for my entity.

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

Do I really have to use a Converter whenever I want to make use of entities in my <h:selectOneMenu>? It should for JSF be possible to just extract the selected item from the list of available items. If I really have to use a converter, what is the practical way of doing it? So far I came up to this:

  1. 为实体创建一个 Converter 实现.
  2. 覆盖getAsString().我想我不需要这个,因为 SelectItem 的 label 属性将用于显示下拉选项标签.
  3. 覆盖getAsObject().我认为这将用于返回正确的 SelectItem 或实体,具体取决于托管 bean 中定义的所选字段的类型.
  1. Create a Converter implementation for the entity.
  2. Overriding the getAsString(). I think I don't need this since the label property of the SelectItem will be used to display the dropdown option label.
  3. Overriding the getAsObject(). I think this will be used to return the correct SelectItem or entity depending on the type of the selected field defined in the managed bean.

getAsObject() 让我很困惑.这样做的有效方法是什么?拥有字符串值,如何获取关联的实体对象?我应该根据字符串值从服务对象中查询实体对象并返回实体吗?或者也许我可以以某种方式访问​​形成选择项的实体列表,循环它们以找到正确的实体,然后返回实体?

The getAsObject() confuses me. What is the efficient way to do this? Having the string value, how do I get the associated entity object? Should I query the entity object from the service object based on the string value and return the entity? Or perhaps somehow I can access the list of the entities that form the selection items, loop them to find the correct entity, and return the entity?

一般的做法是什么?

推荐答案

简介

JSF 生成 HTML.HTML 在 Java 术语中基本上是一个大的 String.要在 HTML 中表示 Java 对象,必须将它们转换为 String.此外,当提交 HTML 表单时,提交的值在 HTTP 请求参数中被视为 String.在幕后,JSF 从 HttpServletRequest#getParameter() 返回 String.

Introduction

JSF generates HTML. HTML is in Java terms basically one large String. To represent Java objects in HTML, they have to be converted to String. Also, when a HTML form is submitted, the submitted values are treated as String in the HTTP request parameters. Under the covers, JSF extracts them from the HttpServletRequest#getParameter() which returns String.

在非标准 Java 对象之间进行转换(即不是 StringNumberBoolean,EL 已为其内置转换,或Date/LocalDate/ZonedDateTime JSF 为其提供了内置的 标签),你真的必须提供一个自定义的 转换器.SelectItem 根本没有特殊用途.它只是 JSF 1.x 的一个遗留物,当它无法提供时,例如List 直接到.它也没有对标签和转换进行特殊处理.

To convert between a non-standard Java object (i.e. not a String, Number or Boolean for which EL has builtin conversions, or Date/LocalDate/ZonedDateTime for which JSF provides builtin <f:convertDateTime> tag), you really have to supply a custom Converter. The SelectItem has no special purpose at all. It's just a leftover from JSF 1.x when it wasn't possible to supply e.g. List<Warehouse> directly to <f:selectItems>. It has also no special treatment as to labels and conversion.

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

You need to implement getAsString() method in such way that the desired Java object is been represented in an unique String representation which can be used as HTTP request parameter. Normally, the technical ID (the database primary key) is used here.

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

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

请注意,如果模型值为空/空,则返回空字符串很重要,并且 javadoc:

Note that returning an empty string in case of a null/empty model value is significant and required by the javadoc:

返回:一个长度为零的字符串,如果值为空,否则为转换结果

Returns: a zero-length String if value is null, otherwise the result of the conversion

否则生成的 将没有 value 属性,默认情况下将项目标签发送回 getAsObject().另请参阅 使用请选择"f:selectItem 在 p:selectOneMenu 中具有 null/空值.

Otherwise the generated <option> will not have a value attribute and by default send the item label back into getAsObject(). See also Using a "Please select" f:selectItem with null/empty value inside a p:selectOneMenu.

您需要实现 getAsObject() 这样正是 String<getAsString() 返回的/code> 表示可以转换回完全,在 getAsString().

You need to implement getAsObject() in such way that exactly that String representation as returned by getAsString() can be converted back to exactly the same Java object specified as modelValue in getAsString().

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

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

In other words, you must be technically able to pass back the returned object as modelValue argument of getAsString() and then pass back the obtained string as submittedValue argument of getAsObject() in an infinite loop.

最后只用 @FacesConverter 挂钩有问题的对象类型,JSF 将在 Warehouse 类型出现时自动处理转换:

Finally just annotate the Converter with @FacesConverter to hook on the object type in question, JSF will then automatically take care of conversion when Warehouse type ever comes into the picture:

@FacesConverter(forClass=Warehouse.class)

那是规范的"JSF 方法.毕竟它不是很有效,因为它确实也可以从 中抓取项目.但是Converter 最重要的一点是它返回一个唯一 String 表示,这样Java 对象就可以通过一个简单的String 适合在 HTTP 和 HTML 中传递.

That was the "canonical" JSF approach. It's after all not very effective as it could indeed also just have grabbed the item from the <f:selectItems>. But the most important point of a Converter is that it returns an unique String representation, so that the Java object could be identified by a simple String suitable for passing around in HTTP and HTML.

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

JSF utility library OmniFaces has a SelectItemsConverter which works based on toString() outcome of the entity. This way you do not need to fiddle with getAsObject() and expensive business/database operations anymore. For some concrete use examples, see also the showcase.

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

To use it, just register it as below:

<h:selectOneMenu ... converter="omnifaces.SelectItemsConverter">

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

And make sure that the toString() of your Warehouse entity returns an unique representation of the entity. You could for instance directly return the ID:

@Override
public String toString() {
    return String.valueOf(id);
}

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

Or something more readable/reusable:

@Override
public String toString() {
    return "Warehouse[id=" + id + "]";
}

另见:

  • 如何填充选项h:从数据库中选择一个菜单?
  • 通用 JSF 实体转换器 - 这样您就不会需要为每个实体编写一个转换器.
  • 在 JSF 选择项中使用枚举 - 枚举需要区别对待
  • 如何注入@EJB、@PersistenceContext、@FacesConverter 中的@Inject、@Autowired 等?
  • See also:

    • How to populate options of h:selectOneMenu from database?
    • Generic JSF entity converter - so that you don't need to write a converter for every entity.
    • Using enums in JSF selectitems - enums needs to be treated a bit differently
    • How to inject @EJB, @PersistenceContext, @Inject, @Autowired, etc in @FacesConverter?
    • 与问题无关,因为 JSF 2.0 不再明确要求将 List 作为 > 价值.一个 List 也足够了.

      Unrelated to the problem, since JSF 2.0 it's not explicitly required anymore to have a List<SelectItem> as <f:selectItem> value. Just a List<Warehouse> would also suffice.

      <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>
      

      private Warehouse selectedWarehouse;
      private List<Warehouse> availableWarehouses;
      

      这篇关于“空转换器"的转换错误设置值 - 为什么我需要在 JSF 中使用转换器?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆