【问题标题】:Generic JSF entity converter [duplicate]通用 JSF 实体转换器 [重复]
【发布时间】:2011-05-15 04:02:01
【问题描述】:

我正在编写我的第一个 Java EE 6 Web 应用程序作为学习练习。我没有使用框架,只是 JPA 2.0、EJB 3.1 和 JSF 2.0。

我有一个自定义转换器将存储在 SelectOne 组件中的 JPA 实体转换回实体。我正在使用 InitialContext.lookup 获取对会话 Bean 的引用以查找相关实体。

我想创建一个通用实体转换器,因此我不必为每个实体创建一个转换器。我想我会创建一个抽象实体并让所有实体扩展它。然后为抽象实体创建一个自定义转换器,并将其用作所有实体的转换器。

这听起来合理和/或可行吗?

没有一个抽象实体,只是一个转换任何实体的转换器会更有意义吗?在那种情况下,我不确定如何获得对适当会话 Bean 的引用。

我已经包含了我当前的转换器,因为我不确定我是否以最有效的方式获得对我的会话 Bean 的引用。

package com.mycom.rentalstore.converters;

import com.mycom.rentalstore.ejbs.ClassificationEJB;
import com.mycom.rentalstore.entities.Classification;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.ConverterException;
import javax.faces.convert.FacesConverter;
import javax.naming.InitialContext;
import javax.naming.NamingException;

@FacesConverter(forClass = Classification.class)
public class ClassificationConverter implements Converter {

    private InitialContext ic;
    private ClassificationEJB classificationEJB;

    @Override
    public Object getAsObject(FacesContext context, UIComponent component, String value) {

        try {
            ic = new InitialContext();
            classificationEJB = (ClassificationEJB) ic.lookup("java:global/com.mycom.rentalstore_RentalStore_war_1.0-SNAPSHOT/ClassificationEJB");

        } catch (NamingException e) {
            throw new ConverterException(new FacesMessage(String.format("Cannot obtain InitialContext - %s", e)), e);
        }

        try {
            return classificationEJB.getClassificationById(Long.valueOf(value));
        } catch (Exception e) {
            throw new ConverterException(new FacesMessage(String.format("Cannot convert %s to Classification - %s", value, e)), e);
        }
    }

    @Override
    public String getAsString(FacesContext context, UIComponent component, Object value) {
        return String.valueOf(((Classification) value).getId());
    }
}

