2010-02-23 16 views
21

साथ मैं इस बारे में जानकारी प्राप्त करने की कोशिश की, लेकिन खाली हाथ आए हैं:बनाना कक्षाएं गतिशील जावा

मैं यह संभव है प्रतिबिंब या प्रॉक्सी का उपयोग कर जावा में गतिशील रूप से एक वर्ग बनाने के लिए, लेकिन मैं बाहर नहीं मिल सकता है इकट्ठा किस तरह। मैं एक साधारण डेटाबेस ढांचे को कार्यान्वित कर रहा हूं जहां मैं प्रतिबिंब का उपयोग कर SQL क्वेरी बनाते हैं। विधि डेटाबेस फ़ील्ड के साथ ऑब्जेक्ट को पैरामीटर के रूप में प्राप्त करती है और उस पर आधारित क्वेरी बनाता है। लेकिन यह बहुत उपयोगी होगा अगर मैं वस्तु को गतिशील रूप से भी बना सकता हूं इसलिए मुझे प्रत्येक तालिका के लिए एक साधारण डेटा रैपर ऑब्जेक्ट की आवश्यकता नहीं होगी।

गतिशील कक्षाओं को केवल साधारण फ़ील्ड (String, Integer, Double) की आवश्यकता होगी, उदाहरण के लिए

public class Data { 
    public Integer id; 
    public String name; 
} 

क्या यह संभव है और मैं यह कैसे करूँगा?

संपादित करें: यह मैं यह कैसे प्रयोग करेंगे है:

/** Creates an SQL query for updating a row's values in the database. 
* 
* @param entity Table name. 
* @param toUpdate Fields and values to update. All of the fields will be 
* updated, so each field must have a meaningful value! 
* @param idFields Fields used to identify the row(s). 
* @param ids Id values for id fields. Values must be in the same order as 
* the fields. 
* @return 
*/ 
@Override 
public String updateItem(String entity, Object toUpdate, String[] idFields, 
     String[] ids) { 
    StringBuilder sb = new StringBuilder(); 

    sb.append("UPDATE "); 
    sb.append(entity); 
    sb.append("SET "); 

    for (Field f: toUpdate.getClass().getDeclaredFields()) { 
     String fieldName = f.getName(); 
     String value = new String(); 
     sb.append(fieldName); 
     sb.append("="); 
     sb.append(formatValue(f)); 
     sb.append(","); 
    } 

    /* Remove last comma */ 
    sb.deleteCharAt(sb.toString().length()-1); 

    /* Add where clause */ 
    sb.append(createWhereClause(idFields, ids)); 

    return sb.toString(); 
} 
/** Formats a value for an sql query. 
* 
* This function assumes that the field type is equivalent to the field 
* in the database. In practice this means that this field support two 
* types of fields: string (varchar) and numeric. 
* 
* A string type field will be escaped with single parenthesis (') because 
* SQL databases expect that. Numbers are returned as-is. 
* 
* If the field is null, a string containing "NULL" is returned instead. 
* 
* @param f The field where the value is. 
* @return Formatted value. 
*/ 
String formatValue(Field f) { 
    String retval = null; 
    String type = f.getClass().getName(); 
    if (type.equals("String")) { 
     try { 
      String value = (String)f.get(f); 
      if (value != null) { 
       retval = "'" + value + "'"; 
      } else { 
       retval = "NULL"; 
      } 
     } catch (Exception e) { 
      System.err.println("No such field: " + e.getMessage()); 
     } 
    } else if (type.equals("Integer")) { 
     try { 
      Integer value = (Integer)f.get(f); 
      if (value != null) { 
       retval = String.valueOf(value); 
      } else { 
       retval = "NULL"; 
      } 
     } catch (Exception e) { 
      System.err.println("No such field: " + e.getMessage()); 
     } 
    } else { 
     try { 
      String value = (String) f.get(f); 
      if (value != null) { 
       retval = value; 
      } else { 
       retval = "NULL"; 
      } 
     } catch (Exception e) { 
      System.err.println("No such field: " + e.getMessage()); 
     } 
    } 
    return retval; 
} 
+0

वर्ग के ArrayList बनाने हूँ मुझे नहीं लगता है कि जावा कि के लिए सही उपकरण है, ग्रूवी बेहतर IMO अनुकूल हो पाती या नहीं। –

+0

