DEV Community

aristides villarreal
aristides villarreal

Posted on • Edited on

Optimizando Converter con Primefaces selectOneMenu y Autocomplete

Para optimizar los converter cuando utilizamos componentes como podemos recurrir a crear un services que contenga la lista de objetos a utilizar y hacemos la conversión buscando en esa lista en lugar de realizar una consulta al microservicios que devuelve el resultado, esto mejora el desempeño de la aplicación y reduce la cantidad de peticiones al microservicio.

Image description

Converter

El Converter lo usaremos para selectOneMenu y Autocomplete de Primefaces. Por lo tanto recibirá una lista de objetos delimitados por la cantidad máxima que se especifique en la variable de configuración en el archivo microprofile-config.properties denominada: #-- converter

converter.max.number.of.elements =25
Enter fullscreen mode Exit fullscreen mode

Como valor predeterminado serán 25 elementos. Para evitar cargar listas muy grandes.
Cuando se realiza una selección en un selectOneMenu o Autocomplete de un elemento que excede los 25 se buscara en el microservicio, en caso contrario se buscara en la lista almacenada en la memoria.

Image description

Crear el Services

@ViewScoped
@Named
public class TipoTarjetaConverterServices implements Serializable {

    @Inject
    TipotarjetaServices tipotarjetaServices;

    List<Tipotarjeta> tipotarjetas = new ArrayList<>();

    public List<Tipotarjeta> getTipotarjetas() {
        return tipotarjetas;
    }

    public void setTipotarjetas(List<Tipotarjeta> tipotarjetas) {
        this.tipotarjetas = tipotarjetas;
    }

    // <editor-fold defaultstate="collapsed" desc="Optional<Tipotarjeta> get(Long id)">
    public Optional<Tipotarjeta> get(Long id) {
        Optional<Tipotarjeta> result;

        try {

            result = tipotarjetas.stream().filter(x -> x.getIdtipotarjeta().equals(id)).findFirst();
            if (!result.isPresent()) {
                Tipotarjeta tipotarjeta = tipotarjetaServices.findByIdtipotarjeta(id);
                if (tipotarjeta != null) {
tipotarjetas.add(tipotarjeta);
                    return Optional.of(tipotarjeta);
                }
                result = Optional.empty();
            }
            return result;
        } catch (Exception e) {

            FacesUtil.errorMessage(FacesUtil.nameOfClassAndMethod() + " " + e.getLocalizedMessage());
        }
        return Optional.empty();

    }
// </editor-fold>
    // <editor-fold defaultstate="collapsed" desc="void add(List<Tipotarjeta> tipotarjetas)">

    public void add(List<Tipotarjeta> tipotarjetas) {
        try {
            destroyed();
            this.tipotarjetas = tipotarjetas;

        } catch (Exception e) {

            FacesUtil.errorMessage(FacesUtil.nameOfClassAndMethod() + " " + e.getLocalizedMessage());
        }

    }
// </editor-fold>

// <editor-fold defaultstate="collapsed" desc="void destroyed()">
    public void destroyed() {
        try {
            this.tipotarjetas = new ArrayList<>();
        } catch (Exception e) {

            FacesUtil.errorMessage(FacesUtil.nameOfClassAndMethod() + " " + e.getLocalizedMessage());
        }

    }
// </editor-fold>

}

Enter fullscreen mode Exit fullscreen mode

Crear el Converter

@Named
@FacesConverter(forClass = Tipotarjeta.class, managed = true)
public class TipotarjetaConverter implements Converter<Tipotarjeta> {

    @Inject
    TipoTarjetaConverterServices tipoTarjetaConverterServices;

    // <editor-fold defaultstate="collapsed" desc="String getAsString(FacesContext fc, UIComponent uic, Tipotarjeta t)">
    @Override
    public String getAsString(FacesContext fc, UIComponent uic, Tipotarjeta t) {
        try {
            if (t == null) {
                return "";
           }
            if (t.getIdtipotarjeta() != null) {
                return t.getIdtipotarjeta().toString();
            }
        } catch (Exception e) {
            new FacesMessage("Error en converter  " + e.getLocalizedMessage());
        }
        return "";
    }
    // </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Tipotarjeta getAsObject(FacesContext fc, UIComponent uic, String submittedValue)">

    @Override
    public Tipotarjeta getAsObject(FacesContext fc, UIComponent uic, String submittedValue) {
        Tipotarjeta result = new Tipotarjeta();
        if (submittedValue == null || submittedValue.isEmpty()) {
            return null;
        }

        try {
            Integer id = Integer.parseInt(submittedValue);
            Long idTipotarjeta = id.longValue();
            Optional<Tipotarjeta> optional = tipoTarjetaConverterServices.get(idTipotarjeta);
            if (optional.isPresent()) {
                result = optional.get();
            }
            return result;
        } catch (Exception e) {

            System.out.println("====================");
            System.out.println(FacesUtil.nameOfClassAndMethod() + " " + e.getLocalizedMessage());
            ConsoleUtil.test("\t " + FacesUtil.nameOfClassAndMethod() + " submittedValue " + submittedValue);
            System.out.println("====================");
            throw new ConverterException(new FacesMessage(submittedValue + " is not a valid selecction from Converter"), e);
        }
    }
// </editor-fold>
}


Enter fullscreen mode Exit fullscreen mode

SelectOneMenu

