【问题标题】:How create a custom converter in JSF 2?如何在 JSF 2 中创建自定义转换器?
【发布时间】:2011-12-21 12:18:31
【问题描述】:

我有一个名为“操作”的实体:

@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,这是我的实体。该产品包含一个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

为什么会这样?

【问题讨论】:

    标签: jsf jsf-2 converter


    【解决方案1】:

    您正在尝试将复杂对象作为 HTTP 请求参数传递,该参数只能是 String。 JSF/EL 具有用于原语及其包装器(例如intInteger)甚至枚举的内置转换器。但是对于所有其他类型,您确实需要编写自定义转换器。在这种情况下,您需要编写一个在StringOperation 之间转换的转换器。然后将String 用作选项值(在浏览器中打开页面,右键单击并查看源代码,注意&lt;option value&gt;)。然后将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>
    

    但如果这些值实际上要从数据库中检索,并且其大小未定义,那么您仍然需要一个自定义转换器。您可以在getAsString() 中返回ID,并在getAsObject() 中使用DAO/EJB 操作通过ID 获取Operation

    @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,请阅读:Converting and validating GET request parameters


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

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

    【讨论】:

      猜你喜欢
      • 2012-07-21
      • 2012-03-15
      • 1970-01-01
      • 2011-03-31
      • 1970-01-01
      • 2012-12-16
      • 1970-01-01
      • 2011-08-28
      • 1970-01-01
      相关资源
      最近更新 更多