2010-08-02 19 views
4

首先,我是Java新手。最佳實踐:在Java中使用數據庫

我想弄清楚什麼是使用數據庫從Java工作的好方法。我正在使用c3p0進行連接池。 Hibernate或其他ORM這次不是一個選項,我們現在決定堅持使用「純SQL」。

數據目前基本檢索是這樣的:

private int getUserID(int sessionID, String userIP) { 
int result = 0; 
Connection conn = null; 
PreparedStatement st = null; 
ResultSet rs = null; 
try { 
    // Application.cpds is an instance of c3p0's ComboPooledDataSource 
    conn = Application.cpds.getConnection(); 
    st = conn.prepareStatement("SELECT user_id, user_ip, is_timed_out FROM g_user.user_session WHERE id = ?"); 
    st.setInt(1, sessionID); 
    rs = st.executeQuery(); 
    if (rs.next()) { 
    if (!rs.getBoolean("is_timed_out") && userIP.equals(rs.getString("user_ip"))) { 
    result = rs.getInt("user_id"); 
    } 
    } 
} 
catch (SQLException e) { 
    e.printStackTrace(); 
} 
finally { 
    if (rs != null) { 
    try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } 
    } 
    if (st != null) { 
    try { st.close(); } catch (SQLException e) { e.printStackTrace(); } 
    } 
    if (conn != null) { 
    try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } 
    } 
} 
return result; 
} 

代碼看起來這樣的基本操作很長。另一個問題是大多數代碼必須在許多地方重複(聲明Connection,PreparedStatement,ResultSet,關閉它們,捕獲異常)。雖然,這是我在大多數谷歌搜索示例中看到的。

