我在autocomplete
输入元素中显示了建议项列表。为此,我需要实施converter
来将entity<entityName, entityId>
转换为entityName
&amp;反之亦然。然而,在实现的时候,我意识到我必须读取数据库超过1次才能找到所选entityName的相应entityId(而getAsObject()
),我想知道为什么不将它存储在某个客户端,以便<{1}}被选中时可以传递entityId
。
有什么方法可以避免这种额外的阅读吗?
答案 0 :(得分:6)
这确实是“按设计”,也许在JSF规范中有点疏忽。理论上,你可以通过从UIComponent
参数中提取项目并相反地对它们进行比较来完全避免它。然而,它有点工作。我的同事Arjan Tijms撰写了一篇关于此的博客:Automatic to-Object conversion in JSF selectOneMenu & Co.
这是相关的摘录;以下是您需要扩展的基本转换器:
public abstract class SelectItemsBaseConverter implements Converter {
@Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
return SelectItemsUtils.findValueByStringConversion(context, component, value, this);
}
}
这是SelectItemsUtils
类,部分复制自Mojarra的来源:
public final class SelectItemsUtils {
private SelectItemsUtils() {}
public static Object findValueByStringConversion(FacesContext context, UIComponent component, String value, Converter converter) {
return findValueByStringConversion(context, component, new SelectItemsIterator(context, component), value, converter);
}
private static Object findValueByStringConversion(FacesContext context, UIComponent component, Iterator<SelectItem> items, String value, Converter converter) {
while (items.hasNext()) {
SelectItem item = items.next();
if (item instanceof SelectItemGroup) {
SelectItem subitems[] = ((SelectItemGroup) item).getSelectItems();
if (!isEmpty(subitems)) {
Object object = findValueByStringConversion(context, component, new ArrayIterator(subitems), value, converter);
if (object != null) {
return object;
}
}
} else if (!item.isNoSelectionOption() && value.equals(converter.getAsString(context, component, item.getValue()))) {
return item.getValue();
}
}
return null;
}
public static boolean isEmpty(Object[] array) {
return array == null || array.length == 0;
}
/**
* This class is based on Mojarra version
*/
static class ArrayIterator implements Iterator<SelectItem> {
public ArrayIterator(SelectItem items[]) {
this.items = items;
}
private SelectItem items[];
private int index = 0;
public boolean hasNext() {
return (index < items.length);
}
public SelectItem next() {
try {
return (items[index++]);
}
catch (IndexOutOfBoundsException e) {
throw new NoSuchElementException();
}
}
public void remove() {
throw new UnsupportedOperationException();
}
}
}
以下是如何将它用于您自己的转换器,您只需要实现getAsString()
(getAsObject()
已经处理完毕):
@FacesConverter("someEntitySelectItemsConverter")
public class SomeEntitySelectItemsConverter extends SelectItemsBaseConverter {
@Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
return ((SomeEntity) value).getId().toString();
}
}
更新上述概念已在JSF实用程序库OmniFaces中以下列转换器的风格结束:
SelectItemsConverter
- 基于<f:selectItem(s)>
的{{1}}。SelectItemsIndexConverter
- 基于项目索引的Object#toString()
。ListConverter
- 例如<f:selectItem(s)>
基于<p:autoComplete>
ListIndexConverter
- 例如Object#toString()
基于项目索引。答案 1 :(得分:0)
我发现这样做以便转换器不需要访问数据库的唯一方法是使转换器成为托管bean,以便它可以访问其他一些存储AutoComplete建议值列表的bean成分
这样的事情:
@ManagedBean
@RequestScoped
public class EntityConverter implements Converter
{
@ManagedProperty(value = "#{autoCompleteBean}")
private AutoCompleteBean autoCompleteBean;
public void setAutoCompleteBean(AutoCompleteBean autoCompleteBean)
{
this.autoCompleteBean = autoCompleteBean;
}
@Override
public Object getAsObject(FacesContext context, UIComponent component,
String value)
{
final List<Entity> entities = autoCompleteBean.getCachedSuggestions();
for (final Enity entity : entities)
{
if (entity.getIdAsString().equals(value))
{
return entity;
}
}
throw new IllegalStateException("Entity was not found!");
}
@Override
public String getAsString(FacesContext context, UIComponent component,
Object value)
{ ... }
在jsf页面中,确保将转换器作为bean引用。即:
<p:autoComplete value="#{autoCompleteBean.selectedEntity}"
completeMethod="#{autoCompleteBean.getSuggestions}" var="theEntity"
itemValue="#{theEntity}" itemLabel=#{theEntity.someValue}
converter="#{entityConverter}">