通用 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);
在模板中,使用<f:converter binding="#entityConverter" />
。
【讨论】:
感谢分享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 个HashMap
s(一个反向使用)并将其对象存储在Conversation
。
【讨论】:
以上是关于通用 JSF 实体转换器 [重复]的主要内容,如果未能解决你的问题,请参考以下文章
spring jpa之实体属性类型转换器AttributeConverter,自定义Converter,通用Converter
使用支持 bean 和 jsf 的 Java EE 持久化实体 [重复]
使用JSF Converter时的延迟加载异常(指一个集合)