2013-06-27 3 views
6

मैं जो अन्य बातों के अलावा Converter रों है कि एक ही पैटर्न का पालन की एक संख्या है वसंत और हाइबरनेट साथ JSF परियोजना पर काम कर रहा हूँ:जावा जेनेरिक्स के साथ संस्थाओं के लिए कन्वर्टर्स लागू

  • getAsObject की स्ट्रिंग प्रतिनिधित्व प्राप्त करता है वस्तु आईडी, यह एक संख्या में बदलता है, और दिए गए प्रकार की इकाई और दी गई id

  • getAsString प्राप्त करता है और इकाई को लाने और वस्तु String

  • करने के लिए परिवर्तित की आईडी रिटर्न

कोड अनिवार्य रूप से क्या इस प्रकार (चेक छोड़े गए) है:

@ManagedBean(name="myConverter") 
@SessionScoped 
public class MyConverter implements Converter { 
    private MyService myService; 

    /* ... */ 
    @Override 
    public Object getAsObject(FacesContext facesContext, UIComponent uiComponent, String value) { 
     int id = Integer.parseInt(value); 
     return myService.getById(id); 
    } 

    @Override 
    public String getAsString(FacesContext facesContext, UIComponent uiComponent, Object value) { 
     return ((MyEntity)value).getId().toString(); 
    } 
} 

को देखते हुए Converter कि (MyService और निश्चित रूप से MyEntity के प्रकार के लिए छोड़कर) वास्तव में इस तरह कर रहे हैं की एक बड़ी संख्या है, मैं था यह सोचकर कि क्या यह एक सामान्य जेनरेटर कनवर्टर का उपयोग करने लायक था। जेनेरिक का खुद का कार्यान्वयन मुश्किल नहीं है, लेकिन मुझे बीन्स घोषित करने के सही दृष्टिकोण के बारे में निश्चित नहीं है। - सामान्य कार्यान्वयन लिखें, चलो यह MyGenericConverter कॉल, किसी भी बीन एनोटेशन

2 बिना - विशिष्ट कनवर्टर विज्ञापन MyGenericConverter<T> का एक उपवर्ग लिखें और के रूप में उस पर टिप्पणी

1:

सम्भावित समाधान निम्नलिखित है की जरूरत:

@ManagedBean(name="myFooConverter") 
@SessionScoped 
public class MyFooConverter implements MyGenericConverter<Foo> { 
    /* ... */ 
} 

इस लेखन जबकि मुझे एहसास हुआ कि शायद एक जेनेरिक वास्तव में जरूरत नहीं है, तो शायद मैं बस एक आधार वर्ग दो विधियों के कार्यान्वयन के साथ लिख सकता है, एक जरूरत के रूप में डी subclass।

वहां कुछ गैर-मामूली विवरण हैं जिनकी देखभाल की जानी है (जैसे तथ्य यह है कि मुझे MyService कक्षा को किसी तरह से सार करना होगा) तो मेरा पहला सवाल यह है: क्या यह परेशानी का लायक है?

और यदि हां, तो क्या अन्य दृष्टिकोण हैं?

उत्तर

15

सबसे आसान अपने सभी जेपीए संस्थाओं को इस तरह एक आधार इकाई से विस्तार करने देने के लिए होगा:

public abstract class BaseEntity<T extends Number> implements Serializable { 

    private static final long serialVersionUID = 1L; 

    public abstract T getId(); 

    public abstract void setId(T id); 

    @Override 
    public int hashCode() { 
     return (getId() != null) 
      ? (getClass().getSimpleName().hashCode() + getId().hashCode()) 
      : super.hashCode(); 
    } 

    @Override 
    public boolean equals(Object other) { 
     return (other != null && getId() != null 
       && other.getClass().isAssignableFrom(getClass()) 
       && getClass().isAssignableFrom(other.getClass())) 
      ? getId().equals(((BaseEntity<?>) other).getId()) 
      : (other == this); 
    } 

    @Override 
    public String toString() { 
     return String.format("%s[id=%d]", getClass().getSimpleName(), getId()); 
    } 

} 

ध्यान दें कि यह एक उचित equals() (और hashCode()), अन्यथा आप Validation Error: Value is not valid का सामना करना पड़ेगा होना महत्वपूर्ण है। Class#isAssignableFrom() परीक्षण उदासीन परीक्षणों से बचने के लिए हैं उदा। हाइबरनेट आधारित प्रॉक्सी को बिना हाइबरनेट-विशिष्ट Hibernate#getClass(Object) सहायक विधि पर वापस आने की आवश्यकता के बिना।