व्यक्तिगत रूप से मुझे जावा मॉडल रखने में समस्या दिखाई नहीं देती है जो आपके डेटाबेस मॉडल पर मैप करता है। SQL क्वेरी बनाने के दौरान, SQL स्ट्रिंग बनाने के बजाय, SQL इंजेक्शन से बचने के लिए प्रीपेयरस्टेटमेंट का उपयोग करना सुनिश्चित करें। – JeeBee

उत्तर

16

यह (cglib, asm, javassist, bcel के माध्यम से) कक्षाओं उत्पन्न करने के लिए संभव है, लेकिन आप इसे उस तरह से नहीं करना चाहिए। क्यूं कर?

  • कोड है कि पुस्तकालय का उपयोग कर रहा प्रकार Object उम्मीद और प्रतिबिंब का उपयोग कर सभी क्षेत्रों मिलना चाहिए - नहीं एक अच्छा विचार
  • जावा स्थिर भाषा टाइप किया है, और आप गतिशील टाइपिंग शुरू करने के लिए चाहते हैं - यह जगह नहीं है।

आप बस एक अपरिभाषित प्रारूप में डेटा चाहते हैं, तो आप इसे एक सरणी में लौट सकते हैं, Object[], या Map<String, Object> की तरह आप उन्हें नामित चाहते हैं, और वहाँ से इसे पाने के - यह आप के साथ ज्यादा परेशानी की बचत होगी अनइडेड क्लास पीढ़ी को कुछ डेटा रखने का एकमात्र उद्देश्य है जो प्रतिबिंब द्वारा प्राप्त किया जाएगा।

इसके बजाय आप क्या कर सकते हैं पूर्वनिर्धारित कक्षाएं हैं जो डेटा धारण करती हैं, और क्वेरीिंग विधियों के लिए तर्क के रूप में उन्हें पास करती हैं। उदाहरण के लिए:

public <T> T executeQuery(Class<T> expectedResultClass, 
     String someArg, Object.. otherArgs) {..} 

इस प्रकार आप प्रतिबिंब पारित कर दिया expectedResultClass पर उस प्रकार के नई वस्तु बनाने और क्वेरी के परिणाम से पॉप्युलेट करने के लिए उपयोग कर सकते हैं।

कहा, मैं तुम्हें कुछ मौजूदा, एक ORM फ्रेमवर्क (हाइबरनेट, EclipseLink), वसंत के JdbcTemplate, आदि

+1

मेरा विचार था कि बड़ी संख्या में कक्षाएं होने से बचें जिनमें केवल साधारण फ़ील्ड हों। फ्लाई पर कक्षाएं बनाना बहुत आसान होगा। बहुत कम कोड और बस उन सभी वर्गों के रूप में स्पष्ट के बारे में। – Makis

+2

@ माकिस लेकिन _how_ क्या आप जेनरेट कक्षाओं का उपयोग करेंगे? कोड को लिखते समय आप उन्हें नहीं डाल सकते हैं, आप नहीं जानते कि उनके क्षेत्र क्या हैं। – Bozho

+0

जैसा कि मैंने लिखा है, मैं उस वर्ग के फ़ील्ड को निर्धारित करने के लिए प्रतिबिंब का उपयोग करूंगा - यही वह जानकारी है जो मुझे चाहिए। मैं प्रत्येक फ़ील्ड के प्रकार, नाम और मूल्य को देखूंगा। मैं उपरोक्त मेरे प्रश्न में उदाहरण जोड़ूंगा। – Makis

17

इस (उदाहरण के लिए प्रॉक्सी, एएसएम) प्राप्त करने के लिए कई अलग अलग तरीके हैं, लेकिन सबसे सरल दृष्टिकोण, एक है कि आप जब प्रोटोटाइप है के साथ शुरू कर सकते हैं :

import java.io.*; 
import java.util.*; 
import java.lang.reflect.*; 

public class MakeTodayClass { 
    Date today = new Date(); 
    String todayMillis = Long.toString(today.getTime()); 
    String todayClass = "z_" + todayMillis; 
    String todaySource = todayClass + ".java"; 

    public static void main (String args[]){ 
    MakeTodayClass mtc = new MakeTodayClass(); 
    mtc.createIt(); 
    if (mtc.compileIt()) { 
     System.out.println("Running " + mtc.todayClass + ":\n\n"); 
     mtc.runIt(); 
     } 
    else 
     System.out.println(mtc.todaySource + " is bad."); 
    } 

    public void createIt() { 
    try { 
     FileWriter aWriter = new FileWriter(todaySource, true); 
     aWriter.write("public class "+ todayClass + "{"); 
     aWriter.write(" public void doit() {"); 
     aWriter.write(" System.out.println(\""+todayMillis+"\");"); 
     aWriter.write(" }}\n"); 
     aWriter.flush();  
     aWriter.close(); 
     } 
    catch(Exception e){ 
     e.printStackTrace(); 
     } 
    } 

