我有一个名为inputPeriod的自定义JSF输入组件,用于输入日期周期。每个时期都有来自和迄今。使用Javascript实现组件的功能,Javascript生成JSON字符串并将其提交给组件。然后,输入组件使用默认转换器,该转换器将JSON句点转换为Period对象列表,并在我的托管bean上设置它们。这一切都很完美。
我遇到的问题的根源是,现在我想在EJB实体中使用相同的组件。我有一个Banner实体,与BannerPeriod实体有一对多的关系。 BannerPeriod实体的每个实例都采用from(开始)和(结束)日期,就像我在输入组件中使用的现有Period对象一样。我为此实现了一个新的转换器:
@ManagedBean
@RequestScoped
public class BannerPeriodConverter implements Converter {
@Override
public Object getAsObject(FacesContext fc, UIComponent uic, String str) {
if (str != null) {
Date from = null, to = null;
try {
JSONObject period = new JSONObject(str);
if (period.has("from")) {
from = new Date(period.getLong("from"));
}
if (period.has("to")) {
to = new Date(period.getLong("to"));
}
} catch (JSONException ex) {
throw new ConverterException(ex);
}
BannerPeriod bp = new BannerPeriod();
bp.setBegins(from);
bp.setEnds(to);
return bp;
}
return null;
}
@Override
public String getAsString(FacesContext fc, UIComponent uic, Object o) {
if (o != null && o instanceof BannerPeriod) {
BannerPeriod bp = (BannerPeriod) o;
JSONObject period = new JSONObject();
try {
period.put("from", bp.getBegins() != null ? bp.getBegins().getTime() : (Object) null);
period.put("to", bp.getEnds() != null ? bp.getEnds().getTime() : (Object) null);
} catch (JSONException ex) {
throw new ConverterException(ex);
}
return period.toString();
}
return "";
}
}
转换器可以正常使用该组件。我遇到的问题是,当我使用现有横幅广告编辑横幅时,实体会丢失其主键。因此,当我提交表单而不是更新现有句点时,我要么获得重复的异常,要么再次创建现有的句点,从而在数据库中生成实际的副本。
所以我的问题是,我该怎么办才能避免这种情况?我的猜测是输入组件需要将主键保留在现有实体上,但我怎样才能最好地制作类似的东西呢?目前,输入组件与实体和我的EJB项目完全分离。输入组件甚至位于其自己的JSF项目中,而上面的转换器位于EJB项目中。默认情况下,输入组件使用普通的Period对象,该对象根本没有主键。它应该继续这样做。
或许这应该以其他方式解决?
答案 0 :(得分:1)
在getAsObject()
中,您正在创建一个完全不受管理的BannerPeriod
实例,而不是通过JPA直接从数据库中获取。{/ p>
BannerPeriod bp = new BannerPeriod();
bp.setBegins(from);
bp.setEnds(to);
return bp;
坚持使用它当然会在DB中创建一个新条目,因为它不受JPA管理。
基本上,您应该通过JPA从DB获取实例:
@EJB
private BannerPeriodService service;
public Object getAsObject(FacesContext context, UIComponent component, String value) {
// ...
return service.find(from, to);
}
其中BannerPeriodService#find()
通过EntityManager
获得所需的实例。
但这种做法非常笨拙。对于来自数据库的实体,规范方法是使用它们的技术/自然标识符,例如自动生成的主键。
E.g。 (null / instanceof检查等省略):
@EJB
private BannerPeriodService service;
public Object getAsString(FacesContext context, UIComponent component, Object value) {
Long id = ((BannerPeriod) value).getId();
return id.toString();
}
public Object getAsObject(FacesContext context, UIComponent component, String value) {
Long id = Long.valueOf(value);
return service.find(id);
}
无需搞乱JSON格式。如果实际上需要它们以JSON格式出于某些不明原因,那么通过使用JSF转换器就会出错方向。
据我所知,在转换器中使用DB是一项相对昂贵的工作。在这种情况下,OmniFaces SelectItemsConverter
可能正是您所寻找的。 p>