2013-03-18 9 views
7

Mam więc prostą funkcję zwracania czegoś z bazy danych. Następnie mogę zmodyfikować to zapytanie, dodając różne parametry w klauzuli WHERE. Jaki byłby najbardziej elegancki i skuteczny sposób radzenia sobie z tym?Jak używać przygotowanej instrukcji, jeśli nie wiem, ile parametrów będę przekazywać?

Przykład:

public static getUsers(int id, string username, string email) { 

     Connection conn = null; 
     PreparedStatement stmt = null; 
     String sql = ""; 

     sql = "SELECT * FROM users " ......... 

I to gdzie jestem mylić o klauzuli WHERE. Jeśli wykonuję coś takiego, jak:

"WHERE id = ? AND username = ? AND email = ?"; 

Co się stanie, jeśli wezwę metodę z tylko identyfikatorem i bez nazwy użytkownika lub adresu e-mail? Przerwa i nie mogę tego mieć.

Również trudno jest zarządzać indeksami, bo gdybym zrobił coś takiego, jak stmt.setInt(1, id), ale co, gdybym tylko chciał wywołać metodę z nazwą użytkownika, a ten identyfikator przyjdzie jako zerowy, nie rzuciłby a NPE?

Jestem trochę nowy w Javie, przepraszam ... ale myślę, że powinienem użyć przesłonięć? powinienem zbudować moją klauzulę where w instrukcji warunkowej? Każda pomoc będzie doceniona.

Dzięki

+0

Możemy potencjalnie sprawdź, czy istnieją różne parametry i dołącz je tylko do "WHERE" w tych przypadkach. Alternatywnie funkcja może przyjmować tablicę asocjacyjną, której używa do odwzorowania różnych kryteriów klauzuli WHERE. Prawdopodobnie jednak chcesz sprawdzić poprawność kluczy, aby upewnić się, że otrzymujesz kolumny, które istnieją. – jchapa

+0

To nie jest ładne, ale możesz zmodyfikować ciąg znaków sql za pomocą instrukcji if/else dla każdego parametru, a następnie przygotować instrukcję później: –

+0

@JeffHawthorne to jest to, dokąd zmierzałem, chociaż myślałem, że będzie bardziej eleganckie rozwiązanie. Obsługuje klauzulę WHERE dla sql, ale co z indeksami readstatement? Jak mam to powiedzieć, aby zignorować 'stmt.setInt (1, id)' i że to tak naprawdę 'stmt.setString (1, nazwa użytkownika)', ponieważ szukam po nazwie tym razem zamiast Id? Być może czegoś mi brakuje: P –

Odpowiedz

0

Doskonały pomysł z @ghdalum w rzeczywistości nie wymaga wstępnej oceny. Oto moja adaptacja jego budowniczy pomysł, aby produkować PreparedStatement:

public class UserQueryBuilder { 

    private Connection conn; 
    private StringBuilder query = new StringBuilder("SELECT * FROM users"); 
    private List<ValueSetter> valueSetters = new ArrayList<ValueSetter>(); 

    // callback interface for setting the column values 
    private interface ValueSetter { 
     void setValue(PreparedStatement ps); 
    } 

    // the caller is responsible for closing the connection afterwards 
    public QueryBuilder(Connection conn) { 
     this.conn = conn; 
    }   

    public QueryBuilder byId(final Integer id) { 
     appendSeparator(); 
     query.append("id = ?"); 
     valueSetters.add(new ValueSetter() { 
      public void setValue(PreparedStatement ps) { 
       ps.setInt(id); 
      } 
     }); 
     return this; 
    } 

    public QueryBuilder byEmail(String email) { 
     appendSeparator(); 
     query.append("email = ?"); 
     valueSetters.add(new ValueSetter() { 
      public void setValue(PreparedStatement ps) { 
       ps.setString(email); 
      } 
     }); 
     return this; 
    } 

    public QueryBuilder byUsername(String username) { 
     appendSeparator(); 
     query.append("username= ?"); 
     valueSetters.add(new ValueSetter() { 
      public void setValue(PreparedStatement ps) { 
       ps.setString(username); 
      } 
     }); 
     return this; 
    } 

    private void appendSeparator() { 
     if (filterValues.size() == 0) { 
      query.append(" WHERE ") 
     } 
     else { 
      query.append(" AND ") 
     } 
    } 

    public PreparedStatment build() { 
     PreparedStatement ps = conn.prepareStatement(query.toString()); 
     for(ValueSetter valueSetter : valueSetters) { 
      valueSetter.setValue(ps); 
     } 
     return ps; 
    } 
} 

Zastosowanie:

PreparedStatement userQuery = new UserQueryBuilder(conn) 
           .byId("2") 
           .byEmail("test") 
           .build(); 
userQuery.execute(); 

(BTW nie przetestować ten kod, więc nie może być literówki)

+0

jest to bardzo podobne do tego, co ostatecznie wprowadziłem w życie. Dzięki i 1 do was obu. –

0

zapisu jeśli else tj

if(username!=null) 
query=query+"username=?"; 

i

if(username!=null) 
stmt.setInt(2, username) 
0

chciałbym zrobić coś takiego stworzyć metodę, która akceptuje List<ColumnNames> iw tej pętli metody przez cały nazwy kolumn przez dołączenie właściciela miejsca do nazwy kolumny.

public List<Something> method(List<Something> list){ 

String sql = "SELECT * FROM users WHERE "+ this.processPlaceHolders(list); 

} 

public String processPlaceHolders(List<Something> list) { 
StringBuilder finalStr=new StringBuilder(""); 
for(int i=0;i<list.size(); i++){ 
if(i==list.size()-1){ 
finalStr.append(list.get(i)+"=" +"?"); 
} 
else { 
    finalStr.append(list.get(i)+"=" +"?,"); 
} 
} 
return finalStr.toString(); 
} 
0

Będziesz musiał dynamicznie budować zapytanie SQL, aby to osiągnąć.

public static getUsers(int id, string username, string email) { 

    Connection conn = null; 
    PreparedStatement stmt = null; 
    String sql = ""; 

    sql = "SELECT * FROM users where id=? "; 
    if (username != null) 
     sql += " AND username=? "; 
    if (email !=null) 
     sql += " AND email=?"; 

    stmt = conn.prepareStatement(sql); 
    stmt.setInt(1,id); 
    if (username != null && email !=null) 
    { 
     stmt.setString(2,username); 
     stmt.setString(3,email); 
    } 
    else if (username != null) 
     stmt.setString(2,username); 
    else if (email != null) 
     stmt.setString(2,email); 
+0

Problem z tą implementacją polega na tym, że zakłada ona, że ​​"identyfikator" jest zawsze dostarczany, ale może tak nie być. Pytanie brzmi, że możesz chcieć wyszukać użytkownika bez "id" (możesz stwierdzić, że -1 jest w tym przypadku przekazywane jako "identyfikator"). – rob

0

Rozwiązaniem problemu jest coś takiego:

public void getRecord(String id, String username, String email) 
{ 
    String sql = " select * from users "; 
    boolean isID , isUserName, isEmail; 
    boolean isFirst = true; 
    if (id!=null) 
    { 
    if (isFirst) 
    { 
     sql = sql + " where"; 
     isFirst = false; 
    } 
    sql = sql + " id = ?"; 
    isID = true; 
    } 
    if (username != null) 
    { 
    if (isFirst) 
    { 
     sql = sql + " where"; 
     sql = sql + " username = ?"; 
     isFirst = false; 
    } 
    else 
    sql = sql + " and username = ?"; 
    isUserName = true; 
    } 
    if (email != null) 
    { 
    if (isFirst) 
    { 
     sql = sql + " where"; 
     sql = sql + " email = ?"; 
     isFirst = false; 
    } 
    else 
    sql = sql + " and email = ?"; 
    isEmail = true; 
    } 
    PreparedStatement pst = con.prepareStatement(sql); 
    int counter = 1; 
    if (isID) 
    pst.setString(counter++,id); 
    if (isUserName) 
    pst.setString(counter++,username); 
    if (isEmail) 
    pst.setString(counter++,email); 
    //Then execute the query 
    ResultSet rs = pst.executeQuery(); 
    ....... 
} 
0

Spróbuj tego:

public static getUsers(int id, String username, String email) { 
     Connection conn = null; 
     PreparedStatement stmt = null; 
     String sql = "SELECT * FROM users WHERE id=? "; 

     if (username != null && username.trim().length() != 0) sql = sql + " AND username=? "; 
     if (email != null && email.trim().length() != 0) sql = sql + " AND username=? "; 

     stmt = conn.prepareStatement(sql); 
     stmt.setInt(1, id); 

     if (username != null && username.trim().length() != 0) stmt.setString(2, username); 
     if (email != null && email.trim().length() != 0) stmt.setString(3, email); 
//..... 
    } 
1

Chciałbym utworzyć klasę sqlquery który implementuje Builder Pattern. This to doskonały post wyjaśniający użycie wzoru.

Przykład:

public class SqlQuery { 
    private StringBuilder tableQuery = new StringBuilder(); 
    private StringBuilder whereQuery = new StringBuilder(); 

    public SqlQuery(String selection, String table) { 
     tableQuery.append("SELECT ").append(selection).append(" FROM ").append(table); 
    } 

    public SqlQuery addWhereClause(String parameter, String value) { 
     if (whereQuery.length() == 0) { 
      whereQuery.append(" WHERE "); 
     } 
     else { 
      whereQuery.append(" AND "); 
     } 
     whereQuery.append(parameter).append(" = ").append(value); 
     return this; 
    } 

    public String toString() { 
     return tableQuery.toString() + whereQuery.toString(); 
    } 
} 

SqlQuery sqlQ = new SqlQuery("*", "users") 
        .addWhereClause("id", "2") 
        .addWhereClause("email", "test"); 
System.out.println(sqlQ); 

Drukuje:

SELECT * FROM użytkowników WHERE id = 2 i e-mail = test

+0

Dlaczego nie pokazujesz nam przykładu? –

+1

Jestem posłuszny @AdriaanKoster – ghdalum

+0

@ghdalum można łańcuch append(), aby zachować wydajność StringBuilder: 'whereQuery.append (parametr) .append (" = ") .append (wartość);' – rob

0

Zamiast pisać jeden bardzo skomplikowany sposób, aby dynamicznie skonstruować a PreparedStatement, należy rozważyć napisanie osobnej metody dla każdej ważnej kombinacji danych wejściowych. Następnie każdy z łatwością może zweryfikować swoje dane wejściowe i zawsze będzie korzystał z określonego PreparedStatement.Nie tylko będzie to łatwiejsze do zrozumienia i utrzymania w przyszłości, ale będzie łatwiejsze do przetestowania.

Jeśli potrzebujesz kompatybilności wstecznej z istniejącym API, możesz napisać metodę getUsers(int id, string username, string email), która deleguje na prostsze metody.

Powiązane problemy