और इस तरह एक आधार सेवा है (हाँ, मैं तथ्य यह है कि आप वसंत का उपयोग कर रहे अनदेखी कर रहा हूँ, यह सिर्फ बेस विचार दे करने के लिए):

@Stateless 
public class BaseService { 

    @PersistenceContext 
    private EntityManager em; 

    public BaseEntity<? extends Number> find(Class<BaseEntity<? extends Number>> type, Number id) { 
     return em.find(type, id); 
    } 

} 

और इस प्रकार कनवर्टर लागू:

@ManagedBean 
@ApplicationScoped 
@SuppressWarnings({ "rawtypes", "unchecked" }) // We don't care about BaseEntity's actual type here. 
public class BaseEntityConverter implements Converter { 

    @EJB 
    private BaseService baseService; 

    @Override 
    public String getAsString(FacesContext context, UIComponent component, Object value) { 
     if (value == null) { 
      return ""; 
     } 

     if (modelValue instanceof BaseEntity) { 
      Number id = ((BaseEntity) modelValue).getId(); 
      return (id != null) ? id.toString() : null; 
     } else { 
      throw new ConverterException(new FacesMessage(String.format("%s is not a valid User", modelValue)), e); 
     } 
    } 

    @Override 
    public Object getAsObject(FacesContext context, UIComponent component, String value) { 
     if (value == null || value.isEmpty()) { 
      return null; 
     } 

     try { 
      Class<?> type = component.getValueExpression("value").getType(context.getELContext()); 
      return baseService.find((Class<BaseEntity<? extends Number>>) type, Long.valueOf(submittedValue)); 
     } catch (NumberFormatException e) { 
      throw new ConverterException(new FacesMessage(String.format("%s is not a valid ID of BaseEntity", submittedValue)), e); 
     } 
    } 

} 

ध्यान दें कि यह एक @ManagedBean बजाय एक @FacesConverter के रूप में पंजीकृत है। यह चाल आपको कनवर्टर में एक सेवा इंजेक्ट करने की अनुमति देती है उदा।@EJBHow to inject @EJB, @PersistenceContext, @Inject, @Autowired, etc in @FacesConverter? भी देखें, इसलिए आपको converter="baseEntityConverter" के बजाय इसे converter="#{baseEntityConverter}" के रूप में संदर्भित करने की आवश्यकता है।

आप UISelectOne/UISelectMany घटकों (<h:selectOneMenu> और दोस्तों) के लिए अक्सर अधिक से अधिक इस तरह के एक कनवर्टर का उपयोग करने के लिए है, तो आप OmniFacesSelectItemsConverter बहुत अधिक उपयोगी मिल सकता है। यह हर बार (संभावित रूप से महंगा) डीबी कॉल करने के बजाय <f:selectItems> में उपलब्ध मानों के आधार पर परिवर्तित होता है।

+1

साथ बराबर और हैशकोड विधियों में अनुशंसित नहीं है: वे व्यापार कुंजी समानता का उपयोग कर बराबर() और हैशकोड() को कार्यान्वित करने की अनुशंसा करते हैं: https://docs.jboss.org/hibernate/stable/core.old/reference/en/html/persistent-classes -equalshashcode.html और समग्र आईडी के बारे में कैसे? –

0

यहाँ इस विचार के साथ अपने समाधान है:

  • मैं मान आप जेपीए में रुचि (हाइबरनेट नहीं)
  • मेरे समाधान किसी भी वर्ग फैली करने की आवश्यकता नहीं है और किसी भी जेपीए इकाई के लिए काम करना चाहिए रहे हैं बीन, यह केवल एक साधारण वर्ग है जिसका उपयोग आप करते हैं, और न ही इसे किसी भी सेवा या डीएओ को लागू करने की आवश्यकता होती है। एकमात्र आवश्यकता यह है कि कनवर्टर सीधे जेपीए लाइब्रेरी पर निर्भर करता है जो बहुत ही सुरुचिपूर्ण नहीं हो सकता है।
  • यह बीन की आईडी को क्रमबद्ध/deserializing के लिए सहायक विधियों का उपयोग करता है। यह केवल इकाई बीन की आईडी को परिवर्तित करता है और वर्गनाम के साथ स्ट्रिंग को जोड़ता है और आईडी धारावाहिक और बेस 64 में परिवर्तित हो जाता है। यह तथ्य इस तथ्य के कारण संभव है कि जेपीए में इकाइयों की आईडी को धारावाहिक लागू करना आवश्यक है। इस तरीकों के कार्यान्वयन जावा 1.7 में है, लेकिन आप वहाँ पर जावा < 1.7 के लिए एक और कार्यान्वयन मिल सकता है
 
