2017-09-19 25 views
1

我有JDBC語句執行有許多方法的一些包裝類,像下面:的Java 8拉姆達風格包裝的JDBC語句的執行

public ResultSet executeQuery(String sql) { 
    try (Statement statement = this.connection.createStatement()) { 
     return statement.executeQuery(sql); 
    } catch (SQLException e) { 
     throw new RuntimeException("My custom statement execution failure", e); 
    } 
} 

public int executeUpdate(String sql) { 
    try (Statement statement = this.connection.createStatement()) { 
     return statement.executeUpdate(sql); 
    } catch (SQLException e) { 
     throw new RuntimeException("My custom statement execution failure", e); 
    } 
} 

... 

所以有很多的方法,這些方法的不同僅1)返回類型和2)實際的委託方法。

我想通過可執行呼叫Ruby的拉姆達時尚,以減少樣板代碼是這樣的:

public Boolean executeQuery(String sql) { 
    return wrapException(s -> {s.executeQuery(sql)}); 
} 

private <T> wrapException(Function<Statement, <T>> query)throws Exception { 
    try (Statement statement = this.connection.createStatement()) { 
     return query.apply(statement); 
    } catch (Exception e) { 
     throw new RuntimeException(e); 
    } 
} 

兩件事情我目前無法繞到我的頭是:

  1. 如何保留wrappedQuery函數的通用返回類型
  2. s.executeQuerty(sql)仍然抱怨未處理的SQLException, 如此有效我無法將所有異常處理way

任何想法?

+0

您忘了將'wrapException'重命名爲'wrappedQuery'嗎? – nullpointer

+0

@nullpointer噢,謝謝,複製/粘貼問題:) –

回答

0
  1. Function<Statement, <T>>是無效的語法。它應該只是Function<Statement, T>。很明顯,你在使用類型參數時會混淆類型參數的聲明,例如在方法的開始處(<T>),該類型參數應該是普通名稱,如T。請注意,可能出於同樣的原因,您的方法缺少返回類型。應該有一個<T>來聲明類型參數,然後在T之前聲明方法名稱之前的返回類型。

  2. 您的方法wrapException仍然聲明throws Exception這比僅聲明拋出SQLException的JDBC方法更糟糕。這與整個意圖相矛盾。

  3. 返回lambda表達式或者應,遵循表達式語法,
    s -> s.executeQuery(sql),或含有一個return聲明的值,例如s -> { return s.executeQuery(sql); }。您的表達s -> {s.executeQuery(sql)}是一個不完整的(缺失分號)語句語法,沒有return。此外,即使executeQuery返回ResultSet,您仍然聲明返回類型爲Boolean

  4. 的功能接口java.util.function.Function聲明函數方法apply不聲明任何checked異常,因此無法通過這可能拋出checked異常象SQLException lambda表達式實現它。所以對於你的用例,你需要一個不同的功能接口。

    您可以設置異常類型的泛型,使您的自定義功能界面可以在需要Function的其他地方重複使用,這些地方可能會拋出特定的異常。例如。

    public interface ThrowingFunction<T,R,E extends Exception> { 
        R apply(T input) throws E; 
    } 
    

這一點,你可以聲明

public ResultSet executeQuery(String sql) { 
    return wrapException(s -> s.executeQuery(sql)); 
} 
public int executeUpdate(String sql) { 
    return wrapException(s -> s.executeUpdate(sql)); 
} 
private <T> T wrapException(ThrowingFunction<Statement, T, SQLException> operation) { 
    try(Statement statement = this.connection.createStatement()) { 
     return operation.apply(statement); 
    } catch(SQLException e) { 
     throw new RuntimeException(e); 
    } 
} 

但請注意,是獨立於適用於您的原代碼,以及仿製藥/λ使用一個普遍的問題:

Statement.close()

注意:當一個Statement對象關閉時,其當前的ResultSet對象(如果存在)也將關閉。

try(…)聲明的全部目的是爲了確保在離開塊時,所以返回ResultSet從那裏是沒有意義的,資源將被立即關閉。 (但這並不適用於executeUpdate)。解決這個問題的一種方法是通過一個函數來處理塊內的ResultSet

public <R> R executeQuery(String sql, ThrowingFunction<ResultSet,R,SQLException> op) { 
    return wrapException(s -> op.apply(s.executeQuery(sql))); 
} 

這允許函數返回不依賴於ResultSet任意值了,例如

String value = executeQuery("SELECT STRING_COL FROM SOME_TABLE WHERE ID=42", 
          rs -> rs.next()? rs.getString(1): null); 
int max = executeQuery("SELECT MAX(INTVAL_COL) FROM SOME_TABLE", 
         rs -> rs.next()? rs.getInt(1): -1);