كيفية كتابة محول مخصص لـ
-
26-09-2019 - |
سؤال
كيف يمكنني كتابة محول مخصص عند العمل مع مكونات PrimeFaces التي تستخدم قائمة Pojo؟ مشكلتي الخاصة هي مع <p:pickList>
<p:pickList converter="????" value="#{bean.projects}" var="project"
itemLabel="#{project.name}" itemValue="#{project}">
بدون محول أحصل عليه java.lang.ClassCastException
لأن JSF يحدد القيم المقدمة مع غير محول java.lang.String
القيم المقدمة.
المحلول 2
بعد البحث حول كيفية كتابة المحول المخصص ، إليك الحل.
1. قم بإنشاء فئة Java تنفذ 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. قم بتسجيل محولك المخصص في faces-config.xml
<converter>
<converter-id>projectConverter</converter-id>
<converter-class>org.xdrawing.converter.ProjectConverter</converter-class>
</converter>
3. حتى الآن داخل مكون Primefaces ، أنت فقط تفعل converter="projectConverter"
. لاحظ أن projectConverter
هل <convert-id>
لقد خلقت للتو. حتى لحل مشكلتي أعلاه ، أفعل هذا:
<p:pickList converter="projectConverter" value="#{bean.projects}" var="project"
itemLabel="#{project.name}" itemValue="#{project}">
نصائح أخرى
من الممكن ، مع الوصول إلى قاعدة البيانات الأخرى ، لكنني لا أعرف أفضل طريقة. أستخدم محولًا محددًا للغاية ، ويعمل فقط في قائمة الاختيار. جرب هذا:
@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;
}
وقائمة الاختيار:
<p:pickList converter="primeFacesPickListConverter" value="#{bean.projects}" var="project"
itemLabel="#{project.name}" itemValue="#{project}">
العمل بالنسبة لي ، التحسينات ضرورية.
نعم ، يمكنك كتابة محول يقوم بتسلسل/تخلص من الكائنات في قائمة الاختيار مثل هذا:
@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 "";
}
}
}
ثم قم بتطبيق هذا المحول على قائمة الاختيار مثل هذا:
<p:pickList converter="PositionMetricConverter" value="#{bean.positionMetrics}"
var="positionMetric" itemLabel="#{positionMetric.name}" itemValue="#{positionMetric}"/>
وتأكد من أن الأشياء الخاصة بك قابلة للتسلسل.
هذه المشكلة ليست ذات صلة PrimeFaces ، فقط General JSF ذات الصلة.
لماذا يجب أن تضغط على قاعدة البيانات مرة أخرى؟ تحتوي الفاصوليا بالفعل على قائمة الكائنات التي تريد عرضها في مكون ، أم أنها تطلب تحديد النطاق؟
- قم بإنشاء فئة فائقة لاتحادات السبات التي تحتوي على حقل هوية. إذا كنت لا ترغب في إنشاء فئة فائقة ، فما عليك سوى استخدام فئة PoJo ، ولكنك تحتاج إلى المزيد من فئات المحول.
- مع هذه الفئة الفائقة ، يمكنك إنشاء محول عام لجميع فئات PoJO التي تحتوي على قائمة من Pojo التي تم تمريرها في المنشئ.
- أضف المحول كخاصية في حبة الجلسة واستخدم هذا المحول في مكون JSF الخاص بك.
إذا قمت بالوصول إلى القائمة النهائية عبر خاصية GET في الفاصوليا أو التداخل في المحول ، فهو من اختيارك.
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>
هل هناك أي طريقة لتنفيذ ذلك بدون ضربات قاعدة بيانات 2؟
أعني ، عندما يكون لديك
#{bean.projects}
هذه قاعدة بيانات.
وعندما يضع المحول
sBean.getProjectById(value);
هي قاعدة بيانات غير ضرورية ، لأن Bean.projects لديها بالفعل معرف وقيمة الكائنات
انه من الممكن:
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="#...