import java.io.ByteArrayInputStream; 
import java.io.ByteArrayOutputStream; 
import java.io.IOException; 
import java.io.ObjectInput; 
import java.io.ObjectInputStream; 
import java.io.ObjectOutput; 
import java.io.ObjectOutputStream; 

import javax.faces.bean.ManagedBean; 
import javax.faces.bean.ManagedProperty; 
import javax.faces.bean.RequestScoped; 
import javax.faces.component.UIComponent; 
import javax.faces.context.FacesContext; 
import javax.faces.convert.Converter; 
import javax.faces.convert.ConverterException; 
import javax.persistence.EntityManagerFactory; 

/** 
* Generic converter of jpa entities for jsf 
* 
* Converts the jpa instances to strings with this form: @ Converts from strings to instances searching by id in 
* database 
* 
* It is possible thanks to the fact that jpa requires all entity ids to 
* implement serializable 
* 
* Requires: - You must provide instance with name "entityManagerFactory" to be 
* injected - Remember to implement equals and hashCode in all your entity 
* classes !! 
* 
*/ 
@ManagedBean 
@RequestScoped 
public class EntityConverter implements Converter { 

    private static final char CHARACTER_SEPARATOR = '@'; 

    @ManagedProperty(value = "#{entityManagerFactory}") 
    private EntityManagerFactory entityManagerFactory; 

    public void setEntityManagerFactory(EntityManagerFactory entityManagerFactory) { 
     this.entityManagerFactory = entityManagerFactory; 
    } 

    private static final String empty = ""; 

    @Override 
    public Object getAsObject(FacesContext context, UIComponent c, String value) { 
     if (value == null || value.isEmpty()) { 
      return null; 
     } 

     int index = value.indexOf(CHARACTER_SEPARATOR); 
     String clazz = value.substring(0, index); 
     String idBase64String = value.substring(index + 1, value.length()); 
EntityManager entityManager=null; 
     try { 
      Class entityClazz = Class.forName(clazz); 
      Object id = convertFromBase64String(idBase64String); 

     entityManager = entityManagerFactory.createEntityManager(); 
     Object object = entityManager.find(entityClazz, id); 

      return object; 

     } catch (ClassNotFoundException e) { 
      throw new ConverterException("Jpa entity not found " + clazz, e); 
     } catch (IOException e) { 
      throw new ConverterException("Could not deserialize id of jpa class " + clazz, e); 
     }finally{ 
     if(entityManager!=null){ 
      entityManager.close(); 
     } 
    } 

    } 

    @Override 
    public String getAsString(FacesContext context, UIComponent c, Object value) { 
     if (value == null) { 
      return empty; 
     } 
     String clazz = value.getClass().getName(); 
     String idBase64String; 
     try { 
      idBase64String = convertToBase64String(entityManagerFactory.getPersistenceUnitUtil().getIdentifier(value)); 
     } catch (IOException e) { 
      throw new ConverterException("Could not serialize id for the class " + clazz, e); 
     } 

     return clazz + CHARACTER_SEPARATOR + idBase64String; 
    } 

    // UTILITY METHODS, (Could be refactored moving it to another place) 

    public static String convertToBase64String(Object o) throws IOException { 
     return javax.xml.bind.DatatypeConverter.printBase64Binary(convertToBytes(o)); 
    } 

    public static Object convertFromBase64String(String str) throws IOException, ClassNotFoundException { 
     return convertFromBytes(javax.xml.bind.DatatypeConverter.parseBase64Binary(str)); 
    } 

    public static byte[] convertToBytes(Object object) throws IOException { 
     try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutput out = new ObjectOutputStream(bos)) { 
      out.writeObject(object); 
      return bos.toByteArray(); 
     } 
    } 

    public static Object convertFromBytes(byte[] bytes) throws IOException, ClassNotFoundException { 
     try (ByteArrayInputStream bis = new ByteArrayInputStream(bytes); ObjectInput in = new ObjectInputStream(bis)) { 
      return in.readObject(); 
     } 
    } 

} 

का उपयोग करते हुए "आईडी" संपत्ति है एक और कनवर्टर की तरह इसका इस्तेमाल

<h:selectOneMenu converter="#{entityConverter}" ... 
संबंधित मुद्दे