Java 通用 JSF 实体转换器

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4268179/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 15:12:22  来源:igfitidea点击:

Generic JSF entity converter

javajsfjsf-2

提问by retrodev

I'm writing my first Java EE 6 web app as a learning exercise. I'm not using a framework, just JPA 2.0, EJB 3.1 and JSF 2.0.

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

I have a Custom Converter to convert a JPA Entity stored in a SelectOne component back to an Entity. I'm using an InitialContext.lookup to obtain a reference to a Session Bean to find the relevant Entity.

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

I'd like to create a generic Entity Converter so I don't have to create a converter per Entity. I thought I'd create an Abstract Entity and have all Entities extend it. Then create a Custom Converter for the Abstract Entity and use it as the converter for all Entities.

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

Does that sound sensible and/or practicable?

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

Would it make more sense not to have an abstract entity, just a converter that converts any entity? In that case I'm not sure how I'd obtain a reference to the appropriate Session Bean.

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

I've included my current converter because I'm not sure I'm obtaining a reference to my Session Bean in the most efficient manner.

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

采纳答案by rbento

Well I had the same problem today, and I solved it by creating a generic ConversionHelper and using it in the converter. For this purpose I have an EntityService which is a generic SLSB that I use to perform simple CRUD operations for any entity type. Also my entities implement a PersistentEntity interface, which has a getId and setId methods and I keep them with simple primary keys. That's it.

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

In the end my converter looks like this:

最后我的转换器看起来像这样:


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

And my conversion helper looks like this:

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


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

Now creating converters for simple JPA entities is a matter of duplicate a converter and change 3 parameters.

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

This is working well for me, but I don't know if it is the best approach in terms of style and performance. Any tips would be appreciated.

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

回答by Ecmel Ercan

I am using JSF 2.0 view map:

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

回答by Craig Ringer

Use Seam Faces, it provides a Converter class that does what you want.

使用 Seam Faces,它提供了一个可以执行您想要的操作的 Converter 类。

org.jboss.seam.faces.conversion.Converter

org.jboss.seam.faces.conversion.Converter

While it's a JBoss project, Seam 3 works fine with Glassfish 3.1 and newer.

虽然它是一个 JBoss 项目,但 Seam 3 可以与 Glassfish 3.1 和更新版本配合使用。

http://seamframework.org/Seam3/FacesModule

http://seamframework.org/Seam3/FacesModule

On 3.1 it does have a couple of additional dependencies; see http://blog.ringerc.id.au/2011/05/using-seam-3-with-glassfish-31.html

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

回答by Anthony O.

To complete the response of Craig Ringer, you can use the generic org.jboss.seam.faces.conversion.ObjectConverterof the Seam 3 FacesModule.

要完成克雷格·林格的响应,你可以使用通用org.jboss.seam.faces.conversion.ObjectConverter的中缝3 FacesModule

You can grab the code here : https://github.com/seam/faces/blob/develop/impl/src/main/java/org/jboss/seam/faces/conversion/ObjectConverter.java

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

It uses 2 HashMaps (one is used reversly) and stocks its objects in the Conversation.

它使用 2HashMap秒(反向使用 1秒)并将其对象存储在Conversation.

回答by ichalos

Try this using Seam Facesfrom Seam 3.

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

}

回答by alexandrubarbat

my solution is the following :

我的解决方案如下:

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

and in pages:

并在页面中:

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

回答by Xavier Dury

(UPDATED FOR JSF 2.3)

(针对 JSF 2.3 更新)

I am using something like this:

我正在使用这样的东西:

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

In template, use <f:converter binding="#{entityConverter}" />.

在模板中,使用<f:converter binding="#{entityConverter}" />.