    public boolean compileIt() { 
    String [] source = { new String(todaySource)}; 
    ByteArrayOutputStream baos= new ByteArrayOutputStream(); 

    new sun.tools.javac.Main(baos,source[0]).compile(source); 
    // if using JDK >= 1.3 then use 
    // public static int com.sun.tools.javac.Main.compile(source);  
    return (baos.toString().indexOf("error")==-1); 
    } 

    public void runIt() { 
    try { 
     Class params[] = {}; 
     Object paramsObj[] = {}; 
     Class thisClass = Class.forName(todayClass); 
     Object iClass = thisClass.newInstance(); 
     Method thisMethod = thisClass.getDeclaredMethod("doit", params); 
     thisMethod.invoke(iClass, paramsObj); 
     } 
    catch (Exception e) { 
     e.printStackTrace(); 
     } 
    } 
} 
+0

मैं इस दृष्टिकोण को इस तरह से सुझाव देता हूं क्योंकि ऐसा लगता है कि आप जो कक्षाएं उत्पन्न कर रहे हैं वह तुच्छ हैं। –

+1

मुझे sun.tools.javac.Main और com.sun.tools.javac नहीं मिल रहा है। मुझे ये कहां मिल सकता है? – Makis

+0

आज़माएं: com.sun.tools.javac.Main आयात करें और मुझे बताएं कि क्या यह –

0

यह संभव है, लेकिन (मेरा मानना ​​है कि) आप ASM या BCEL की तरह कुछ की जरूरत है।

वैकल्पिक रूप से, आप अधिक शक्ति (जैसे Groovy) के साथ कुछ उपयोग कर सकते हैं।

0

मैं या तो इस कोशिश नहीं की तरह इस्तेमाल कर सकते हैं लगता है। एक वर्ग डेटा और कोड दोनों है, आप अपने गतिशील डेटा के साथ जोड़ने के लिए किस प्रकार का कोड तैयार कर रहे हैं?

जो आप शायद चाहते हैं वह एक संग्रह है - या शायद हाइबरनेट।

आप जो भी चाहते हैं उसे करने के लिए आप संग्रह के साथ कई सारी चालें खेल सकते हैं। वस्तुओं को सीधे संग्रह में रखने के बजाय, आप उन्हें मेटा-ऑब्जेक्ट्स में डेटा के साथ लपेट सकते हैं जो उनके प्रकार को सुनिश्चित करता है या वे शून्य नहीं हैं। आप अपने पूरे संग्रह को उस वर्ग में लपेट सकते हैं जो प्रकार की सुरक्षा, पूर्णता और रिश्तों को लागू करता है। मैंने अपने संग्रहों को असाइन किए गए डेटा को सत्यापित करने के लिए "सत्यापनकर्ता" कक्षाएं लेने की क्षमता भी दी है।

सत्यापन, संरचना और प्रारंभिक प्रविष्टियां XML से या कोड से डेटाबेस से आ सकती हैं।

1

प्रत्येक तालिका के लिए डेटा मॉडल क्लास बनाने में कुछ मिनट लगेंगे, जिसे आप आसानी से डेटाबेस में एचआईआरनेट के साथ या अपने स्वयं के जेडीबीसी डीएओ लिखकर डेटाबेस में मैप कर सकते हैं। प्रतिबिंब में गहराई से बहने से कहीं ज्यादा आसान है।

आप एक उपयोगिता बना सकते हैं जो किसी तालिका के लिए डेटाबेस संरचना से पूछताछ करता है, और आपके लिए डेटा मॉडल वर्ग और डीएओ बनाता है। वैकल्पिक रूप से आप जावा में मॉडल बना सकते हैं और उस से डेटाबेस स्कीमा और डीएओ बनाने के लिए उपयोगिता बना सकते हैं (सहायता के लिए प्रतिबिंब और जावा 5 एनोटेशन का उपयोग करके)। यह न भूलें कि javaFieldNames डेटाबेस_column_names से आम तौर पर अलग हैं।

0

मैं की तरह

Type classType = new TypeToken<ArrayList<MyModelClass>>() { 
               }.getType(); 
             ArrayList<MyModelClass> arrangements = new Gson().fromJson(
               jObj.getJSONArray(Keys.MODEL_CLASS_LIST).toString(), 
               classType); 
संबंधित मुद्दे