2016-01-21 117 views
0

我正在研究一個項目,我正在做很多查詢,並且時間是一個考慮事項,所以我想嘗試並實現JDBC多線程。我不確定正確的方法是什麼。使用JNDI正確實現JDBC多線程

這是我的第一個草案實施:

春數據源的Bean:

private DataSource ds; 
@Resource(name="jdbc") 
public void setDataSource(DataSource ds) { 
    this.ds = ds; 
} 

初始化方法:

public void checkUsersMulti(List<User> users) throws Exception { 
    if(users!= null || users.size() != 0) { 
     ExecutorService executorService = Executors.newFixedThreadPool(20); 
     Queue<User> queue = new ConcurrentLinkedQueue<>(); 
     queue.addAll(users); 
     for (Useruser: users) { 
      executorService.submit(new ProcessUser(queue)); 
     } 
     executorService.shutdown(); 
     try { 
      executorService.awaitTermination(1, TimeUnit.HOURS); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 

    } 
} 

運行的類:

class ProcessUser implements Runnable { 
    private final Queue<User> queue; 

    public ProcessUser(Queue<User> queue) { 
     this.queue = queue; 
    } 

    public void run() { 
     try { 
      Connection conn = ds.getConnection(); 
      User user = null; 
      while((user = queue.poll()) != null) { 
       userDao.getUser(user , conn)); 
      } 
      DbUtils.closeQuietly(conn); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
    } 
} 

用戶DAO方法:

public User retrieveUser(User user, Connection conn) { 
    PreparedStatement st = null; 
    ResultSet rs = null; 
    try { 
     String sql = "SELECT firstname, lastname FROM users WHERE id= ?; 

     st = conn.prepareStatement(sql); 
     st.setString(1, user.getId()); 
     rs = st.executeQuery(); 

     while(rs.next()) { 
      user.setFirstName(rs.getString("firstname")); 
      user.setLastName(rs.getString("lastname")); 
     } 
    } catch (Exception e) { 
     return null; 
    } 
    finally { 
     DbUtils.closeQuietly(rs); 
     DbUtils.closeQuietly(st); 
    } 
    return user; 
} 

UPDATE:Tomcat的JNDI連接池設置:

<Resource 
    name="jdbc" 
    auth="Container" 
    type="javax.sql.DataSource" 
    maxTotal ="25" 
    maxIdle="30" 
    maxWaitMillis ="10000" 
    driverClassName="***Vendor Driver***" 
    url="***Valid URL" 
    username="***Valid Username***" 
    password="***Valid Password***" 
/> 

不過,我得到這個錯誤偶爾:

java.sql.SQLException: Cannot get a connection, pool error Timeout waiting for idle object

但是,該過程仍按預期完成 - 可能在我的JNDI設置中。我更關心的是,如果這是實現這一目標的「正確」方式。我認爲最好保持連接對象,這樣就不必重新初始化。

+0

'users'的大小是多少?任何大小的「用戶」都會發生問題嗎? –

+0

我的數據集現在約爲200,我會嘗試擴展並收縮它以查看行爲是什麼 - 我實施了@ rmalchow的建議,但我很好奇它的行爲。 –

回答

0

有一對夫婦的這段代碼的問題,最重要的是,我不知道這是什麼:

public void checkUsersMulti(List<User> users) throws Exception { 
    if(users!= null || users.size() != 0) { 
     ExecutorService executorService = Executors.newFixedThreadPool(20); 
     Queue<User> queue = new ConcurrentLinkedQueue<>(); 
     queue.addAll(users); 
     for (Useruser: users) { 
      executorService.submit(new ProcessUser(queue)); 
     } 
     executorService.shutdown(); 
     try { 
      executorService.awaitTermination(1, TimeUnit.HOURS); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 

    } 
} 

是應該做的。如果我理解正確,就可以創建一個所有用戶在其中的隊列,然後將其提交給執行者的次數與有用戶一樣多。這將意味着:

  • 你可以處理用戶的數量方面受到限制,因爲隊列不能無限增長(這是什麼@sabit汗大概是擔心)

  • 您必須爲每個用戶至少運行一次runnable,但除非某些runnable由於錯誤而退出,否則前20個將繼續運行,直到隊列爲空,然後剩餘的將被調用,請參閱隊列爲空,並立即退出。

您應該可能限制您正在啓動的並行進程的數量。你有一個20的線程池,所以如果你正確地關閉你的連接,那麼永遠不應該有超過20個併發連接。你不提任何其他異常,但這:

try { 
     Connection conn = ds.getConnection(); 
     User user = null; 
     while((user = queue.poll()) != null) { 
      userDao.getUser(user , conn)); 
     } 
     DbUtils.closeQuietly(conn); 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } 

大概應該是:

Connection conn = null; 
    try { 
     conn = ds.getConnection(); 
     User user = null; 
     while((user = queue.poll()) != null) { 
      userDao.getUser(user , conn)); 
     } 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } finally { 
     DbUtils.closeQuietly(conn); 
    } 

只是爲了確保連接在任何情況下關閉。

此外,由於您顯然使用的是底層連接池,因此您可能需要嘗試每次獲取並返回連接。

至於你的實際問題:你這樣做的方式意味着前20個runnables試圖獲得連接,然後持有它們很長一段時間。如果你的池被配置爲使用比這個更少的連接(比如15),那麼這很容易發生,因爲前15個連接成功,然後接下來的5個連續運行一個runnable,try,fail,exit 。或者您在游泳池中使用了20個連接,您可能會因爲沒有按照上述說明正確關閉連接而泄漏連接。

+0

所以通過做**(用戶用戶:用戶)**,我實際上創造了很多線程......其中20個實際上做了一些事情。所以我已經將它改爲**(int i = 0; i <20; i ++)**。我按照你的建議更新到最後。持有連接的問題是他們可能會超時並失敗? –

+0

我想要更直接,堅持下去有什麼優點和缺點?我認爲必須返回到數據源才能獲得連接的開銷稍微小一點......但我不知道可能會導致您陷入什麼樣的不利情況,或者爲什麼建議將其返回。 –

+0

持續時間不是一個大問題,但正如@ kiresays所寫的,沒有太多的開銷。 – rmalchow