Wie eine benutzerdefinierte Konverter schreiben
-
26-09-2019 - |
Frage
Wie kann ich schreiben einen benutzerdefinierten Konverter, wenn sie mit PrimeFaces Komponenten arbeiten, die eine Liste von POJO verwenden? Mein besonderes Problem ist mit <p:pickList>
<p:pickList converter="????" value="#{bean.projects}" var="project"
itemLabel="#{project.name}" itemValue="#{project}">
Ohne Konverter I java.lang.ClassCastException
bekommen, weil JSF die vorgelegten Werte mit nicht umgesetzten java.lang.String
vorgelegten Werte setzt.
Lösung 2
Nach der Forschung auf, wie benutzerdefinierte Konverter zu schreiben, hier ist die Lösung.
1. eine Java-Klasse, die javax.faces.convert.Converter;
public class ProjectConverter implements Converter{
@EJB
DocumentSBean sBean;
public ProjectConverter(){
}
public Object getAsObject(FacesContext context, UIComponent component, String value){
return sBean.getProjectById(value);
//If u look below, I convert the object into a unique string, which is its id.
//Therefore, I just need to write a method that query the object back from the
//database if given a id. getProjectById, is a method inside my Session Bean that
//does what I just described
}
public String getAsString(FacesContext context, UIComponent component, Object value)
{
return ((Project) value).getId().toString(); //--> convert to a unique string.
}
}
2. Registrieren Sie Ihr benutzerdefinierten Konverter in faces-config.xml
<converter>
<converter-id>projectConverter</converter-id>
<converter-class>org.xdrawing.converter.ProjectConverter</converter-class>
</converter>
3. So, jetzt innerhalb Primefaces Komponente, u tun nur converter="projectConverter"
. Beachten Sie, dass projectConverter
ist die <convert-id>
ich gerade erstellt haben. Also mein Problem zu lösen oben, ich dies tun:
<p:pickList converter="projectConverter" value="#{bean.projects}" var="project"
itemLabel="#{project.name}" itemValue="#{project}">
Andere Tipps
Es ist möglich, whithout anderer Datenbankzugriff, aber ich weiß nicht, die beste Art und Weise. Ich benutze einen sehr spezieller Konverter, funktioniert nur für Auswahlliste. Versuchen Sie folgendes:
@FacesConverter(value = "primeFacesPickListConverter")public class PrimeFacesPickListConverter implements Converter {
@Override
public Object getAsObject(FacesContext arg0, UIComponent arg1, String arg2) {
Object ret = null;
if (arg1 instanceof PickList) {
Object dualList = ((PickList) arg1).getValue();
DualListModel dl = (DualListModel) dualList;
for (Object o : dl.getSource()) {
String id = "" + ((Project) o).getId();
if (arg2.equals(id)) {
ret = o;
break;
}
}
if (ret == null)
for (Object o : dl.getTarget()) {
String id = "" + ((Project) o).getId();
if (arg2.equals(id)) {
ret = o;
break;
}
}
}
return ret;
}
@Override
public String getAsString(FacesContext arg0, UIComponent arg1, Object arg2) {
String str = "";
if (arg2 instanceof Project) {
str = "" + ((Project) arg2).getId();
}
return str;
}
und Auswahlliste:
<p:pickList converter="primeFacesPickListConverter" value="#{bean.projects}" var="project"
itemLabel="#{project.name}" itemValue="#{project}">
Arbeit für mich, Verbesserungen notwendig.
Ja, können Sie einen Konverter schreiben, dass serialisiert / deserialisiert die Objekte in der Auswahlliste wie folgt aus:
@FacesConverter(value="PositionMetricConverter")
public class PositionMetricConverter implements Converter {
private static final Logger log = Logger.getLogger(PositionMetricConverter.class.getName());
@Override
public Object getAsObject(FacesContext facesContext, UIComponent uiComponent, String value) {
try {
byte[] data = Base64.decodeBase64(value);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data));
Object o = ois.readObject();
ois.close();
return o;
} catch (Exception e) {
log.log(Level.SEVERE, "Unable to decode PositionMetric!", e);
return null;
}
}
@Override
public String getAsString(FacesContext facesContext, UIComponent uiComponent, Object value) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(value);
oos.close();
return Base64.encodeBase64String(baos.toByteArray());
} catch (IOException e) {
log.log(Level.SEVERE, "Unable to encode PositionMetric!", e);
return "";
}
}
}
Dann bewerben Sie diesen Konverter auf Ihrer Auswahlliste wie folgt aus:
<p:pickList converter="PositionMetricConverter" value="#{bean.positionMetrics}"
var="positionMetric" itemLabel="#{positionMetric.name}" itemValue="#{positionMetric}"/>
und stellen Sie sicher, dass Ihre Objekte sind serialisierbar.
Dieses Problem ist nicht primefaces verwandt, nur allgemeine JSF Zusammenhang stehen.
Warum sollten Sie die Datenbank wieder treffen? Ihre Bohne enthält bereits die Liste der Objekte, die Sie in einer Komponente angezeigt werden sollen, oder ist es scoped anfordern?
- Erstellen Sie eine übergeordnete Klasse für Ihre Hibernate Pojo ist ein ID-Feld enthält. Wenn Sie nicht über eine Superklasse erstellen möchten nur die pojo Klasse verwenden, aber Sie mehr Konverter-Klassen benötigen.
- Mit dieser übergeordneten Klasse eine Liste von Pojo der einen generischen Konverter für alle Pojo Klassen erstellen können, in den Konstruktor übergeben enthält.
- Fügen Sie den Konverter als eine Eigenschaft in Ihrer Session Bean und verwenden Sie diese Wandler in Ihrer JSF-Komponente.
Wenn Sie die endgültige Liste über eine get-Eigenschaft in Ihrem Bean oder die verschachtelten eine im Konverter zuzugreifen, ist Ihre Wahl.
public class SuperPojo
{
protected Integer id;
//constructor & getter
}
public class PojoTest extends SuperPojo
{
private String label = null;
//constructor & getter
}
public class SuperPojoConverter<T extends SuperPojo> implements Converter
{
private Collection<T> superPojos;
public IdEntityConverter(Collection<T> superPojos)
{
this.superPojos = superPojos;
}
@Override
public Object getAsObject(FacesContext context, UIComponent component, String value)
{
//catch exceptions and empty or null value!
final int intValue = Integer.parseInt(value);
for(SuperPojo superPojo : this.superPojos)
if(superPojo.getId().intValue() == intValue)
return superPojo;
return null;
}
@Override
public String getAsString(FacesContext context, UIComponent component, Object value)
{
//catch null and instanceof
return String.valueOf(((SuperPojo)value).getId().intValue());
}
public Collection<T> getSuperPojos()
{
return this.superPojos;
}
}
public class Bean
{
private SuperPojoConverter<PojoTest> pojoTestConverter = null;
public Bean()
{
final List<PojoTest> pojoTests = //get list from hibernate
this.pojoTestConverter = new SuperPojoConverter<PojoTest>(pojoTests);
}
public SuperPojoConverter<PojoTest> getPojoTestConverter()
{
return this.pojoTestConverter;
}
}
<h:selectOneMenu value="#{selPojoTest}" converter="#{bean.getPojoTestConverter}">
<f:selectItems value="#{bean.getPojoTestConverter.getSuperPojos}" var="varPojoTest" itemLabel="#{varPojoTest.label}" itemValue="#{varPojoTest}"/>
</h:selectOneMenu>
Gibt es eine Möglichkeit, dass Treffer ohne 2-Datenbank zu implementieren?
Ich meine, wenn Sie
haben#{bean.projects}
Dies ist ein Datenbank-Hit.
und wenn die Wandler puts
sBean.getProjectById(value);
ist ein unnötiger Datenbank-Hit, da bean.projects bereits id und den Wert der Objekte
Ja, es ist möglich:
public class DocumentSBean sBean implements Serializable{
private List<Document> projects;
// projects methods...
// ...
public Converter getDocumentConverter(){
return docConverter;
}
private Converter docConverter = new Converter() {
@Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
return projects.stream().filter(p -> p.getName().equals(value)).findFirst().orElse(null);
}
@Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
return (value != null)
? ((Document) value).toString()
: null;
}
};
}
<p:pickList converter="#{sBean.documentConverter}" value="#...