【问题讨论】:

  • 这是我的 allinone 类解决方案:stackoverflow.com/a/40069411/4158634
  • @BalusC 为什么这个问题被认为是“重复”,因为它早于(2010
  • 日期有多重要?此外,它于 2015 年关闭。
  • @BalusC SO 说 之前 有人问过这个问题,并且已经有了答案,这是不正确的。不要生气,我知道你是THE超级善良/乐于助人的 JSF 专家,我喜欢全脸和你为使 JSF 变得更好所做的一切 (</compliments>),但人们可能会认为,在 2 个问题中,您选择将没有接受您的答案的(较旧的)问题标记为重复,以宣传您的答案被接受的问题,这可能被人们视为不公平谁回答了最初的问题(尽管我认为您不是故意这样做的)。
  • 确实如此。我是 OP,这是最初的问题。它不应该被标记为重复。先问后答。

标签: java jsf jsf-2


【解决方案1】:

我正在使用 JSF 2.0 查看地图:

@FacesConverter("entityConverter")
public class EntityConverter implements Converter {

private static final String key = "com.example.jsf.EntityConverter";
private static final String empty = "";

private Map<String, Object> getViewMap(FacesContext context) {
    Map<String, Object> viewMap = context.getViewRoot().getViewMap();
    @SuppressWarnings({ "unchecked", "rawtypes" })
    Map<String, Object> idMap = (Map) viewMap.get(key);
    if (idMap == null) {
        idMap = new HashMap<String, Object>();
        viewMap.put(key, idMap);
    }
    return idMap;
}

@Override
public Object getAsObject(FacesContext context, UIComponent c, String value) {
    if (value.isEmpty()) {
        return null;
    }
    return getViewMap(context).get(value);
}

@Override
public String getAsString(FacesContext context, UIComponent c, Object value) {
    if (value == null) {
        return empty;
    }
    String id = ((Persistent) value).getId().toString();
    getViewMap(context).put(id, value);
    return id;
}
}

【讨论】:

  • 能否详细说明view map的作用和用途?
  • 视图被销毁后将有资格进行垃圾回收,因此无需使用弱哈希图。 (它是@ViewScoped,所以每个视图都会有自己的实体哈希映射)
【解决方案2】:

好吧,我今天遇到了同样的问题,我通过创建一个通用的 ConversionHelper 并在转换器中使用它来解决它。 为此,我有一个 EntityService,它是一个通用的 SLSB,我用它来为任何实体类型执行简单的 CRUD 操作。我的实体也实现了一个 PersistentEntity 接口,它有一个 getId 和 setId 方法,我用简单的主键来保存它们。就是这样。

最后我的转换器是这样的:

@FacesConverter(value = "userConverter", forClass = User.class) public class UserConverter implements Converter { @Override public Object getAsObject(FacesContext ctx, UIComponent component, java.lang.String value) { return ConversionHelper.getAsObject(User.class, value); } @Override public String getAsString(FacesContext ctx, UIComponent component, Object value) { return ConversionHelper.getAsString(value); } }

我的转换助手看起来像这样:

public final class ConversionHelper { private ConversionHelper() { } public static <T> T getAsObject(Class<T> returnType, String value) { if (returnType== null) { throw new NullPointerException("Trying to getAsObject with a null return type."); } if (value == null) { throw new NullPointerException("Trying to getAsObject with a null value."); } Long id = null; try { id = Long.parseLong(value); } catch (NumberFormatException e) { throw new ConverterException("Trying to getAsObject with a wrong id format."); } try { Context initialContext = new InitialContext(); EntityService entityService = (EntityService) initialContext.lookup("java:global/myapp/EntityService"); T result = (T) entityService.find(returnType, id); return result; } catch (NamingException e) { throw new ConverterException("EntityService not found."); } } public static String getAsString(Object value) { if (value instanceof PersistentEntity) { PersistentEntity result = (PersistentEntity) value; return String.valueOf(result.getId()); } return null; } }

现在为简单的 JPA 实体创建转换器只需复制一个转换器并更改 3 个参数。

这对我来说效果很好,但我不知道这是否是风格和性能方面的最佳方法。任何提示将不胜感激。

【讨论】:

  • 我的也很适合我。我喜欢我的 AbstractEntity,因为它允许我集中常见的样板代码,例如审计、PK、toString、hashCode 等。我还为简单的 CRUD 使用抽象会话 Bean,我在需要时覆盖它。非常感谢来自更有经验的编码人员关于 icwnd 和我的方法之间的性能的任何 cmet。
  • JSF 怎么知道,应该在名为 getAsObject 的 Converter-Method 中传递哪个值作为 String-param 调用值?
【解决方案3】:

我的解决方案如下:

@ManagedBean
@SessionScoped
public class EntityConverterBuilderBean {
    private static Logger logger = LoggerFactory.getLogger(EntityConverterBuilderBean.class);
    @EJB
    private GenericDao dao;

    public GenericConverter createConverter(String entityClass) {
        return new GenericConverter(entityClass, dao);
    }


}

public class GenericConverter implements Converter {

    private Class clazz;
    private GenericDao dao;

    public GenericConverter(String clazz, Generic dao) {
        try {
            this.clazz = Class.forName(clazz);
            this.dao = dao;
        } catch (Exception e) {
            logger.error("cannot get class: " + clazz, e);
            throw new RuntimeException(e);
        }
    }

    public Object getAsObject(javax.faces.context.FacesContext facesContext, javax.faces.component.UIComponent uiComponent, java.lang.String s) {
        Object ret = null;

        if (!"".equals(s)) {
            Long id = new Long(s);
            ret = dao.findById(clazz, id);
        }

        return ret;
    }

    public String getAsString(javax.faces.context.FacesContext facesContext, javax.faces.component.UIComponent uiComponent, java.lang.Object o) {
        if (o != null) {
            return ((SimpleEntity) o).getId() + "";
        } else {
            return "";
        }
    }
}

在页面中:

 <h:selectOneMenu id="x" value="#{controller.x}" 
      converter="#{entityConverterBuilderBean.createConverter('com.test.model.TestEntity')}">

【讨论】:

  • 不错的 sn-p,比 @Xavier 的类型更安全。
  • ¿为什么使用@SessionScoped?
【解决方案4】:

使用 Seam Faces,它提供了一个 Converter 类来做你想做的事。

org.jboss.seam.faces.conversion.Converter

虽然它是一个 JBoss 项目,但 Seam 3 可以与 Glassfish 3.1 及更高版本完美配合。

http://seamframework.org/Seam3/FacesModule

在 3.1 上,它确实有几个额外的依赖项;见http://blog.ringerc.id.au/2011/05/using-seam-3-with-glassfish-31.html

【讨论】:

    【解决方案5】:

    使用 Seam 3 中的 Seam Faces 试试这个。

    @Named("DocTypeConverter")
    public class DocumentTypeConverter implements Converter, Serializable {
    
        private static final long serialVersionUID = 1L;
    
        @Inject
        private DocumentTypeSessionEJB proDocTypeSb;
    
        @Override
        public Object getAsObject(FacesContext context, UIComponent component,
                String value) {
            DocumentType result = null;
    
            if (value != null && !value.trim().equals("")) {  
                try {  
                    result = (DocumentType) proDocTypeSb.findById(DocumentType.class, value);
                } catch(Exception exception) {  
                    throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Conversion Error", "Not a valid value"));  
                }  
            }  
    
            return result;
        }
    
        @Override
        public String getAsString(FacesContext context, UIComponent component,
                Object value) {
            String result = null;
            if (value != null && value instanceof DocumentType){
                DocumentType docType = (DocumentType) value;
                result = docType.getId();
            }
    
            return result;
        }
    
    }
    

    【讨论】:

    • 如果需要 SEAM,则不符合问题的原始规范(JPA 2.0、EJB 3.1、JSF 2.0)。仅仅为了这个目的而引入 Seam 实在是太荒谬了。
    • 我知道并且我同意。但它正在工作并且不需要整个 Seam 包。只是 Seam Faces 罐子。此外,使用 JNDI 查找 Session EJB 总是有经典的方法。
    【解决方案6】:

    (针对 JSF 2.3 更新)

    我正在使用这样的东西:

    @FacesConverter(value = "entityConverter", managed = true)
    public class EntityConverter implements Converter<Object> {
    
        @Inject
        private EntityManager entityManager;
    
        @Override
        public Object getAsObject(FacesContext context, UIComponent component, String value) {
            Class<?> entityType = component.getValueExpression("value").getType(context.getELContext());
            Class<?> idType = entityManager.getMetamodel().entity(entityType).getIdType().getJavaType();
            Converter idConverter = context.getApplication().createConverter(idType);
            Object id = idConverter.getAsObject(context, component, value);
            return entityManager.getReference(entityType, id);
        }
    
        @Override
        public String getAsString(FacesContext context, UIComponent component, Object value) {
            Object id = entityManager.getEntityManagerFactory().getPersistenceUnitUtil().getIdentifier(value);
            Converter idConverter = context.getApplication().createConverter(id.getClass());
            return idConverter.getAsString(context, component, id);
        }
    }
    

    在模板中,使用&lt;f:converter binding="#{entityConverter}" /&gt;

    【讨论】:

    • 感谢分享component.getValueExpression("value").getType(context.getELContext()) 对通用转换器非常有帮助。
    • 从 JSF 2.3 开始,@Named 现在可以替换为 @FacesConverter(managed = true)
    【解决方案7】:

    要完成克雷格·林格的回复,您可以使用Seam 3 FacesModule 的通用org.jboss.seam.faces.conversion.ObjectConverter

    您可以在这里获取代码:https://github.com/seam/faces/blob/develop/impl/src/main/java/org/jboss/seam/faces/conversion/ObjectConverter.java

    它使用 2 个HashMaps(一个反向使用)并将其对象存储在Conversation

    【讨论】:

      猜你喜欢
      • 2012-12-06
      • 2013-06-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-25
      • 2016-03-03
      • 2011-01-02
      • 1970-01-01
      相关资源
      最近更新 更多