2012-02-07 11 views
8

Attualmente ho il seguente codice che recupera i dati dal database e quindi crea un User. Questo codice è usato in molti dei miei corsi per creare altri oggetti come News, Comments ecc ...java generici costruttori

Usa dbutils di apache comuni.

final ResultSetHandler<User> handler = new ResultSetHandler<User>() { 

      @Override 
      public User handle(ResultSet rs) throws SQLException { 

       User user = null; 
       if (rs.next()) { 
        user = new User(); 
        user.setId(rs.getInt("id")); 
        user.setUsername(rs.getString("username")); 
        user.setPassword(rs.getString("password")); 
       } 
       return user; 
      } 
     }; 

     final User user = run.query(
       "SELECT id, username, password FROM users WHERE username = ? AND active = 2 LIMIT 1;", handler, 
       username); 

sarebbe possibile avvolgere il QueryRunner in una classe generica e sovrascrivere il metodo query in modo il gestore instanciate il generico T con il ResultSet. Mi assicuravo che qualsiasi tipo di T avesse un costruttore che accetta un ResultSet.

Come così:

 public class QueryExecuter<T> extends QueryRunner { 
    private ResultSetHandler<T> _handler; 

    public QueryExecuter(){//The T type was for testing haha 
     super(); 
     handler = new ResultSetHandler<T>() { 

      @Override 
      public T handle(ResultSet rs) throws SQLException { 

       T object = null; 
       if (rs.next()) { 
        object = new T(rs); 
       } 
       return object; 
      } 
     }; 
    } 
} 

Non so se capirai, ma spero di sì, mi chiede se volete maggiori dettagli o una spiegazione migliore.

EDIT

ho pensato che avrei potuto usare un AbstractClass invece che il tipo generico che tutti gli oggetti differenti avrebbero estende, ma sembra che non posso scrivere un costruttore astratto. Devo fare un metodo statico che restituisca un'istanza dell'oggetto come:

public abstract class DatabaseEntity { 
    public static abstract DatabaseEntity create(ResultSet rs);//even this doesn't work... 
} 
+0

perché è necessario passare 'T type' nel costruttore? – yair

+0

usa reflection puoi invocare il costruttore di classi con il set di risultati –

risposta

8

Possibile, sì? Ma è una cattiva idea.

Si potrebbe fare:

class ResultSetHandler<T> { 
    ResultSetHandler<T>(Class<T> clazz) { 
    this.clazz = clazz; 
    } 

    public T handle(ResultSet rs) throws SQLException { 
    T object = null; 
    if (rs.next()) { 
     object = clazz.getConstructor(ResultSet.class).newInstance(rs) 
    } 
    return object; 
    } 
} 

dominio e il database di miscelazione è una cattiva idea, però. Che cosa sarebbe meglio, però, sarebbe quello di definire un metodo di abtract che crea l'oggetto in base al gruppo di risultati:

abstract class ResultSetHandler<T> { 

    protected abstract T create(ResultSet rs); 

    public T handle(ResultSet rs) throws SQLException { 
    T object = null; 
    if (rs.next()) { 
     object = create(rs); 
    } 
    return object; 
    } 
} 

Poi, nella tua classe che implementa, avete solo bisogno di fornire un metodo create() invece di gestire il risultato imposta te stesso, ad esempio:

h = new ResultSetHandler<Person>() { 
    protected Person create(ResultSet rs) { 
    return new Person(rs.getString("name")); 
    } 
} 
+0

+1 per fornire la stessa risposta della mia, ma con esempi di codice. –

+0

Si dice che mescolare il database con il dominio non è una buona idea e lo prendo, ma cosa faresti per costruire questi oggetti dai risultati delle query? – David

+0

Sto usando un'architettura MVC, quindi il mio modello è lì per la persistenza e sa come salvarlo e aggiornarsi nel database, ma io uso un'utilità per eseguire le query sul database. Forse usi DAO? – David

0

Non credo sia possibile farlo in Java. Non è possibile creare un'istanza di T in un generico. Generics in java non sono realmente modelli come in C++, sono solo zucchero di sintassi attorno a uno object che rimuove i cast e induce avvisi di tempo di compilazione.

In C# non esiste un modo per limitare T in modo che sia necessario un costruttore.

La soluzione migliore se ciò è davvero necessario è risolvere la classe appropriata utilizzando il reflection, ma anche in questo caso si verificherà un problema poiché non è possibile conoscere la classe di esecuzione di T come viene invocato il metodo - questa informazione viene rimossa da il bytecode java. Quindi ti rimane il compito di passare la classe al metodo per usare la riflessione su di essa. E non sono troppo sicuro che sia comunque una buona idea di design.

+0

Può farlo con la riflessione proprio come diceva JB Nizet. –

+0

Definitivamente possibile. Hai solo bisogno di un riferimento a 'Classe '. –

+0

Sì, con la riflessione. Dovresti comunque passare la classe effettiva al metodo. Non puoi andare a T.class come vorresti, le informazioni sulla classe erano presenti al runtime. – Dervall

3

Si potrebbe fare qualcosa del genere (passando la classe dell'oggetto da creare, e usare reflection per chiamare il suo costruttore), ma troverei il design sbagliato avere il POJO dipendente da JDBC, e non solo sapere come è memorizzato nel database, ma anche quali alias sono stati utilizzati nella query utilizzata per caricarlo.

In breve, non è responsabilità del costruttore POJO utente gestire un set di risultati di una query esterna sconosciuta.

si potrebbe progettare una superclasse AbstractSingleEntityHandler che avrebbe semplicemente avere il blocco

if (rs.next()) { 

, e sarebbe delegare la creazione effettiva entità ad un metodo astratto, ma non sarebbe guadagnare molto.

+0

+1 per essere più veloce di me e scrivere esattamente quello che volevo;) –

+0

Sto provando a farlo perché il corpo del gestore che vedi sopra nel primo blocco di codice, viene ripetuto in ogni query per qualsiasi oggetto. Ho pensato che sarebbe stato bello assicurarmi che i miei oggetti che provengono dal db sappiano come costruirsi da una query, il metodo 'query'method restituirebbe direttamente l'oggetto. – David

+0

E cosa intendi per POJO? – David