2012-02-07 37 views
8

Mam obecnie następujący kod, który pobiera dane z bazy danych, a następnie utworzyć User. Ten kod jest używany w wielu klassach do tworzenia innych obiektów, takich jak News, Comments itp ...Generic Javy Generic Konstruktorzy

Używa aputes commons dbutils.

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); 

byłoby możliwe, aby owinąć QueryRunner w ogólnej klasy i zastąpić metodę obsługi zapytań tak instanciate generycznego T z ResultSet. Upewniam się, że każdy typ T będzie konstruktorem akceptującym ResultSet.

tak:

 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; 
      } 
     }; 
    } 
} 

Nie wiem czy zrozumiesz, ale mam nadzieję, że tak, pytają mnie, jeśli chcesz poznać więcej szczegółów lub lepsze wyjaśnienie.

EDIT

Myślałem, że mogę użyć AbstractClass zamiast typu rodzajowego, że wszystkie obiekty zróżnicowanych by rozciąga ale wydaje się, że nie mogę napisać streszczenie konstruktora. Będę musiał dokonać statycznej metody, które zwracają instancję obiektu jak:

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

dlaczego musisz podać 'T type' w konstruktorze? – yair

+0

użyj refleksji, możesz wywołać konstruktor klasy z zestawem wyników –

Odpowiedz

8

Możliwe, tak? Ale to zły pomysł.

Można zrobić:

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; 
    } 
} 

Mieszanie domenę i bazy danych jest złym pomysłem, jednak. Co byłoby lepiej jednak byłoby zdefiniować abtract metodę, która tworzy obiekt na podstawie wynikowego:

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; 
    } 
} 

Następnie w swojej klasie wykonawczego, tylko trzeba zapewnić metodę create() zamiast obsługiwać wynik ustaw się, na przykład:

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

+1 za udzielenie takiej samej odpowiedzi jak moja, ale z ładnymi przykładami kodu. –

+0

Mówisz, że mieszanie bazy danych z domeną nie jest dobrym pomysłem i rozumiem, ale co zrobiłbyś, żeby zbudować te obiekty z wyników zapytania? – David

+0

Używam architektury MVC, więc mój model jest tam dla persistance i wie, jak zapisać i zaktualizować się w bazie danych, ale używam narzędzia do wykonywania kwerend przeciwko bazy danych. Może korzystasz z DAO? – David

0

Nie sądzę, że jest możliwe, aby to zrobić w Javie. Nie można utworzyć instancji klasy T w języku ogólnym. Generics w java nie są tak naprawdę szablonami jak w C++, są one tylko cukrem składowym wokół object, który usuwa odlewy i indukuje ostrzeżenia o czasie kompilacji.

Nie ma sposobu, jak w języku C#, aby ograniczyć T, tak aby musiał mieć konstruktora.

Najlepszym rozwiązaniem, jeśli jest to naprawdę konieczne, jest rozwiązanie odpowiedniej klasy za pomocą refleksji, ale nawet wtedy napotkasz problem, ponieważ nie możesz znać klasy środowiska T podczas wywoływania tej metody - ta informacja jest usunięta z kod bajtowy Java. Pozostaje ci przekazanie klasy do metody, aby użyć refleksji na jej temat. I nie jestem zbyt pewien, że i tak jest to dobry pomysł na projekt.

+0

Potrafi to zrobić z refleksją, tak jak powiedział JB Nizet. –

+0

Zdecydowanie możliwe. Potrzebujesz tylko odniesienia do 'Class '. –

+0

Tak, z refleksji. Nadal musisz przekazać aktualną klasę do metody. Nie możesz przejść do T.class, tak jak chciałbyś, gdyby informacje o klasie były obecne w czasie wykonywania. – Dervall

3

Możesz zrobić coś takiego (przekazując klasę obiektu do stworzenia i użyć odbicia, aby wywołać jego konstruktora), ale uznałbym to za zły projekt, aby POJO było zależne od JDBC i wiedząc nie tylko jak jest przechowywany w bazie danych, ale także jakie aliasy zostały użyte w kwerendzie użytej do załadowania.

Podsumowując, konstruktor POJO użytkownika nie jest odpowiedzialny za obsługę zestawu wyników zewnętrznego, nieznanego zapytania.

Można było zaprojektować superklasę AbstractSingleEntityHandler które właśnie mają blok

if (rs.next()) { 

i będzie delegowania tworzenia właściwego podmiotu, do abstrakcyjnej metody, ale nie zyskałaby wiele.

+0

+1 za bycie szybszym ode mnie i pisanie dokładnie tego, co chciałem;) –

+0

Próbuję to zrobić, ponieważ treść handler'a, którą widzisz powyżej w pierwszym bloku kodu, powtarzaj się w każdym zapytaniu dla dowolnego obiektu. Pomyślałem, że byłoby miło zapewnić, że moje obiekty pochodzące z bazy wiedzy wiedzą, jak skonstruować się z wyniku zapytania, metoda 'query' bezpośrednio zwróci obiekt. – David

+0

Co masz na myśli przez POJO? – David