JSF在SelectOneMenu中的转换器问题

4
再来一次,我在这里遇到麻烦了。我的观点是:在我的项目中,我需要一个转换器,显然是将SelectOneMenu组件中的项转换为相应bean中的列表属性。在我的JSF页面中,我有:
<p:selectOneMenu id="ddlPublicType" value="#{publicBean.selectedPublicType}"    effect="fade" converter="#{publicBean.conversor}" > 
    <f:selectItems value="#{publicoBean.lstPublicTypes}" var="pt" itemLabel="#{pt.label}" itemValue="#{pt.value}"></f:selectItems>
</p:selectOneMenu>

"我的豆子是:"
@ManagedBean(name = "publicBean")
@RequestScoped
public class PublicBean {

// Campos
private String name; // Nome do evento
private TdPublicType selectedPublicType = null;
private List<SelectItem> lstPublicTypes = null;
private static PublicTypeDAO publicTypeDao; // DAO 

static {
    publicTypeDao = new PublicTypeDAO();
}
// Construtor

public PublicoBean() {

    lstPublicTypes = new ArrayList<SelectItem>();
    List<TdPublicType> lst = publicTypeDao.consultarTodos();
    ListIterator<TdPublicType> i = lst.listIterator();
    lst.add(new SelectItem("-1","Select..."));
    while (i.hasNext()) {
        TdPublicType actual = (TdPublicType) i.next();
        lstPublicTypes.add(new SelectItem(actual.getIdPublicType(), actual.getNamePublicType()));
    }

}

// Getters e Setters

...

public Converter getConversor() {
    return new Converter() {
        @Override
        public Object getAsObject(FacesContext context, UIComponent component, String value) {
            // This value parameter seems to be the value i had passed into SelectItem constructor
            TdPublicType publicType = null; // Retrieving the PublicType from Database based on ID in value parameter
            try {
                if (value.compareTo("-1") == 0 || value == null) {
                    return null;
                }
                publicType = publicTypeDao.findById(Integer.parseInt(value));
            } catch (Exception e) {
                FacesMessage msg = new FacesMessage("Error in data conversion.");
                msg.setSeverity(FacesMessage.SEVERITY_ERROR);
                FacesContext.getCurrentInstance().addMessage("info", msg);
            }
            return publicType;
        }

        @Override
        public String getAsString(FacesContext context, UIComponent component, Object value) {
            return value.toString(); // The value parameter is a TdPublicType object ?
        }
    };
}

...
}

在`getAsObject()`方法中,value参数似乎是我传递给SelectItem构造函数的值。但是在`getAsString()`方法中,value参数似乎也是一个ID的字符串表示形式。这个参数不应该是TdPublicType类型吗?我的代码有什么问题吗?
3个回答

4
getAsString()方法应该将Object(在您的情况下为TdPublicType类型)转换为String,以便能够唯一地标识实例,例如某个ID,以便可以内联到HTML代码中并作为HTTP请求参数传递。 getAsObject()方法应该将确切的唯一String表示形式转换回具体的Object实例,以便提交的HTTP请求参数可以转换回原始对象实例。

基本上来说(忽略了一些检查和异常处理):

@Override
public String getAsString(FacesContext context, UIComponent component, Object modelValue) throws ConverterException {
    // Convert Object to unique String representation for display.
    return String.valueOf(((TdPublicType) modelValue).getId());
}

@Override
public Object getAsObject(FacesContext context, UIComponent component, String submittedValue) throws ConverterException {
    // Convert submitted unique String representation back to Object.
    return tdPublicTypeService.find(Long.valueOf(submittedValue));
}

更新:您还有一个问题,即将TdPublicType类的value属性指定为项值,而不是TdPublicType实例本身。这样,转换器将检索value属性而不是TdPublicType实例,在getAsString()中。请相应地进行修正。
<f:selectItems value="#{publicoBean.lstPublicTypes}" var="pt" 
    itemLabel="#{pt.label}" itemValue="#{pt}"/>

我需要重载 TdPublicType 的 toString() 方法吗?在 getAsString() 方法内部,会生成一个异常,表示无法将字符串转换为 TdPublicType。 - mnatan.brito
那么模型值显然不是TdPublicType类型。实际上,您使用了itemValue =“#{pt.value}”而不是itemValue =“#{pt}”。请相应地进行修复。 - BalusC
他仍然将整数作为组件的后备模型返回,但我已经更改了itemValue属性为#{pt}。 - mnatan.brito
我找到了为什么 getAsString() 的值参数返回一个整数而不是 TdPublicType 对象的原因,我需要将 <f:selectItems /> 的 value 属性设置为 List<TdPublicType> 而不是 List<SelectItem>。 - mnatan.brito
是的,没错。List<SelectItem> 是旧的 JSF 1.x 的方式。请参见 http://stackoverflow.com/tags/selectonemenu/info。 - BalusC

0

您可以使用通用转换器将后端Bean中的值进行转换。您无需进行任何强制类型转换。

@FacesConverter(value = "GConverter")
public class GConverter implements Converter{

    private static Map<Object, String> entities = new WeakHashMap<Object, String>();

    @Override
    public String getAsString(FacesContext context, UIComponent component, Object entity) {
        synchronized (entities) {
            if (!entities.containsKey(entity)) {
                String uuid = UUID.randomUUID().toString();
                entities.put(entity, uuid);
                return uuid;
            } else {
                return entities.get(entity);
            }
        }
    }

    @Override
    public Object getAsObject(FacesContext context, UIComponent component, String uuid) {
        for (Entry<Object, String> entry : entities.entrySet()) {
            if (entry.getValue().equals(uuid)) {
                return entry.getKey();
            }
        }
        return null;
    }

}

示例用法如下:

<p:selectOneMenu id="ddlPublicType" value="#{publicBean.selectedPublicType}"    effect="fade" converter="GConverter" > 
    <f:selectItems value="#{publicoBean.lstPublicTypes}" var="pt" itemLabel="#{pt.label}" itemValue="#{pt}"></f:selectItems>
</p:selectOneMenu>

警告:此转换器内存效率低下,当使用第二级数据库缓存时可能会导致内存泄漏。不建议在生产环境中使用。另请参见https://dev59.com/zIrda4cB1Zd3GeqPQcDA。 - BalusC
到目前为止没有任何故障,我们已经使用这个转换器相当长的时间了。不过,感谢提供的信息。我们肯定也会寻找其他选项。 - Maverick

0

现在代码可以运行了。我的错误在于加载方法上。我之前是这样做的:

// Loading menu
List<TdPublicType> l = daoPublicType.retrieveAll();
Iterator<TdPublicType> i = l.iterator();
while (i.hasNext()) {
    TdPublicType actual = (TdPublicType) i.next();
    lstMenuPublicType.add(new SelectItem(actual.getIdtPublicType(), actual.getNamePublicType()));
}

但正确的方法是:

// Loading menu
List<TdPublicType> l = daoPublicType.retrieveAll();
Iterator<TdPublicType> i = l.iterator();
while (i.hasNext()) {
    TdPublicType actual = (TdPublicType) i.next();
    lstMenuPublicType.add(new SelectItem(actual, actual.getNamePublicType())); // In the first parameter i passed the PublicType object itself not his id.
} 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接