最も簡単なのは、次のようにすべての 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()
適切な(and hashCode()
)を持つことが重要であることに注意してください。そうしないと、 Validation Error: Value is not valid が発生します。テストは、Hibernate 固有のヘルパー メソッド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
。このトリックを使用すると、eg を介してコンバーターにサービスを挿入できます@EJB
。@FacesConverter で @EJB、@PersistenceContext、@Inject、@Autowired などを注入する方法も参照してください。converter="#{baseEntityConverter}"
したがって、の代わりにとして参照する必要がありますconverter="baseEntityConverter"
。
このようなコンバーターをUISelectOne
/UISelectMany
コンポーネント (<h:selectOneMenu>
およびその友人) に頻繁に使用する場合は、 OmniFaces SelectItemsConverter
がはるかに便利であることに気付くかもしれません。<f:selectItems>
毎回 (潜在的に高価な) DB 呼び出しを行う代わりに、で利用可能な値に基づいて変換します。