使用Java泛型为实体实现转换器

我正在使用Spring和Hibernate进行JSF项目,其中除其他外,还有许多Converter遵循相同模式的s:


getAsObject 接收对象id的字符串表示形式,将其转换为数字,并获取给定种类和给定id的实体


getAsString 接收和实体,并返回转换为的对象的ID String


该代码实质上是以下代码(省略了检查):


@ManagedBean(name="myConverter")

@SessionScoped

public class MyConverter implements Converter {

    private MyService myService;


    /* ... */

    @Override

    public Object getAsObject(FacesContext facesContext, UIComponent uiComponent, String value) {

        int id = Integer.parseInt(value);

        return myService.getById(id);

    }


    @Override

    public String getAsString(FacesContext facesContext, UIComponent uiComponent, Object value) {

        return ((MyEntity)value).getId().toString();

    }

}

鉴于有大量Converter完全像这样的s(当然MyService和类型除外MyEntity),我想知道是否值得使用单个通用转换器。泛型本身的实现并不困难,但是我不确定声明Bean的正确方法。


可能的解决方案如下:


1-编写通用实现,我们称之为MyGenericConverter,没有任何Bean批注


2-将特定的转换器广告编写为的子类,MyGenericConverter<T>并根据需要对其进行注释:


@ManagedBean(name="myFooConverter")

@SessionScoped

public class MyFooConverter implements MyGenericConverter<Foo> {

    /* ... */

}

在编写本文时,我意识到也许并不是真的需要泛型,所以也许我可以简单地编写具有这两种方法的实现的基类,并根据需要编写子类。


有一些非琐碎的细节需要处理(例如,我必须以MyService某种方式抽象类的事实),所以我的第一个问题是:值得为之烦恼吗?


如果是这样,还有其他方法吗?


慕尼黑5688855
浏览 2928回答 2
2回答

阿晨1998

最简单的方法是让您的所有JPA实体都从这样的基本实体扩展:public abstract class BaseEntity<T extends Number> implements Serializable {&nbsp; &nbsp; private static final long serialVersionUID = 1L;&nbsp; &nbsp; public abstract T getId();&nbsp; &nbsp; public abstract void setId(T id);&nbsp; &nbsp; @Override&nbsp; &nbsp; public int hashCode() {&nbsp; &nbsp; &nbsp; &nbsp; return (getId() != null)&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ? (getClass().getSimpleName().hashCode() + getId().hashCode())&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; : super.hashCode();&nbsp; &nbsp; }&nbsp; &nbsp; @Override&nbsp; &nbsp; public boolean equals(Object other) {&nbsp; &nbsp; &nbsp; &nbsp; return (other != null && getId() != null&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; && other.getClass().isAssignableFrom(getClass())&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; && getClass().isAssignableFrom(other.getClass()))&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ? getId().equals(((BaseEntity<?>) other).getId())&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; : (other == this);&nbsp; &nbsp; }&nbsp; &nbsp; @Override&nbsp; &nbsp; public String toString() {&nbsp; &nbsp; &nbsp; &nbsp; return String.format("%s[id=%d]", getClass().getSimpleName(), getId());&nbsp; &nbsp; }}请注意,拥有一个适当的equals()(和hashCode())很重要,否则您将面临“ 验证错误:值无效”。该Class#isAssignableFrom()测试是避免如Hibernate基于代理失败的测试,而不需要回落到休眠特定Hibernate#getClass(Object)的辅助方法。并拥有这样的基础服务(是的,我忽略了您使用Spring的事实;这只是给出基本思想):@Statelesspublic class BaseService {&nbsp; &nbsp; @PersistenceContext&nbsp; &nbsp; private EntityManager em;&nbsp; &nbsp; public BaseEntity<? extends Number> find(Class<BaseEntity<? extends Number>> type, Number id) {&nbsp; &nbsp; &nbsp; &nbsp; return em.find(type, id);&nbsp; &nbsp; }}并实现转换器如下:@ManagedBean@ApplicationScoped@SuppressWarnings({ "rawtypes", "unchecked" }) // We don't care about BaseEntity's actual type here.public class BaseEntityConverter implements Converter {&nbsp; &nbsp; @EJB&nbsp; &nbsp; private BaseService baseService;&nbsp; &nbsp; @Override&nbsp; &nbsp; public String getAsString(FacesContext context, UIComponent component, Object value) {&nbsp; &nbsp; &nbsp; &nbsp; if (value == null) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return "";&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; if (modelValue instanceof BaseEntity) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Number id = ((BaseEntity) modelValue).getId();&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return (id != null) ? id.toString() : null;&nbsp; &nbsp; &nbsp; &nbsp; } else {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; throw new ConverterException(new FacesMessage(String.format("%s is not a valid User", modelValue)), e);&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }&nbsp; &nbsp; @Override&nbsp; &nbsp; public Object getAsObject(FacesContext context, UIComponent component, String value) {&nbsp; &nbsp; &nbsp; &nbsp; if (value == null || value.isEmpty()) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return null;&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; try {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Class<?> type = component.getValueExpression("value").getType(context.getELContext());&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return baseService.find((Class<BaseEntity<? extends Number>>) type, Long.valueOf(submittedValue));&nbsp; &nbsp; &nbsp; &nbsp; } catch (NumberFormatException e) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; throw new ConverterException(new FacesMessage(String.format("%s is not a valid ID of BaseEntity", submittedValue)), e);&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }}请注意,它被注册为@ManagedBean而不是@FacesConverter。这个技巧可以让您通过例如在转换器中注入服务@EJB。另请参见如何在@FacesConverter中注入@ EJB,@ PersistenceContext,@ Inject,@ Autowired等?因此,您需要引用converter="#{baseEntityConverter}"而不是converter="baseEntityConverter"。如果您碰巧将此类转换器更多地用于UISelectOne/ UISelectMany组件(<h:selectOneMenu>和朋友),您可能会发现OmniFaces SelectItemsConverter更加有用。它基于可用的值进行转换,<f:selectItems>而不是每次都进行(可能很昂贵)DB调用。
打开App,查看更多内容
随时随地看视频慕课网APP