2012-01-11 23 views
3

代碼我繼承了許多功能的Java代碼中有這種形式的許多交易代碼方法:你會如何去優化(無碼重複),其中有這類

public void addCourseToCourses(String values) 
{ 
    try 
    { 
     conn.setAutoCommit(false); 
    } 
    catch (SQLException e) 
    { 
     Logger.getLogger(connDb.class.getName()).log(Level.SEVERE, null, e); 
     return; 
    } 
    try 
    { 
     stmt.executeUpdate("insert into courses values " + values); 
     conn.commit(); 
    } 
    catch (SQLException e) 
    { 
     try 
     { 
      conn.rollback(); 
     } 
     catch (SQLException ex) 
     { 
      Logger.getLogger(connDb.class.getName()).log(Level.SEVERE,null, ex); 
     } 
    } 
    finally 
    { 
     try 
     { 
      conn.setAutoCommit(true); 
     } 
     catch (SQLException ex) 
     { 
      Logger.getLogger(connDb.class.getName()).log(Level.SEVERE,null, ex); 
     } 
    } 
} 

如果這一方法之間不同的可變部分是

stmt.executeUpdate("insert into courses values " + values); 

有時是幾個插入,有時候是刪除。 我錯過了使用來自C++的宏,但我確信有一種方法不會爲每種方法重複所有這些事務代碼。

有幫助嗎?

(康涅狄格州和語句是類型java.sql.Connection中和java.sql.Statement中的類成員)

+0

重複?我沒有看到任何重複。每個** catch塊**都做了一項獨特的工作。重複在哪裏? – 2012-01-11 14:43:49

+0

重複的是,有許多方法像上面每個人都有一個不同的'可變部分'上面顯示 – thedrs 2012-01-11 14:46:49

+0

除了排除日誌和事務處理等常見任務的問題之外,考慮使用PreparedStatements而不是字符串。 – Ingo 2012-01-11 14:51:20

回答

6

創建接受一個接口幷包裝起來異常時的處理的方法。

每個接口的匿名(或沒有)執行包含SQL調用,它的參數等

例如(非常粗略地):

public void addCourseToCourses(final String values) { 
    handleSql(new SqlCommand() { 
     @Override public void run(Statement stmt) { 
      stmt.executeUpdate("insert into courses values " + values); 
     } 
    }); 
} 

handleSql是形成類似靜態導入:

public class SqlWrapper { 
    public static void handleSql(SqlCommand cmd) { 
     Connection conn = // get connection; 
     try { 
      conn.setAutoCommit(false); 
     } catch (SQLException e) { 
      LOG.log(Level.SEVERE, null, e); 
      return; 
     } 

     try { 
      cmd.run(); 
      conn.commit(); 
     } catch (SQLException e) { 
      cleanRollback(); 
     } finally { 
      cleanClose(); 
     } 
    } 
} 

可以添加各種掛鉤,因爲看起來合理。通用版本將允許各種返回類型,這可能更合適,只取決於您實際需要的內容。

評論提及RunnableCallable,IMO Runnable是專門針對線程(和基礎接口不是通用的)。 Callable是一個更好的選擇,但我希望能夠添加足夠的其他鉤子來處理特定於應用程序的特定於SQL/JDBC的功能。因人而異。

這種模式已被重新發明到處都是;採用Spring JDBC之類的東西並使用它可能更有意義。

+1

自從我上次用C++編程已經過去了15年,但我確信我記得他們也包含了方法。 – 2012-01-11 14:44:33

+0

該死的,我以爲是C#,我正在寫一個代表的建議。然後我意識到:「呃,問題是關於java的。」 – 2012-01-11 14:48:46

+0

是的,Callable 更好,因爲它可能會證明一個人必須重複某些事情。 – Ingo 2012-01-11 15:43:07

0

爲什麼不那麼就做這樣的事情:

public void addCourseToCourses(String values) 
{ 
    callDB("insert into courses values " + values) 
} 

protected void callDB(String call) 
{ 
    try 
    { 
     conn.setAutoCommit(false); 
    } 
    catch (SQLException e) 
    { 
     Logger.getLogger(connDb.class.getName()).log(Level.SEVERE, null, e); 
     return; 
    } 
    try 
    { 
     stmt.executeUpdate(call); 
     conn.commit(); 
    } 
    catch (SQLException e) 
    { 
     try 
     { 
      conn.rollback(); 
     } 
     catch (SQLException ex) 
     { 
      Logger.getLogger(connDb.class.getName()).log(Level.SEVERE,null, ex); 
     } 
    } 
    finally 
    { 
     try 
     { 
      conn.setAutoCommit(true); 
     } 
     catch (SQLException ex) 
     { 
      Logger.getLogger(connDb.class.getName()).log(Level.SEVERE,null, ex); 
     } 
    } 
} 