Para el selectOneMenu se pasa la lista completa al services.

En la clase Faces

Necesita agregar en su clase

    // # Converter
     @Inject
    private Config config;

     @Inject
    @ConfigProperty(name = "converter.max.number.of.elements")
    private Provider<Integer> converterMaxNumberOfElements;

Enter fullscreen mode Exit fullscreen mode
@Inject
TipoTarjetaConverterServices tipoTarjetaConverterServices;
/**
 * invoca al método para cargar la lista en el ConverterServices
 */
    @PostConstruct
    public void init() {
        try {
 loadTipotarjetaByProyecto(proyectoSelected);

 } catch (Exception e) {
            FacesUtil.errorMessage(FacesUtil.nameOfClassAndMethod() + " " + e.getLocalizedMessage());
        }

}

/**
Carga la lista de tipotarjeta en el ConverterServices
*/
private void loadTipotarjetaByProyecto(Proyecto proyecto) {
        try {
          tipoTarjetaConverterServices.add(tipotarjetaServices.loadTipotarjetaByProyecto(proyecto))
            );

        } catch (Exception e) {
            FacesUtil.errorMessage(FacesUtil.nameOfClassAndMethod() + " " + e.getLocalizedMessage());
        }
    }
/**

/**
 * Limpia la lista
 */
@PreDestroy
public void preDestroy() {
  tipoTarjetaConverterServices.destroyed();
}

Enter fullscreen mode Exit fullscreen mode

En las paginas xhtml


 <p:selectOneMenu  value="#{tableroFaces.tarjetaSelected.tipotarjeta}" 
var="item">

<f:selectItems value="#{tipoTarjetaConverterServices.tipotarjetas}" 
 var="item" 
 itemLabel="#{item.tipotarjeta}"                                                    itemValue="#{item}"

 />
<p:column>
<i class="#{item.tipotarjeta}" /> #{item.tipotarjeta} 
</p:column>


</p:selectOneMenu>

Enter fullscreen mode Exit fullscreen mode

Image description

Autocomplete

Pasamos el máximo de registros permitidos que se configuraron en el archivo microprofile-config.properties. Mediante el método:

calcularConverterMaxNumberOfElements(result.size(),converterMaxNumberOfElements.get())
Enter fullscreen mode Exit fullscreen mode
  // <editor-fold defaultstate="collapsed" desc="List<Tipotarjeta> completeTipotarjeta(String query)">
    public List<Tipotarjeta> completeTipotarjeta(String query) {

        List<Tipotarjeta> result = new ArrayList<>();
        try {
            query = query.trim();
            result = tipotarjetaServices.likeByTipotarjeta(query);

            tipoTarjetaConverterServices.add(result.subList(0,                     
                    calcularConverterMaxNumberOfElements(result.size(),converterMaxNumberOfElements.get()))
            );
        } catch (Exception e) {

            FacesUtil.errorMessage(FacesUtil.nameOfClassAndMethod() + " " + e.getLocalizedMessage());
        }

        return result;
    }


Enter fullscreen mode Exit fullscreen mode

En la pagina xhtml


 <p:autoComplete id="tipotarjeta" 
  multiple="false"
  value="#{tipoTarjetaFaces.tipotarjetaSelectedAutocomplete}"

  completeMethod="#{tipoTarjetaFaces.completeTipotarjeta}"

  var="tipotarjeta"
  itemLabel="#{tipotarjeta.tipotarjeta}" 

  itemValue="#{tipotarjeta}"

   forceSelection="true"

  title="#{msg['autocomplete.minimo3caracteres']}"

  dropdown="true"

  minQueryLength="3"

  scrollHeight="250"

  converter="#{tipotarjetaConverter}"
>

<p:column>
    <h:outputText style="vertical-align: middle; margin-left: .5rem" value="#{tipotarjeta.tipotarjeta} - #{tipotarjeta.grupoTipoTarjeta.grupoTipoTarjeta}"/>
</p:column>

<p:ajax event="itemSelect"  
  listener="#{tipoTarjetaFaces.autocompleteSelectedEvent}"  
  update=":form:growl, tipotarjeta, dataTable" />  

<p:ajax event="itemUnselect" 
  process="@this" 
  listener="#{tipoTarjetaFaces.autocompleteUnselectListener}" 

 update=":form:growl, tipotarjeta, dataTable"
 />
 </p:autoComplete>


Enter fullscreen mode Exit fullscreen mode

Image of Timescale

🚀 pgai Vectorizer: SQLAlchemy and LiteLLM Make Vector Search Simple

We built pgai Vectorizer to simplify embedding management for AI applications—without needing a separate database or complex infrastructure. Since launch, developers have created over 3,000 vectorizers on Timescale Cloud, with many more self-hosted.

Read more

Top comments (0)

Image of Timescale

🚀 pgai Vectorizer: SQLAlchemy and LiteLLM Make Vector Search Simple

We built pgai Vectorizer to simplify embedding management for AI applications—without needing a separate database or complex infrastructure. Since launch, developers have created over 3,000 vectorizers on Timescale Cloud, with many more self-hosted.

Read more

AWS GenAI Live!

GenAI LIVE! is a dynamic live-streamed show exploring how AWS and our partners are helping organizations unlock real value with generative AI.

Tune in to the full event

DEV is partnering to bring live events to the community. Join us or dismiss this billboard if you're not interested. ❤️