怎樣實現關閉connection時自動關閉Statement和ResultSet
轉自:怎樣實現關閉connection時自動關閉Statement和ResultSet
但是, 關閉Statement和ResultSet是乏味的工作.
例如下面的程式碼:
- @Test
- public void testConnection() throw Exception{
- Connection conn = null;
- PreparedStatement ps = null;
- ResultSet rs = null;
- try{
- conn = DsUtil.getConnection();
- ps = conn.prepareStatement("select now()");
- rs = ps.executeQuery();
- //do something
- }finally {
- //DsUtil是一個工具類, 就不貼程式碼了, 你懂的.
- DsUtil.close(rs);
- DsUtil.close(ps);
- DsUtil.close(conn);
- }
- }
上面的程式碼只有一個Statement和一個ResultSet, 關閉一下也不算太麻煩, 所以你可能覺得筆者沒有必要寫這篇文章.
但是, 如果有多個Statement和ResultSet呢?
考慮如下程式碼:
- @Test
- public void testConnection() throws Exception {
- Connection conn = null;
- PreparedStatement psFoo = null;
- ResultSet rsFoo = null;
- PreparedStatement psBar = null;
- ResultSet rsBar = null;
- try {
- conn = DsUtil.getConnection();
- psFoo = conn.prepareStatement("....");
- rsFoo = psFoo.executeQuery();
- psBar = conn.prepareStatement("....");
- rsBar = psBar.executeQuery();
- //do something
- } finally {
- DsUtil.close(rsFoo);
- DsUtil.close(psFoo);
- DsUtil.close(rsBar);
- DsUtil.close(psBar);
- DsUtil.close(conn);
- }
- }
上面的程式碼有兩個statement和result, 關起來就不那麼令人愉快了.
大多數程式設計師肯定是不喜歡關閉多個statement和result的. 我見到過偷懶的程式用這種寫法來規避關閉多個statement的問題.
- @Test
- public void testConnectionNotGood() throws Exception {
- Connection conn = null;
- PreparedStatement psFoo = null;
- ResultSet rsFoo = null;
- try {
- conn = DsUtil.getConnection();
- psFoo = conn.prepareStatement("....");
- rsFoo = psFoo.executeQuery();
- //do something
- psFoo = conn.prepareStatement("....");
- rsFoo = psFoo.executeQuery();
- //do something
- } finally {
- DsUtil.close(rsFoo);
- DsUtil.close(psFoo);
- DsUtil.close(conn);
- }
- }
上面這斷程式碼的不妥之處, 是Statement和ResultSet被重用了. 實際上建立了兩個Statement和Result, 但最後只關閉了一個. 這樣顯然是不對的, 屬於鴕鳥政策, 沒解決實際問題.
那麼, 有沒有辦法實現一個自定義的Connection, 使得程式設計師不需要手動關閉Statement和Result, 並支援Statement/PrepareStatement/CallableStatement呢?
==================分割線========================
簡單地說:
我們希望connection持有statement的軟引用, 而statement又持有resultset的軟引用, 並分別重寫connection和statement的close方法, 在關閉之前先關閉軟引用中的物件.
詳細地說:
1. 建立一個ResultSetStatementAwareConnection.
該自定義Connection會記住所有的Statement/PreparedStatement/CallableStatement例項.
2. 建立一個ResultSetAwareStatement, 記住所有Statement.
3. 建立一個ResultSetAwarePreparedStatement, 記住所有的PreparedStatement.
4. 建立一個ResultSetAwareCallableStatement, 記住所有的CallableStatement.
先說ResultSetStatementAwareConnection的實現.
- class ResultSetStatementAwareConnection implements Connection {
- private Map> openStatements = new ConcurrentHashMap>(3);
- protected Connection underlyingConnection;
- public ResultSetStatementAwareConnection(Connection conn) {
- this.underlyingConnection = conn;
- }
- private void addToOpenStatement(Statement statement) {
- openStatements.put(DsUtil.getIdentityHexString(statement), new SoftReference(statement));
- }
- public void close() throws SQLException {
- try {
- closeOpenStatements();
- } finally {
- closeUnderlyingConnection();
- }
- }
- private void closeOpenStatements() {
- for (Map.Entry> entry : openStatements.entrySet()) {
- DsUtil.close(entry.getValue().get());
- }
- openStatements.clear();
- openStatements = null;
- }
- protected void closeUnderlyingConnection() {
- DsUtil.close(underlyingConnection);
- underlyingConnection = null;
- }
- @Override
- public Statement createStatement() throws SQLException {
- ResultSetAwareStatement statement = ResultSetAwareStatement.decorate(underlyingConnection.createStatement());
- statement.setConnection(this);
- addToOpenStatement(statement);
- return statement;
- }
- @Override
- public PreparedStatement prepareStatement(String sql) throws SQLException {
- ResultSetAwarePreparedStatement statement = ResultSetAwarePreparedStatement.decorate(underlyingConnection.prepareStatement(sql));
- statement.setConnection(this);
- addToOpenStatement(statement);
- return statement;
- }
- @Override
- public CallableStatement prepareCall(String sql) throws SQLException {
- ResultSetAwareCallableStatement statement = ResultSetAwareCallableStatement.decorate(underlyingConnection.prepareCall(sql));
- statement.setConnection(this);
- addToOpenStatement(statement);
- return statement;
- }
- @Override
- public String getCatalog() throws SQLException {
- return underlyingConnection.getCatalog();
- }
- //更多程式碼見附件
- }
通過openStatements持有所有statement的軟引用, 並且close方法中會先呼叫closeOpenStatements把軟引用持有的statement全部關閉, 然後再通過closeUnderlyingConnection去真正關閉連線.
到現在為止, 就只需要關閉資料庫連線, 不需要顯式關閉statement了.
類似地, statement也可以通過這種模式來關閉resultset. 以PreparedStatement為例.
- class ResultSetAwarePreparedStatement implements PreparedStatement {
- private Map> openResultSets = new ConcurrentHashMap>(3);
- private ResultSetStatementAwareConnection connection;
- private PreparedStatement underlyingPreparedStatement;
- static ResultSetAwarePreparedStatement decorate(PreparedStatement statement) {
- ResultSetAwarePreparedStatement instance = new ResultSetAwarePreparedStatement();
- instance.underlyingPreparedStatement = statement;
- return instance;
- }
- private void addToOpenResultSet(ResultSet resultSet) {
- openResultSets.put(DsUtil.getIdentityHexString(resultSet), new SoftReference(resultSet));
- }
- public void close() throws SQLException {
- try {
- closeOpenResultSets();
- } finally {
- closeUnderlyingStatement();
- }
- }
- private void closeOpenResultSets() {
- for (Map.Entry> entry : openResultSets.entrySet()) {
- DsUtil.close(entry.getValue().get());
- }
- openResultSets.clear();
- openResultSets = null;
- }
- private void closeUnderlyingStatement() {
- DsUtil.close(underlyingPreparedStatement);
- connection = null;
- }
- //更多程式碼見附件
- }
通過openResultSets持有resultset的軟引用, 並且close方法中會先呼叫closeOpenResultSets把軟引用持有的resultset全部關閉, 然後再通過closeUnderlyingStatement去真正關閉statement.
到這裡, 所有的事情就完成了. 下面舉個栗子.
- @Test
- public void testConnectionNotGood() throws Exception {
- Connection conn = null;
- try {
- conn = new ResultSetStatementAwareConnection(DsUtil.getConnection());
- PreparedStatement psFoo = conn.prepareStatement("....");
- ResultSet rsFoo = psFoo.executeQuery();
- //do something
- psFoo = conn.prepareStatement("....");
- rsFoo = psFoo.executeQuery();
- //do something
- psFoo = conn.prepareStatement("....");
- rsFoo = psFoo.executeQuery();
- //do something
- } finally {
- DsUtil.close(conn);
- }
- }
我們看到PreparedStatement和ResultSet一共使用了三次, 建立了三個PreparedStatement和三個ResultSet. 但是在finally塊中只顯式關閉了Connection, 並沒有顯式關閉PreparedStatement和ResultSet.
不過放心, 雖然沒有顯式關閉, 但其實三個PrepareStatement和ResultSet都會被自動關閉.
相關文章
- 怎樣關閉win10自動更新 如何關閉windows10自動更新Win10Windows
- 訂單超時自動關閉的實現方案總結
- win10系統關閉自動更新怎麼關_windows10如何關閉自動更新Win10Windows
- 怎麼關閉配置更新_如何關閉win10自動配置更新Win10
- win10自動更新怎麼關閉?怎麼關閉win10系統自動更新Win10
- ubuntu 關閉防火牆命令 ubuntu怎樣關閉防火牆Ubuntu防火牆
- WPS自動更新怎麼關閉? 詳解WPS自動更新的關閉方法步驟
- windows10 自動更新怎麼關閉_關閉windows10 自動更新的方法Windows
- Laravel實現:待付款訂單,超48小時自動關閉Laravel
- win10 wlan自動關閉怎麼辦_win10wlan自動關閉怎麼解決Win10
- 怎麼關閉win10自動更新 筆記本win10怎麼關閉自動更新Win10筆記
- win10 自動重啟關閉方法_win10自動重啟怎麼關閉Win10
- 如何關閉win10的自動更新_關閉自動更新win10怎麼操作Win10
- win10徹底關閉自動更新方法 win10怎樣徹底關閉更新Win10
- win10 自帶安全中心如何關閉_怎樣關閉win10的自帶安全中心Win10
- win10 關閉自動更新方法_win10怎麼永久關閉自動更新Win10
- win10怎麼關閉自動更新 win10關閉自動更新的三種方法Win10
- Linux啟動tomcat後執行shutdown.sh關閉時出現異常:Connection refused (Connection refused)LinuxTomcat
- 怎樣在Mac上安排自動啟動或關閉,技巧來啦~Mac
- 如何關閉Win10自動更新 win10永久關閉自動更新Win10
- win10 維護怎麼關閉_win10自動維護要怎麼關閉Win10
- win10 windows update怎麼關閉 win10 windows update自動更新怎麼關閉Win10Windows
- win10如何關閉127.0.0.1自動代理_win10怎麼完全關閉代理Win10127.0.0.1
- Winform MessageBox訊息彈窗如何實現自動關閉ORM
- 如何關閉Windows自動更新Windows
- WebStorm關閉自動拆疊WebORM
- filebeat自動關閉解決
- 3 啟動和關閉
- Win10怎麼通過組策略關閉自動更新 策略組關閉win10自動更新Win10
- Win10怎麼透過組策略關閉自動更新 策略組關閉win10自動更新Win10
- 電腦自動更新系統怎麼關閉 微軟win10更新怎麼永久關閉微軟Win10
- win10系統如何關閉自動更新驅動 win10怎麼關閉系統自動更新驅動Win10
- win10如何關閉自動喚醒_win10關閉自動喚醒方法Win10
- win10怎麼關閉defender自動掃描_win10關閉defender自動掃描的步驟Win10
- 榮耀手機自動調節亮度怎麼關閉?榮耀手機關閉調節自動亮度教程
- w10怎麼關閉電腦自動更新 win10徹底關閉自動更新方法Win10
- win10怎麼關閉自動關機_win10怎麼解除電腦自動關機Win10
- iOS 關閉系統自動更新iOS
- win10 怎樣關閉防火牆 win10 誤殺軟體如何關閉自帶防火牆Win10防火牆