這樣,所有的交易相關的代碼是在一個地方,每一個人的方法是隻用於格式化相應的聲明負責。

+1

一個非常糟糕的主意。 SQL漏洞大開放。不,只使用PreparedStatements。 – Ingo 2012-01-11 14:49:37

+0

如果不是更新會怎麼樣?如果他需要'stmt.executeQuery()'或'stmt.execute()'會怎麼樣? – Bohemian 2012-01-11 14:52:10

+0

這種方法不好,因爲可變部分有時包含多個sql動作(例如3個插入和2個刪除) – thedrs 2012-01-11 14:56:38

0

更有意思的方法是創建一個像executeSQL這樣的方法來處理常見的東西,並且只需傳遞sql(String)即可執行。

更好的方法是查看Spring並讓Spring處理事務和消息處理。

2

其實,你不需要一個新的界面。像Runnable或Callable這樣的現有產品可以做得很好。

+0

IMO它不可能只有一個鉤子,而是會有額外的SQL,JDBC或應用程序特定的功能。也許是延長'Callable'的東西,但是meh。 'Runnable'意味着一個單獨的線程(至少對我來說),所以我會小心使用它。 – 2012-01-11 14:58:03

+0

@Dave - 關於Runnable,不,它不。看看它,它只是'void run();'只是因爲人們經常使用Runnable來告訴線程該做什麼並不意味着它是唯一的目的。 – Ingo 2012-01-11 15:24:56

+0

我沒有說這是它的* only *目的,但是Javadocs明確聲明「[[]]由其實例旨在由線程執行的任何類實現」。它*是其*規範*目的。我特別說「至少對我來說」。不管喜不喜歡,「Runnable」都會傳達一些可能無意的東西。 – 2012-01-11 15:33:05

1

您可以使用「回調」的格局,這在Java中使用匿名類完成,像這樣:

創建一個接口來完成實際工作:

interface Exec { 
    exec(PreparedStatement stmt) throws SQLException; 
} 

重構代碼創建方法與接受的Exec的通用代碼:

void perform(Exec exec) { 
    try 
    { 
     conn.setAutoCommit(false); 
    } 
    catch (SQLException e) 
    { 
     Logger.getLogger(connDb.class.getName()).log(Level.SEVERE, null, e); 
     return; 
    } 
    try 
    { 
     exec.exec(stmt); 
     conn.commit(); 
    } 
    catch (SQLException e) 
    { 
     try 
     { 
      conn.rollback(); 
     } 
     catch (SQLException ex) 
     { 
      Logger.getLogger(connDb.class.getName()).log(Level.SEVERE,null, ex); 
     } 
    } 
    finally 
    { 
     try 
     { 
      conn.setAutoCommit(true); 
     } 
     catch (SQLException ex) 
     { 
      Logger.getLogger(connDb.class.getName()).log(Level.SEVERE,null, ex); 
     } 
    } 
} 

重構你的方法是這樣的:

public void addCourseToCourses(final String values) 
{ 
    perform(new Exec() { 
     exec(PreparedStatement stmt) throws SQLException { 
      stmt.executeUpdate("insert into courses values " + values); 
     } 
    }); 
} 
+0

(看起來幾乎完全相同)。如果您沒有準備好聲明,使用準備好的聲明會有好處嗎? – 2012-01-11 16:48:24

+0

@DaveNewton OP的問題也沒有準備好(我也注意到了)。我只是重構他的代碼,這回答了他的直接問題。我沒有嘗試修復代碼。 – Bohemian 2012-01-11 23:33:26

0

使用SQL語句的列表:

function void executeTransaction(List<String> sqlStatements) { 
    // init transaction (as before) 
    try 
    { 
     conn.setAutoCommit(false); 
    } 
    catch (SQLException e) 
    { 
     Logger.getLogger(connDb.class.getName()).log(Level.SEVERE, null, e); 
     return; 
    } 
    try 
    { 
     // execute all statements from transaction 
     for(String statement: sqlStatements) { 
      stmt.executeUpdate("insert into courses values " + values); 
     } 
     // Commit when all succeed 
     conn.commit(); 
    } 
    catch (SQLException e) 
    { 
     try 
     { 
      // rollback on failure (as before) 
      conn.rollback(); 
     } 
     catch (SQLException ex) 
     { 
      Logger.getLogger(connDb.class.getName()).log(Level.SEVERE,null, ex); 
     } 
    } 
    finally 
    { 
     try 
     { 
      // cleanup (as before) 
      conn.setAutoCommit(true); 
     } 
     catch (SQLException ex) 
     { 
      Logger.getLogger(connDb.class.getName()).log(Level.SEVERE,null, ex); 
     } 
    } 
}