为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

慕尼黑5688855 2019-12-04 11:10:53
我正在使用Spring和Hibernate进行JSF项目,其中除其他外,还有许多Converter遵循相同模式的s:getAsObject 接收对象id的字符串表示形式,将其转换为数字,并获取给定种类和给定id的实体getAsString 接收和实体,并返回转换为的对象的ID String该代码实质上是以下代码(省略了检查):@ManagedBean(name="myConverter")@SessionScopedpublic 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")@SessionScopedpublic class MyFooConverter implements MyGenericConverter<Foo> {    /* ... */}在编写本文时,我意识到也许并不是真的需要泛型,所以也许我可以简单地编写具有这两种方法的实现的基类,并根据需要编写子类。有一些非琐碎的细节需要处理(例如,我必须以MyService某种方式抽象类的事实),所以我的第一个问题是:值得为之烦恼吗?如果是这样,还有其他方法吗?
查看完整描述

2 回答

?
阿晨1998

TA贡献2037条经验 获得超6个赞

最简单的方法是让您的所有JPA实体都从这样的基本实体扩展:


public abstract class BaseEntity<T extends Number> implements Serializable {


    private static final long serialVersionUID = 1L;


    public abstract T getId();


    public abstract void setId(T id);


    @Override

    public int hashCode() {

        return (getId() != null) 

            ? (getClass().getSimpleName().hashCode() + getId().hashCode())

            : super.hashCode();

    }


    @Override

    public boolean equals(Object other) {

        return (other != null && getId() != null

                && other.getClass().isAssignableFrom(getClass()) 

                && getClass().isAssignableFrom(other.getClass())) 

            ? getId().equals(((BaseEntity<?>) other).getId())

            : (other == this);

    }


    @Override

    public String toString() {

        return String.format("%s[id=%d]", getClass().getSimpleName(), getId());

    }


}

请注意,拥有一个适当的equals()(和hashCode())很重要,否则您将面临“ 验证错误:值无效”。该Class#isAssignableFrom()测试是避免如Hibernate基于代理失败的测试,而不需要回落到休眠特定Hibernate#getClass(Object)的辅助方法。


并拥有这样的基础服务(是的,我忽略了您使用Spring的事实;这只是给出基本思想):


@Stateless

public class BaseService {


    @PersistenceContext

    private EntityManager em;


    public BaseEntity<? extends Number> find(Class<BaseEntity<? extends Number>> type, Number id) {

        return em.find(type, id);

    }


}

并实现转换器如下:


@ManagedBean

@ApplicationScoped

@SuppressWarnings({ "rawtypes", "unchecked" }) // We don't care about BaseEntity's actual type here.

public class BaseEntityConverter implements Converter {


    @EJB

    private BaseService baseService;


    @Override

    public String getAsString(FacesContext context, UIComponent component, Object value) {

        if (value == null) {

            return "";

        }


        if (modelValue instanceof BaseEntity) {

            Number id = ((BaseEntity) modelValue).getId();

            return (id != null) ? id.toString() : null;

        } else {

            throw new ConverterException(new FacesMessage(String.format("%s is not a valid User", modelValue)), e);

        }

    }


    @Override

    public Object getAsObject(FacesContext context, UIComponent component, String value) {

        if (value == null || value.isEmpty()) {

            return null;

        }


        try {

            Class<?> type = component.getValueExpression("value").getType(context.getELContext());

            return baseService.find((Class<BaseEntity<? extends Number>>) type, Long.valueOf(submittedValue));

        } catch (NumberFormatException e) {

            throw new ConverterException(new FacesMessage(String.format("%s is not a valid ID of BaseEntity", submittedValue)), e);

        }

    }


}

请注意,它被注册为@ManagedBean而不是@FacesConverter。这个技巧可以让您通过例如在转换器中注入服务@EJB。另请参见如何在@FacesConverter中注入@ EJB,@ PersistenceContext,@ Inject,@ Autowired等?因此,您需要引用converter="#{baseEntityConverter}"而不是converter="baseEntityConverter"。


如果您碰巧将此类转换器更多地用于UISelectOne/ UISelectMany组件(<h:selectOneMenu>和朋友),您可能会发现OmniFaces SelectItemsConverter更加有用。它基于可用的值进行转换,<f:selectItems>而不是每次都进行(可能很昂贵)DB调用。


查看完整回答
反对 回复 2019-12-04
  • 2 回答
  • 0 关注
  • 2911 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信