通用 JSF 实体转换器 [重复]

Posted

技术标签:

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

【讨论】:

以上是关于通用 JSF 实体转换器 [重复]的主要内容,如果未能解决你的问题,请参考以下文章

spring jpa之实体属性类型转换器AttributeConverter,自定义Converter,通用Converter

从不同核心数据实体获取数据并将结果转换为相应类的通用方法

使用支持 bean 和 jsf 的 Java EE 持久化实体 [重复]

使用JSF Converter时的延迟加载异常(指一个集合)

使用 JavaScript (RegExp) 转换 HTML 实体 [重复]

当 JSF 2 的视图中存在元数据转换器时,为啥 JSTL-JSF2 不能正常工作