2010-02-23 9 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를을 만드는 오전. –

+0

개인적으로 데이터베이스 모델에 매핑되는 Java 모델을 사용하는 데 문제가 발생하지 않습니다. 또한 SQL 쿼리를 만들 때 SQL 문자열을 작성하는 대신 PreparedStatements를 사용하여 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

@Makis하지만 _how_ 생성 된 클래스를 사용 하시겠습니까? 당신은 그들에게 던지기를 할 수 없으며, 코드를 작성할 때 자신의 분야가 무엇인지 알 수 없습니다. – Bozho

+0

필자가 작성한대로 반성을 사용하여 해당 클래스의 필드를 결정합니다. 필요한 모든 정보입니다. 각 필드의 유형, 이름 및 값을 살펴 보겠습니다. 위의 질문에 예제를 추가하겠습니다. – Makis

17

가이 (예를 들어 프록시, ASM)를 달성하는 방법에는 여러 가지가 있지만 당신은 프로토 타입 인 경우로 시작할 수있는 가장 간단한 방법 하나 :

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

같은 기존의 것을 사용할 수있다 생각합니다. 클래스는 데이터와 코드 둘 다입니다. 동적 데이터와 연관시킬 코드는 어떤 종류입니까?

아마 당신이 원하는 것은 콜렉션 또는 아마도 Hibernate 일 것입니다.

컬렉션으로 많은 트릭을 재생하여 원하는대로 할 수 있습니다. 오브젝트를] 렉션에 직접 놓는 대신 메타 오브젝트에 랩핑하여 유형을 보장하는 데이터 나 널이 아닌 데이터로 저장할 수 있습니다. 유형 안전성, 완전성 및 관계를 강화하는 클래스에서 전체 콜렉션을 래핑 할 수 있습니다. 나는 심지어 내 컬렉션에 할당 된 데이터의 유효성을 검사하는 "Validator"클래스를 사용할 수있는 기능을 제공했습니다.

유효성 검사, 구조 및 초기 항목은 데이터베이스, XML 또는 코드에서 가져올 수 있습니다.

1

Hibernate와 같은 ORM으로 데이터베이스에 쉽게 맵핑하거나 자신의 JDBC DAO를 작성하여 각 테이블에 대한 데이터 모델 클래스를 만드는 데 몇 분이 걸립니다. 그것은 성찰에 깊이 빠져있는 것보다 훨씬 쉽습니다.

테이블의 데이터베이스 구조를 조사하고 사용자를 위해 데이터 모델 클래스와 DAO를 만드는 유틸리티를 만들 수 있습니다. 또는 Java로 모델을 생성하고 유틸리티를 작성하여 데이터베이스 스키마와 DAO를 작성할 수 있습니다 (리플렉션 및 Java 5 Annotations을 사용하여 지원). javaFieldNames가 일반적으로 database_column_names와 다른 점을 잊지 마십시오.

0

는 내가 자바, 그루비는 더 나은 IMO에 적합 할 것이라고 적합한 도구라고 생각하지 않는다

Type classType = new TypeToken<ArrayList<MyModelClass>>() { 
               }.getType(); 
             ArrayList<MyModelClass> arrangements = new Gson().fromJson(
               jObj.getJSONArray(Keys.MODEL_CLASS_LIST).toString(), 
               classType); 
관련 문제