如何在JSF 2中创建自定义转换器? [英] How create a custom converter in JSF 2?

查看:112
本文介绍了如何在JSF 2中创建自定义转换器?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个称为运营"的实体:

I have this Entity called '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实例,但是只是将其保留在本地,例如,可以吗?;))

I retrieve the operations this way: (It could be through an EJB instance but just to keep it local and as an example, okay? ;) )

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中获取所选的操作:

So I'm trying to get the selected operation in this selectOneMenu:

productc是具有viewScopeManagedBeanproductb是具有sessionScope的ManagedBean,其具有是我的实体product.该产品包含一个operation,因此是这样的:

The productc is a ManagedBean which has a viewScope, productb is a ManagedBean with a sessionScope which has a product which is my entity. The product contais one operation, so is something like this:

(字母 c 具有控制的含义,与我的实体产品相关的所有操作都应由该bean处理,好吗?)

(the letter c has the meaning of control, where all operations related about my entity product should be handled by this bean, okay?)

Product productc (ViewScope) 
-- ProductBean productb (SessionScope)
---- Product product (Entity)
-------- Operation operation (Entity)

转换器与之前建议使用@BalusC相同:

The converter is the same as @BalusC is suggest before:

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

如日志中所示检索哪个所选操作:

Which retrieve the operation selected as showed in the log:

FINE: SELECT ID, OPERATION FROM operation WHERE (ID = ?)
    bind => [1 parameter bound]
INFO: Getting the operation value = exchange

因此,当我尝试提交表单时,出现以下错误:

So when I try to submit the form gives the follow error:

form_add_product:operation: Validation error: the value is not valid

为什么会这样?

推荐答案

您正尝试传递一个复杂的对象作为HTTP请求参数,该参数只能是String. JSF/EL为基元及其包装器(例如intInteger)甚至枚举内置了转换器.但是对于所有其他类型,您确实需要编写一个自定义转换器.在这种情况下,您需要编写一个在StringOperation之间转换的转换器.然后,将String用作选项值(在浏览器中打开页面,右键单击并查看源文件并注意<option value>).然后,将Operation用作模型值. String应该唯一地标识Operation对象.您可以为此使用操作ID.

You're trying to pass a complex object around as HTTP request parameter which can be only be a String. JSF/EL has builtin converters for primitives and its wrappers (e.g. int, Integer) and even enums. But for all other types you really need to write a custom converter. In this case you need to write a converter which converts between String and Operation. The String is then used as option value (open page in browser, rightclick and View Source and notice the <option value>). The Operation is then used as model value. The String should uniquely identify the Operation object. You could use operation ID for this.

但是在这种特殊情况下,使用这样的硬编码映射和相对简单的模型,我认为使用enum会更容易.

But in this particular case, with such a hardcoded map and a relatively simple model, I think it's easier to use an enum instead.

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.

But if those values have actually to be retrieved from the DB and its size is undefinied, then you still really need a custom converter. You could in getAsString() return the ID and in getAsObject() use the operation DAO/EJB to get an Operation by the 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的原因,请阅读以下内容:

As to why it's a @ManagedBean instead of @FacesConverter, read this: Converting and validating GET request parameters.

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

Update as to the Validation Error: value not valid error, this means that the equals() method of the Operation class is broken or missing. During validation, JSF compares the submitted value with the list of available values by Object#equals(). If no one of the list matches with the submitted value, then you'll see this error. So, ensure that equals() is properly implemented. Here's a basic example which compares by the DB technical identity.

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

这篇关于如何在JSF 2中创建自定义转换器?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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