2016-11-09 3 views
3

Ich bin neugierig, können wir nur Parameter dynamisch eingeben? Zum Beispiel habe ich ein Objekt wie:Parameter dynamisch für PreparedStatements einstellen

class Fields { 
    private Integer id; 
    private String name; 

    public Fields(Integer id, String name) { 
    this.id = id; 
    this.name = name; 
    } 
    //Getter - setter here 
} 

Jetzt bereite ich Aussage für:

public void main() { 
    PreparedStatement ps = conn.PrepareStatement("SELECT * FROM table WHERE id = ? AND name = ?");` 
} 

dann stelle ich

Fields param = new Fields(2, "xxx");

Nun .. rufen Sie mich an faul, wie kann ich param zu PrepareStatement wie

senden

oder vielleicht:

ps.setParameters(param.getAsArray())

Zu meinem Punkt besser zu verdeutlichen, möchte ich, wie etwas machen:

for (Fields f: fieldList) { 
    sql += f + " = ?"; 
} 

pardon mein "und"/"OR" Operator oben fehlt Codes. Mein oberstes Ziel ist, dass ich die gesamten Felder und die entsprechenden Parameter durch Übergabe der Entitätsklasse erstellen kann, indem ich nur ein Teilfeld ausfülle. Der Code wird dann Nullfelder überspringen und gefüllte Felder als Parameter einfügen.

Nun, nur aus Neugier.

Danke

+0

AFAIK Sie sind begrenzt in Bezug auf was Sie zu einem 'PreparedStatement' übergeben können. In der Regel sind nur die Typen zulässig, die Sie in einer Datenbankabfrage verwenden, z. int, varchar, blob usw. Was ist die 'Fields'-Klasse? –

+0

Die Feldklasse wie oben wäre auf einen String beschränkt und daher nicht sehr nützlich. Auch wenn Sie einen Join hatten und dieselbe Variable wiederverwendet wurde, konnte das gleiche Field-Objekt nicht erneut verwendet werden. Natürlich können Sie von 'PreparedStatement' ausgehen und dies selbst implementieren. –

+0

Informationen hinzugefügt, um meinen Punkt besser zu veranschaulichen. Kurz gesagt, ich möchte dynamisch Kriterien Klausel aus Entity-Objekt erstellen, und übergeben Sie meine Felder und Parameter aus Entity-Objekt. Aber das ist nicht nur in der Entity eingeschränkt, ich könnte eine bestimmte Klasse erstellen und relevante Felder teilweise ausfüllen, unnötige überspringen und sie an prepareStatement übergeben. – Magician

Antwort

0

Ok .. das ist eine sehr grobe Lösung. Ich mag immer noch nicht die Tatsache, dass ich alle Felder zweimal wiederholen muss, um den Feldtyp String oder anders zu erhalten, aber ich fühle mich frei, es weiter zu verfeinern, da dies immer noch sehr grob ist. Außerdem müssen Sie diesen ps.setSomething() - Parameter vor ps.setObject() failsafe erweitern. Sie müssen auch eine Abfrage ohne Kriterien antizipieren, wie das Scannen von Kriterienobjekten überspringen und direkt zur Ausführung gehen. Aber ich hoffe, du hast die Idee.

public static String addPrefix(String prefix, String field) { 
    return new StringBuilder(prefix) 
      .append(Character.toUpperCase(field.charAt(0))) 
      .append(field.substring(1)) 
      .toString(); 
} 

public static <T> List<T> query(Connection conn, T criteria, String operator) throws SQLException { 
    List<T> list = null; 
    Class<?> targetClass = criteria.getClass(); 
    if (targetClass.getAnnotation(Table.class) == null) throw new SQLException("ERROR: Table not defined at entity class " + targetClass.getName()); 
    StringBuilder SQL = new StringBuilder("SELECT * FROM ").append(targetClass.getAnnotation(Table.class).name()); 
    List<Object> parameters = new ArrayList<>(); 

    try { 
     Field[] fields = targetClass.getDeclaredFields(); 
     for (Field field : fields) { 
      if (field.getAnnotation(Column.class) == null) continue; 
       Method m = targetClass.getMethod(addPrefix("get", field.getName()).toString()); 
       Object o = m.invoke(criteria); 

       if (o == null) continue; 

       if (parameters.isEmpty()) SQL.append(" WHERE"); else SQL.append(operator); 
       SQL.append(" ").append(field.getAnnotation(Column.class).name()).append(" = ?"); 
       parameters.add(o); 
     } 

     try (Connection connection = IwiPrivate.getInstance().getConnection()) { 
      try (PreparedStatement ps = connection.prepareStatement(SQL.toString())) { 
       Integer x = 1; 
       for (Field field : fields) { 
        String type = field.getType().getName(); 
        Method m = targetClass.getMethod(addPrefix("get", field.getName())); 
        Object o = m.invoke(criteria); 

        if (o == null) continue; 
        if (type == "java.lang.String") ps.setString(x, (String) parameters.get(x)); 
        else if (type == "java.lang.Integer") ps.setInt(x, (Integer) parameters.get(x)); 
        else ps.setObject(x, parameters.get(x)); //Put more set traps here. 
       } 
       try (ResultSet rs = ps.executeQuery();) { 
        while (rs.next()) 
         list.add((T) Database.mapSingle(rs, targetClass)); 
       } 
      } 
     } 
    } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { 
     Logger.getLogger(QueryExperiment.class.getName()).log(Level.SEVERE, null, ex); 
    } 
    return list; 
} 

Nun, es zu benutzen, einfach erstellen Sie Ihre Entitätsobjekt wie diese

@Table(name = "testTable") 
public class Entity { 
    @Column(name = "id") 
    private Integer id; 

    @Column(name = "name") 
    private String name; 

    public Integer getId() { 
     return id; 
    } 

    public void setId(Integer id) { 
     this.id = id; 
    } 

    public String getName() { 
     return name; 
    } 

    public void setName(String name) { 
     this.name = name; 
    } 
} 

Dann legte sie als Kriterien

public void testQuery() { 
    Entity criteria = new Entity(); 
    criteria.setId(7777); 
    try (Connection connection = yourDatabase.getConnection()) { 
     List<Entity> assets = QueryTest.query(connection, criteria, "AND"); 
    } catch (SQLException ex) { 
     Logger.getLogger(IwiPrivateTest.class.getName()).log(Level.SEVERE, null, ex); 
    } 
} 

Es wird eine SQL-ähnliche erstellen: SELECT * FROM testTable WHERE id = ? und sende 7777 als Parameter mit setInt

Wenn Sie ORM vermeiden und einfache Abfragen erstellen möchten, funktioniert diese Methode meiner Meinung nach einwandfrei.

1

Sie direkt die Werte in den Parametern ohne die Feld Klasse setzen.

ps.setParameters(2, "xxx"); 

Versuchen Sie diese, hoffe es funktioniert Ihnen.

Field field1 = new Field(0,1,"id"); 
    Field field2 = new Field(1,"Nathan","name"); 

    List<Field> fields = new ArrayList<>(); 
    fields.add(field1); 
    fields.add(field2); 

    StringBuilder strBuilder = new StringBuilder("SELECT * FROM table WHERE "); 

    // Dynamically Construct your SQL Query String 
    for(int i = 0 ; i < fields.size() ; i++) { 
     String strTemp = " AND "; 
     String strTempEquals = " = ?"; 
     if(fields.size()-1 == i) { 
      strTemp = ";"; 
     } 
     strBuilder.append(fields.get(i).getColumnName()); 
     strBuilder.append(strTempEquals); 
     strBuilder.append(strTemp); 
    } 

     PreparedStatement ps = conn.PrepareStatement(strBuilder); 

    // Dynamically pass your values from your Field class to the parameters of PreparedStatement 
    for (Field tempField : fields) { 
     ps.setParameter(tempField.getIndex(),tempField.getValue()); 
    } 

Ich habe einige Veränderungen in Ihrem Feld Klasse.

class Field { 
private Integer index; 
private Object value; 
private String columnName; 

// Constructor 
//Getters and Setter 
} 
+0

In der Tat möchte ich das vermeiden. – Magician

1

Versuchen parametrisierte Konstruktor wie folgt verwenden ich das hoffen, was Sie

class Fields { 
private Integer id; 
private String name; 
public Fields(int id,String name) 
{ 
    this.id=id; 
    this.name=name; 
    setParameter(); 
} 
public void setParameter() 
{ 
    PreparedStatement ps = conn.PrepareStatement("SELECT * FROM table WHERE id = ? AND name = ?"); 
    ps.setInt(1,id); 
    ps.setString(2,name); 
} 
} 
+1

Im Gegenteil, ich möchte nicht setSomething() für jeden Parameter angeben. – Magician

+0

ohh als du solltest durch den Winterschlaf gehen/Frühling –

+1

Der Frühling ist zu schwer, und es kann nicht direkt von Entity Kriterien nehmen; – Magician

0

PreparedStatement hat eine setObject(int, java.lang.Object) Methode benötigen. Sie könnten dies innerhalb einer a-Klasse oder -Methode verwenden, die Array/Liste von Fields akzeptiert und Namen an sql anfügt und Parameter mit der setObject-Methode festlegt.

Beachten Sie, dass dieser Ansatz sehr anfällig für SQL-Injection-Angriffe ist, also verwenden Sie ihn mit Vorsicht.

+0

Hmm ... das gibt mir eine Idee .. festhalten. Ich werde bald etwas geben. – Magician

+0

Die eigentliche Frage ist, warum brauchst du das? Warum nicht POJO-Klassen erstellen, die Ihren Tabellen entsprechen und die POJO-Objekte in den entsprechenden Tabellen lesen/schreiben? –

+0

Wie gesagt, ich benutze kein ORM. JPA oder Hibernate oder irgendetwas. Ich mag sie einfach nicht. Sie sind zu schwer und es mangelt ihnen an Optimierungen. Daher bevorzuge ich die Optimierung vom Datenbankserver. Alles, was ich brauche, ist ein einfacher Abruf von Ansichten oder gespeicherten Prozeduren. Aber diese Frage war nur ein einfaches Experiment, das meine Arbeit beschleunigen kann, aber immer noch Ressourcen schont. – Magician

Verwandte Themen