通用JSF实体转换器

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

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

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

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

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

我已经包含了我当前的转换器,因为我不确定我是否以最有效的方式获得了对Session 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()); } } 

我今天遇到了同样的问题,我通过创建一个通用的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); } }
@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 getAsObject(Class 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; } }
public final class ConversionHelper { private ConversionHelper() { } public static  T getAsObject(Class 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个参数。

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

我正在使用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 getViewMap(FacesContext context) { Map viewMap = context.getViewRoot().getViewMap(); @SuppressWarnings({ "unchecked", "rawtypes" }) Map idMap = (Map) viewMap.get(key); if (idMap == null) { idMap = new HashMap(); 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; } } 

我的解决方案如下:

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

并在页面中:

   

使用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

使用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; } } 

我正在使用这样的东西:

 @Named public class EntityConverter implements Converter { @Inject private EntityManager entityManager; @Inject private ConversionService conversionService; @Override public Object getAsObject(FacesContext context, UIComponent component, String value) { Class entityType = component.getValueExpression("value").getType(context.getELContext()); Class idType = this.entityManager.getMetamodel().entity(entityType).getIdType().getJavaType(); Object id = this.conversionService.convert(idType, value); return this.entityManager.getReference(entityType, id); // find() is possible too } @Override public String getAsString(FacesContext context, UIComponent component, Object value) { Object id = this.entityManager.getEntityManagerFactory().getPersistenceUnitUtil().getIdentifier(value); return this.conversionService.convert(String.class, id); } } 

ConversionService定义如下(这里的实现超出了范围):

 public interface ConversionService {  T convert(Class targetType, Object source); } 

在模板中,使用

要完成Craig Ringer的响应,可以使用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 (一个被反向使用)并在Conversation存储它的对象。