2012-02-25 205 views
3

我在寫一個servlet,通過訪問和修改數據庫中的某些表來處理每個請求。我希望與數據庫的連接是線程安全的。我不想使用已有的庫/框架(spring,hibernate等)。Java線程安全數據庫連接

我知道我可以通過以下方式使用Java的ThreadLocal的這個:

public class DatabaseRegistry { //assume it's a singleton 


private Properties prop = new Properties(); 
    public static final ThreadLocal<Connection> threadConnection = new ThreadLocal<Connection>(); 

    private Connection connect() throws SQLException { 
     try { 
      // This will load the MySQL driver, each DB has its own driver 
      Class.forName("com.mysql.jdbc.Driver"); 
      // Setup the connection with the DB 
      Connection connection = DriverManager 
        .getConnection("jdbc:mysql://" + prop.getProperty("hostname") + "/" + prop.getProperty("database") + "?" 
          + "user=" + prop.getProperty("username") + "&password=" + prop.getProperty("password")); 
      return connection; 
     } catch (SQLException e) {   
      throw e; 
     } catch (ClassNotFoundException e) { 
      e.printStackTrace(); 
     } 

     return null; 

    } 

    public Connection getConnection() throws SQLException { 

     if(threadConnection.get() == null) { 
      Connection connection = connect(); 
      threadConnection.set(connection); 
      return threadConnection.get(); 
     } else 
      return threadConnection.get(); 
    } 

    private void freeConnection(Connection connection) throws SQLException { 
     connection.close(); 
     threadConnection.remove(); 
    } 
} 

每次調用的getConnection(),新的連接被添加到ThreadLocal的對象,然後當你釋放連接刪除。

這是這樣做的正確方法還是應該DatabaseRegistry本身擴展ThreadLocal類?還是有更好的方法來做到這一點,使所有連接線程安全?

謝謝

+0

http://stackoverflow.com/questions/1209693/is-mysql-connector-jdbc-thread-safe – 2012-02-25 16:11:43

+0

我認爲這不是一個好的做法。請使用連接池,它將保留可用連接的核心大小。如果你使用ThreadLocal,每個請求都將是自己的一個連接,如果你的web服務器是阻塞的,連接將不會按時發佈。 – 2015-03-20 02:26:17

回答

2

我不認爲這使得數據庫連接線程安全是一種常見的做法。通常你想要的是兩種:

  • 序列化訪問你的servlet的某些部分,以便有在同一時間(如實施SingleThreadModel接口)不超過一個的servlet中執行代碼。
  • 鎖定特定的表/頁面/行,以便您可以對某些特定的元組(通過更改數據庫隔離級別)進行操作。
  • 使用樂觀鎖定來檢測表中的修改行(使用表的某些引用屬性來檢查當前版本是否與表中的行相同)。

據我所知,典型的使用ThreadLocal的是存儲每個線程唯一的數據庫連接,讓相同的連接可以用不同的方法來使用你的業務邏輯,而不需要每次都將它作爲一個參數。由於常見的servlet容器實現使用線程來填充HTTP請求,因此兩個不同的請求將保證使用兩個不同的數據庫連接。

+0

您最近的評論適用於此。我通過單個請求使用連接。每個請求都可以通過很多調用來訪問需要連接的功能。例如,我將使用在URL中傳遞的ID通過Finder類在數據庫中查找域對象。然後,我將編輯該對象並通過其他類將其更新到數據庫中。雖然我每次都關閉結果集,但我希望連接是相同的。 – 2012-02-25 04:39:09

+0

我仍然認爲這是在重塑車輪。如果要在容器中運行servlet(如Tomcat),請使用JNDI查找數據源並將數據源配置爲爲您建立池連接。那裏有很多例子。 – dbrin 2012-02-25 05:38:10

+0

是不是什麼SQL DriverManager當你做getConnection(); – 2012-02-25 05:47:17

0

我不確定爲什麼你想要你的數據庫連接是線程安全的。大多數時間建立與數據庫的連接是交易中最長的部分。通常連接在請求和開放連接池之間重複使用(通過框架或更典型的是應用程序服務器)。

如果你擔心併發修改到同一個表,你可能想看看在同步方法:http://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html

+1

-1,使用Java同步來保護數據庫事務不發生碰撞是一個**糟糕的**想法 – 2012-02-25 04:28:31

+0

我不會在一個普通的應用程序中這樣做,但它聽起來像這是某種小型的項目,他想要的東西不使用框架並且想要在內部管理連接池。在這種情況下,這不是一個壞主意。是的,你會等待其他請求,但它會完成骯髒的工作。 – dbrin 2012-02-25 05:44:22

+0

這樣做「正確的方式」並不是那麼難,而且這教導了可怕的習慣。它還留下了大量的代碼債務,使得幾乎不可能擴大應用範圍。 – 2012-02-25 18:55:54

2

我知道你說你不想用庫來做到這一點,但如果你這樣做,你會變得更好。選擇一個標準的連接池(C3P0,DBCP,或其他),你會比你自己烘烤更快樂。爲什麼你不能用圖書館來做到這一點?