在PHP中,我將創建一個包裝類,該類將接受2個參數(字符串)sqlQuery和(數組)參數的方法select(),並返回簡單的數據數組。包裝類也有一些更具體的方法,如:

  • selectValue()單值(例如,select count(*) from user
  • selectRow()爲單行(例如,select name, surname from user where id = :user_id
  • selectColumn單柱(例如,select distinct remote_address from user

這是類似於Java中實踐的東西嗎?或者有什麼更好/更方便嗎?或者我應該使用與上面getUserID()示例中相同的樣式嗎?正如我所說,這次ORM不是一種選擇。

感謝提前:)


編輯:目前DBConnection類被寫入。它從構造函數中的c3p0連接池獲取連接。它具有與DB工作的幾個公共方法:select()表格數據,selectValue()單值,selectRow()selectColumn()單行或列,以及insert()update()delete()ddl()。方法接受String query, Object[] params參數,其中params是可選的。 insert()update()delete()返回Integer這是PreparedStatement.executeUpdate()的結果。 select方法返回不同的結果:

  • ArrayCollection<HashMap<String, Object>> select()
  • Object selectValue()
  • HashMap<String, Object> selectRow()
  • ArrayCollection<Object> selectColumn()

最後一個問題是編譯器警告 - "warning: [unchecked] unchecked cast"。這是因爲所有方法都會調用返回Object的單個私有方法,並將其結果轉換爲提及的類型。由於我是Java新手,我也不確定是否爲選擇選擇了適當的類型。除此之外,一切似乎都按預期工作。

回答

5

如果一個ORM是沒辦法,你仍然可以使用Spring的JDBC助手類: http://docs.spring.io/spring-framework/docs/4.1.0.RELEASE/spring-framework-reference/html/jdbc.html

或者你可以簡單地寫上自己的一些輔助方法。也許DBUtil.close(conn, st, rs);會很好。

順便說一句,你真的應該使用日誌框架,而不是「e.printStackTrace()

編輯: 一兩件事:我認爲這是一種很難添加ORM之後,當你把所有的SQL已經用純JDBC編寫。你不能重構那些東西,你必須扔掉它並重新做。

編輯: 如果您正在關閉語句,則不必關閉resultSet。所述Java ResultSet API讀取:

一個結果對象是自動 關閉時的聲明對象 產生它被關閉時,重新執行, 或用來從多個結果的序列檢索下一個結果 。

除此之外,C3P0也進行資源管理,並在返回連接時關閉語句。你也可以看看那個。

+0

關於伐木 - 是的,我應該。還沒有任何時間去研究它,但我有我的待辦事項清單;) – binaryLV 2010-08-02 10:32:36

+0

這是一個nobrainer,如果你只是抓住log4j。應該只花兩到三個小時來理解概念並將其整合:http://logging.apache.org/log4j/ – 2010-08-02 10:34:20

+0

是的,我知道它不應該太困難,但它不在我的待辦事項列表。使用DB,Red5「共享對象」和流媒體的基礎知識具有更高的優先級。當學到這些東西的時候,我會看看日誌記錄。寫作應用程序將不會開始,直到那時(現在我只是寫一些測試代碼,看看事情是如何工作的),所以日誌可以稍後添加,沒有任何問題。 – binaryLV 2010-08-02 10:48:02

0

「這次Hibernate或其他ORM不是一個選項,我們現在決定堅持使用」純SQL「。」 出於好奇,堅持使用純SQL的原因是什麼?看看你提到的例子和問題,第一個明顯的答案就是使用ORM,而且不用擔心 - 在大多數情況下,標準的ORM特性列表就足夠了。

顯然有很多理由不使用ORM的,所以我對你感興趣?

+0

我們的2個編程人員團隊從來沒有從事過Java和ORM的工作,現在Java將成爲「輔助服務器」,所以我們不會花費太多時間研究我們不熟悉的東西。但是,如果Java有一天會成爲「主服務器」,我們最終會考慮ORM。 – binaryLV 2010-08-02 10:30:51

+0

根據我的經驗,不需要太多時間就可以瞭解JPA,比如幾年前從JDBC遷移出來的更喜歡JPA,尤其是在您瞭解SQL的情況下。這顯然只是從我的角度來看,但我可以說,從長遠來看,它可以爲您節省很多未來的問題並加速發展。 – Greg 2010-08-02 10:38:51

+1

順便說一句,有一件令我們擔心的ORM問題是它能夠替換一些複雜的查詢,例如遞歸查詢。 – binaryLV 2010-08-02 14:36:25

0

我認爲粒度級別始終是開發人員的決定。我的意思是,有這麼多的例外和驗證的好處是,你可以捕捉到具體的錯誤,並根據它來採取行動,但是如果你需要的不需要這種強壯程度,並且正如你所顯示的那樣只是打印出棧跟蹤,我認爲一個包裝方法可以在你的情況下有用。

2

爲了避免重複的代碼,也許使事情變得更簡單。 你可以做的是創建一個數據庫類。

在該類中,您可以創建用於訪問數據庫的通用方法。 例如。

如果類被稱爲DBManager.java然後裏面 創建方法

private connect() 
public boolean update() 
public ResultSet query() 

原因連接方法是顯而易見的,你用它獲取您的連接。由於它是私有的,所以在DBManager的構造函數中調用它。

然後,您可以使用update()方法來執行SQL插入,更新,刪除等操作,基本上任何不返回任何數據的SQL操作,除了可能的成功狀態更新方法。

當您要執行選擇查詢時使用您的查詢方法。您可以返回結果集,然後遍歷調用方法/類中的結果。

您如何處理異常取決於您。您可能更好地處理DBManager類中的異常,這種方式您不必在您查詢的各個類中處理它們。

所以不是

public ResultSet query() Throws SQLException{ 

就像你在你的例子做了上面你可以使用一個嘗試捕捉查詢方法中。 在dbmanager類中處理它的一個明顯的優點是你不必擔心所有其他使用你的sql連接的類。

希望這是有幫助的

響應您的評論:

它取決於你回報什麼,ResultSet中是回報只是一個想法,但也許它會是最好返回的一些集合排序而不是數組,也許?取決於你需要什麼。結果集不需要關閉。

public ResultSet query(String strSql) { 

     try { 
      Statement tmpStatement = connection.createStatement(); 
      ResultSet resultSet = tmpStatement.executeQuery(strSql); 
      return resultSet; 
     } catch (java.sql.SQLException ex) { 
      //handle exception here 
      return null; 
     } 
    } 

那麼你的更新可以像這樣

public boolean updateSql(String strSQL) { 
     try { 
      Statement tmpStatement = connection.createStatement(); 
      tmpStatement.executeUpdate(strSQL); 
      return true; 
     } catch (java.sql.SQLException ex) { 
      //handle exception 
      return false; 
     } 
} 

呃,你就可以用你的查詢方法,像這樣

ResultSet r = query(sql); 

     try { 

      while (r.next()) { 
       someVar[i] = r.getString("columnName");   
      } 
     } catch (SomeException ex) { 
      //handle exception etc 
     } 

但話又說回來像你說的,而不是返回結果設置你可以改變查詢方法將結果複製到數組或集合,然後返回集合並關閉語句

tmpStatement.close(); 

但是,當一個Statement對象關閉,其當前的ResultSet對象,如果存在的話,也被關閉。(從API文檔)

其良好的做法來釋放數據庫資源,儘快使複製你的結果到一個集合對象,然後關閉你的聲明可能是最好的。再次它取決於你。

+0

這就是我在PHP中所做的 - 我創建了類似的類。然而,我擔心的是你建議由於query()返回ResultSet - 是不是某種數組會更好?我相信我會得到很多異常(而不是結果),因爲在開始使用ResultSet之前語句會被關閉...... – binaryLV 2010-08-02 10:58:33

+0

編輯了我的原始答案以迴應您的評論。在這裏沒有回覆,因爲我想使用代碼格式。 :-) – zcourts 2010-08-04 02:58:52

+0

我的當前實現'select()'返回'ArrayCollection >',其中'HashMap '保存每行信息(列名及其值)張貼更多。離開語句「打開」(即創建它們而不調用'.close()')似乎是非常錯誤的,所以ResultSet將不能在函數外部使用。即使聲明不會被關閉,它仍然會在某個時間點被垃圾回收,並且ResultSet將被破壞。 – binaryLV 2010-08-04 11:08:21