JAVA Database Connectivity java 數據庫鏈接.java
JDBC(Java DataBase Connectivity,java數據庫鏈接)是一種用於執行SQL語句的Java API,能夠爲多種關係數據庫提供統一訪問,它由一組用Java語言編寫的類和接口組成。JDBC提供了一種基準,據此能夠構建更高級的工具和接口,使數據庫開發人員可以編寫數據庫應用程序,同時,JDBC也是個商標名。mysql
SUN公司提供的一種數據庫訪問規則、規範, 因爲數據庫種類較多,而且java語言使用比較普遍,sun公司就提供了一種規範,讓其餘的數據庫提供商去實現底層的訪問規則。 咱們的java程序只要使用sun公司提供的jdbc驅動便可。sql
咱們安裝好數據庫以後,咱們的應用程序也是不能直接使用數據庫的,必需要經過相應的數據庫驅動程序,經過驅動程序去和數據庫打交道。其實也就是數據庫廠商的JDBC接口實現,即對Connection等接口的實現類的jar文件。
數據庫
1.Driver接口編程
Driver接口由數據庫廠家提供,做爲java開發人員,只須要使用Driver接口就能夠了。在編程中要鏈接數據庫,必須先裝載特定廠商的數據庫驅動程序,不一樣的數據庫有不一樣的裝載方法。如:緩存
裝載MySql驅動:Class.forName("com.mysql.jdbc.Driver");安全
裝載Oracle驅動:Class.forName("oracle.jdbc.driver.OracleDriver");性能優化
2.Connection接口服務器
Connection與特定數據庫的鏈接(會話),在鏈接上下文中執行sql語句並返回結果。DriverManager.getConnection(url, user, password)方法創建在JDBC URL中定義的數據庫Connection鏈接上。微信
鏈接MySql數據庫:Connection conn = DriverManager.getConnection("jdbc:mysql://host:port/database", "user", "password");
鏈接Oracle數據庫:Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@host:port:database", "user", "password");
鏈接SqlServer數據庫:Connection conn = DriverManager.getConnection("jdbc:microsoft:sqlserver://host:port; DatabaseName=database", "user", "password");
經常使用方法:
3.Statement接口
用於執行靜態SQL語句並返回它所生成結果的對象。
三種Statement類:
經常使用Statement方法:
4.ResultSet接口
ResultSet提供檢索不一樣類型字段的方法,經常使用的有:
ResultSet還提供了對結果集進行滾動的方法:
使用後依次關閉對象及鏈接:ResultSet → Statement → Connection
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
//DriverManager.getConnection("jdbc:mysql://localhost/test?user=SIHAI&password=SIHAI"); //2. 創建鏈接 參數一: 協議 + 訪問的數據庫 , 參數二: 用戶名 , 參數三: 密碼。 conn = DriverManager.getConnection("jdbc:mysql://localhost/student", "root", "root");
//3. 建立statement , 跟數據庫打交道,必定須要這個對象 st = conn.createStatement();
//4. 執行查詢 , 獲得結果集 String sql = "select * from t_stu"; rs = st.executeQuery(sql);
//5. 遍歷查詢每一條記錄 while(rs.next()){ int id = rs.getInt("id"); String name = rs.getString("name"); int age = rs.getInt("age"); System.out.println("id="+id + "===name="+name+"==age="+age); }
if (rs != null) { try { rs.close(); } catch (SQLException sqlEx) { } // ignore rs = null; }
/** * 釋放資源 * @param conn * @param st * @param rs */ public static void release(Connection conn , Statement st , ResultSet rs){ closeRs(rs); closeSt(st); closeConn(conn); } private static void closeRs(ResultSet rs){ try { if(rs != null){ rs.close(); } } catch (SQLException e) { e.printStackTrace(); }finally{ rs = null; } } private static void closeSt(Statement st){ try { if(st != null){ st.close(); } } catch (SQLException e) { e.printStackTrace(); }finally{ st = null; } } private static void closeConn(Connection conn){ try { if(conn != null){ conn.close(); } } catch (SQLException e) { e.printStackTrace(); }finally{ conn = null; } }
/** * 獲取鏈接對象 * @return */ public static Connection getConn(){ Connection conn = null; try { Class.forName(driverClass); //靜態代碼塊 ---> 類加載了,就執行。 java.sql.DriverManager.registerDriver(new Driver()); //DriverManager.registerDriver(new com.mysql.jdbc.Driver()); //DriverManager.getConnection("jdbc:mysql://localhost/test?user=monty&password=greatsqldb"); //2. 創建鏈接 參數一: 協議 + 訪問的數據庫 , 參數二: 用戶名 , 參數三: 密碼。 conn = DriverManager.getConnection(url, name, password); } catch (Exception e) { e.printStackTrace(); } return conn; }
driverClass=com.mysql.jdbc.Driver url=jdbc:mysql://localhost/student name=root password=root
static{ try { //1. 建立一個屬性配置對象 Properties properties = new Properties(); InputStream is = new FileInputStream("jdbc.properties"); //對應文件位於工程根目錄 //使用類加載器,去讀取src底下的資源文件。 後面在servlet //對應文件位於src目錄底下 //InputStream is = JDBCUtil.class.getClassLoader().getResourceAsStream("jdbc.properties"); //導入輸入流。 properties.load(is); //讀取屬性 driverClass = properties.getProperty("driverClass"); url = properties.getProperty("url"); name = properties.getProperty("name"); password = properties.getProperty("password"); } catch (Exception e) { e.printStackTrace(); } }
源代碼以下:
public class JDBCUtil { static String driverClass = null; static String url = null; static String name = null; static String password= null; static{ try { //1. 建立一個屬性配置對象 Properties properties = new Properties(); InputStream is = new FileInputStream("jdbc.properties"); //使用類加載器,去讀取src底下的資源文件。 後面在servlet // InputStream is = JDBCUtil.class.getClassLoader().getResourceAsStream("jdbc.properties"); //導入輸入流。 properties.load(is); //讀取屬性 driverClass = properties.getProperty("driverClass"); url = properties.getProperty("url"); name = properties.getProperty("name"); password = properties.getProperty("password"); } catch (Exception e) { e.printStackTrace(); } } /** * 獲取鏈接對象 * @return */ public static Connection getConn(){ Connection conn = null; try { Class.forName(driverClass); //靜態代碼塊 ---> 類加載了,就執行。 java.sql.DriverManager.registerDriver(new Driver()); //DriverManager.registerDriver(new com.mysql.jdbc.Driver()); //DriverManager.getConnection("jdbc:mysql://localhost/test?user=monty&password=greatsqldb"); //2. 創建鏈接 參數一: 協議 + 訪問的數據庫 , 參數二: 用戶名 , 參數三: 密碼。 conn = DriverManager.getConnection(url, name, password); } catch (Exception e) { e.printStackTrace(); } return conn; } /** * 釋放資源 * @param conn * @param st * @param rs */ public static void release(Connection conn , Statement st , ResultSet rs){ closeRs(rs); closeSt(st); closeConn(conn); } private static void closeRs(ResultSet rs){ try { if(rs != null){ rs.close(); } } catch (SQLException e) { e.printStackTrace(); }finally{ rs = null; } } private static void closeSt(Statement st){ try { if(st != null){ st.close(); } } catch (SQLException e) { e.printStackTrace(); }finally{ st = null; } } private static void closeConn(Connection conn){ try { if(conn != null){ conn.close(); } } catch (SQLException e) { e.printStackTrace(); }finally{ conn = null; } } }
INSERT INTO t_stu (NAME , age) VALUES ('wangqiang',28) INSERT INTO t_stu VALUES (NULL,'wangqiang2',28)
// 1. 獲取鏈接對象 conn = JDBCUtil.getConn(); // 2. 根據鏈接對象,獲得statement st = conn.createStatement(); //3. 執行添加 String sql = "insert into t_stu values(null , 'aobama' , 59)"; //影響的行數, ,若是大於0 代表操做成功。 不然失敗 int result = st.executeUpdate(sql); if(result >0 ){ System.out.println("添加成功"); }else{ System.out.println("添加失敗"); }
DELETE FROM t_stu WHERE id = 6
// 1. 獲取鏈接對象 conn = JDBCUtil.getConn(); // 2. 根據鏈接對象,獲得statement st = conn.createStatement(); //3. 執行添加 String sql = "delete from t_stu where name='aobama'"; //影響的行數, ,若是大於0 代表操做成功。 不然失敗 int result = st.executeUpdate(sql); if(result >0 ){ System.out.println("刪除成功"); }else{ System.out.println("刪除失敗"); }
SELECT * FROM t_stu
// 1. 獲取鏈接對象 conn = JDBCUtil.getConn(); // 2. 根據鏈接對象,獲得statement st = conn.createStatement(); // 3. 執行sql語句,返回ResultSet String sql = "select * from t_stu"; rs = st.executeQuery(sql); // 4. 遍歷結果集 while (rs.next()) { String name = rs.getString("name"); int age = rs.getInt("age"); System.out.println(name + " " + age); }
UPDATE t_stu SET age = 38 WHERE id = 1;
// 1. 獲取鏈接對象 conn = JDBCUtil.getConn(); // 2. 根據鏈接對象,獲得statement st = conn.createStatement(); //3. 執行添加 String sql = "update t_stu set age = 26 where name ='qyq'"; //影響的行數, ,若是大於0 代表操做成功。 不然失敗 int result = st.executeUpdate(sql); if(result >0 ){ System.out.println("更新成功"); }else{ System.out.println("更新失敗"); }
這個命名不必定須要這樣,可是這樣的命名更容易懂得測試的意思,因此建議命名見名知意。
右鍵工程 --- add Library --- Junit --- Junit4
/** * 使用junit執行單元測試 */ public class TestDemo { @Test public void testQuery() { // 查詢 Connection conn = null; Statement st = null; ResultSet rs = null; try { // 1. 獲取鏈接對象 conn = JDBCUtil.getConn(); // 2. 根據鏈接對象,獲得statement st = conn.createStatement(); // 3. 執行sql語句,返回ResultSet String sql = "select * from t_stu"; rs = st.executeQuery(sql); // 4. 遍歷結果集 while (rs.next()) { String name = rs.getString("name"); int age = rs.getInt("age"); System.out.println(name + " " + age); } } catch (Exception e) { e.printStackTrace(); } finally { JDBCUtil.release(conn, st, rs); } } @Test public void testInsert(){ // 查詢 Connection conn = null; Statement st = null; try { // 1. 獲取鏈接對象 conn = JDBCUtil.getConn(); // 2. 根據鏈接對象,獲得statement st = conn.createStatement(); //3. 執行添加 String sql = "insert into t_stu values(null , 'aobama' , 59)"; //影響的行數, ,若是大於0 代表操做成功。 不然失敗 int result = st.executeUpdate(sql); if(result >0 ){ System.out.println("添加成功"); }else{ System.out.println("添加失敗"); } } catch (Exception e) { e.printStackTrace(); }finally{ JDBCUtil.release(conn, st); } } @Test public void testDelete(){ // 查詢 Connection conn = null; Statement st = null; try { // 1. 獲取鏈接對象 conn = JDBCUtil.getConn(); // 2. 根據鏈接對象,獲得statement st = conn.createStatement(); //3. 執行添加 String sql = "delete from t_stu where name='aobama'"; //影響的行數, ,若是大於0 代表操做成功。 不然失敗 int result = st.executeUpdate(sql); if(result >0 ){ System.out.println("刪除成功"); }else{ System.out.println("刪除失敗"); } } catch (Exception e) { e.printStackTrace(); }finally{ JDBCUtil.release(conn, st); } } @Test public void testUpdate(){ // 查詢 Connection conn = null; Statement st = null; try { // 1. 獲取鏈接對象 conn = JDBCUtil.getConn(); // 2. 根據鏈接對象,獲得statement st = conn.createStatement(); //3. 執行添加 String sql = "update t_stu set age = 26 where name ='qyq'"; //影響的行數, ,若是大於0 代表操做成功。 不然失敗 int result = st.executeUpdate(sql); if(result >0 ){ System.out.println("更新成功"); }else{ System.out.println("更新失敗"); } } catch (Exception e) { e.printStackTrace(); }finally{ JDBCUtil.release(conn, st); } } }
Data Access Object 數據訪問對象
DAO(Data Access Object) 數據訪問對象是一個面向對象的數據庫接口,它顯露了 Microsoft Jet 數據庫引擎(由 Microsoft Access 所使用),並容許 Visual Basic 開發者經過 ODBC 像直接鏈接到其餘數據庫同樣,直接鏈接到 Access 表。DAO 最適用於單系統應用程序或小範圍本地分佈使用。
/** * 定義操做數據庫的方法 */ public interface UserDao { /** * 查詢全部 */ void findAll(); }
public class UserDaoImpl implements UserDao{ @Override public void findAll() { Connection conn = null; Statement st = null; ResultSet rs = null; try { //1. 獲取鏈接對象 conn = JDBCUtil.getConn(); //2. 建立statement對象 st = conn.createStatement(); String sql = "select * from t_user"; rs = st.executeQuery(sql); while(rs.next()){ String userName = rs.getString("username"); String password = rs.getString("password"); System.out.println(userName+"="+password); } } catch (Exception e) { e.printStackTrace(); }finally { JDBCUtil.release(conn, st, rs); } } }
@Test public void testFindAll(){ UserDao dao = new UserDaoImpl(); dao.findAll(); }
String sql = "select * from t_user where username='"+ username +"' and password='"+ password +"'"; UserDao dao = new UserDaoImpl(); dao.login("admin", "100234khsdf88' or '1=1"); SELECT * FROM t_user WHERE username='admin' AND PASSWORD='100234khsdf88' or '1=1' //前面先拼接sql語句, 若是變量裏面帶有了 數據庫的關鍵字,那麼一併認爲是關鍵字。 不認爲是普通的字符串。 rs = st.executeQuery(sql);
該對象就是替換前面的statement對象。
String sql = "insert into t_user values(null , ? , ?)"; ps = conn.prepareStatement(sql); //給佔位符賦值 從左到右數過來,1 表明第一個問號, 永遠你是1開始。 ps.setString(1, userName); ps.setString(2, password);
(1) 使用PreparedStatement,代碼的可讀性和可維護性比Statement高。
(2) PreparedStatement 能最大可能提升性能。
DBServer會對預編譯語句提供性能優化。由於預編譯語句有可能被重複調用,因此語句在被DBServer的編譯器編譯後的執行代碼被緩存下來,那麼下次調用時只要是相同的預編譯語句就不須要編譯,只要將參數直接傳入編譯過的語句執行代碼中就會獲得執行。
在statement語句中,即便是相同操做但由於數據內容不同,因此整個語句自己不能匹配,沒有緩存語句的意義。事實是沒有數據庫會對普通語句編譯後的執行代碼緩存。這樣每執行一次都要對傳入的語句編譯一次。
(3) PreparedStatement能保證安全性,但 Statement有sql注入等安全問題。
1. 概述
在數據庫中,所謂事務是指一組邏輯操做單元,使數據從一種狀態變換到另外一種狀態。
爲確保數據庫中數據的一致性,數據的操縱應當是離散的成組的邏輯單元:當它所有完成時,數據的一致性能夠保持,而當這個單元中的一部分操做失敗,整個事務應所有視爲錯誤,全部從起始點之後的操做應所有回退到開始狀態。
事務的操做:先定義開始一個事務,而後對數據做修改操做,這時若是提交(COMMIT),這些修改就永久地保存下來,若是回退(ROLLBACK),數據庫管理系統將放棄您所做的全部修改而回到開始事務時的狀態。
2. 事務的ACID屬性
2.1 原子性(Atomicity)
原子性是指事務是一個不可分割的工做單位,事務中的操做要麼都發生,要麼都不發生。
2.2 一致性(Consistency)
事務必須使數據庫從一個一致性狀態變換到另一個一致性狀態。(數據不被破壞)
2.3 隔離性(Isolation)
事務的隔離性是指一個事務的執行不能被其餘事務干擾,即一個事務內部的操做及使用的數據對併發的其餘事務是隔離的,併發執行的各個事務之間不能互相干擾。
2.4 持久性(Durability)
持久性是指一個事務一旦被提交,它對數據庫中數據的改變就是永久性的,接下來的其餘操做和數據庫故障不該該對其有任何影響。
3. JDBC 事務處理
在JDBC中,事務默認是自動提交的,每次執行一個 SQL 語句時,若是執行成功,就會向數據庫自動提交,而不能回滾。
爲了讓多個 SQL 語句做爲一個事務執行,需調用 Connection 對象的 setAutoCommit(false); 以取消自動提交事務:
conn.setAutoCommit(false);
在全部的 SQL 語句都成功執行後,調用 commit(); 方法提交事務
conn.commit();
在出現異常時,調用 rollback(); 方法回滾事務,通常再catch模塊中執行回滾操做。
conn.rollback();
能夠經過Connection的getAutoCommit()方法來得到當前事務的提交方式。
注意:在MySQL中的數據庫存儲引擎InnoDB支持事務,MyISAM不支持事務。
1. 概述
當須要批量插入或者更新記錄時。能夠採用Java的批量更新機制,這一機制容許多條語句一次性提交給數據庫批量處理。一般狀況下比單獨提交處理更有效率。
JDBC的批量處理語句包括下面兩個方法:
一般咱們會遇到兩種批量執行SQL語句的狀況:
2. Statement批量處理
Statement sm = conn.createStatement(); sm.addBatch(sql1); sm.addBatch(sql2); ... //批量處理 sm.executeBatch() //清除sm中積攢的參數列表 sm.clearBatch();
3. PreparedStatement批量傳參
preparedStatement ps = conn.preparedStatement(sql); for(int i=1;i<100000;i++){ ps.setInt(1, i); ps.setString(2, "name"+i); ps.setString(3, "email"+i); ps.addBatch(); if((i+1)%1000==0){ //批量處理 ps.executeBatch(); //清空ps中積攢的sql ps.clearBatch(); } }
注意:MySQL不支持批量處理。
批量處理應該設置一個上限,當批量處理列表中的sql累積到必定數量後,就應該執行,並在執行完成後,清空批量列表。
通常在excel導入數據的時候會用到批處理。
1. 概述
Java 經過JDBC得到鏈接之後,獲得一個Connection 對象,能夠從這個對象得到有關數據庫管理系統的各類信息,包括數據庫中的各個表,表中的各個列,數據類型,觸發器,存儲過程等各方面的信息。根據這些信息,JDBC能夠訪問一個實現事先並不瞭解的數據庫。
獲取這些信息的方法都是在DatabaseMetaData類的對象上實現的,而DataBaseMetaData對象是在Connection對象上得到的。
2. 獲取數據庫元數據
DatabaseMetaData 類中提供了許多方法用於得到數據源的各類信息,經過這些方法能夠很是詳細的瞭解數據庫的信息:
3. ResultSetMetaData
可用於獲取關於 ResultSet 對象中列的類型和屬性信息的對象:
1. Statement
Statement stmt = conn.createStatement(type,concurrency);
2. PreparedStatement
PreparedStatement stmt = conn.prepareStatement(sql,type,concurrency);
type說明:
ResultSet的Type | 說明 |
---|---|
TYPE_FORWARD_ONLY | 結果集不能滾動,只可向前滾動 |
TYPE_SCROLL_INSENSITIVE | 雙向滾動,但不及時更新,就是若是數據庫裏的數據修改過,並不在ResultSet中反應出來 |
TYPE_SCROLL_SENSITIVE | 雙向滾動,並及時跟蹤數據庫的更新,以便更改ResultSet中的數據 |
Concurrency(併發類型)說明:
ResultSet的Concurrency(併發類型) | 說明 |
---|---|
CONCUR_READ_ONLY | 結果集不可用於更新數據庫 |
CONCUR_UPDATABLE | 結果集能夠用於更新數據庫 |
3. ResultSet滾動的結果集使用
First: 將指針移動到此 ResultSet 對象的第一行
Last: 將指針移動到此 ResultSet 對象的最後一行
beforeFirst: 將指針移動到此 ResultSet 對象的開頭,正好位於第一行以前
afterLast: 將指針移動到此 ResultSet 對象的末尾,正好位於最後一行以後
isFirst: 檢索指針是否位於此 ResultSet 對象的第一行
isLast: 檢索指針是否位於此 ResultSet 對象的最後一行
isBeforeFirst: 檢索指針是否位於此 ResultSet 對象的第一行以前
isAfterLast: 檢索指針是否位於此 ResultSet 對象的最後一行以後
Relative: 按相對行數(或正或負)移動指針
Next: 將指針從當前位置下移一行
Previous: 將指針移動到此 ResultSet 對象的上一行
Absolute: 將指針移動到此 ResultSet 對象的給定行編號
如:
rs.absolute(80); //將指針移動到ResultSet 對象的第80行記錄。
注意:該特性對Oralce數據有效。可是在Mysql數據庫中無效,Mysql只支持TYPE_SCROLL_INSENSITIVE,CONCUR_READ_ONLY
。
1. 爲何要使用JDBC鏈接池
普通的JDBC數據庫鏈接使用 DriverManager 來獲取,每次向數據庫創建鏈接的時候都要將 Connection 加載到內存中,再驗證用戶名和密碼。須要數據庫鏈接的時候,就向數據庫要求一個,執行完成後再斷開鏈接。這樣的方式將會消耗大量的資源和時間。數據庫的鏈接資源並無獲得很好的重複利用.若同時有幾百人甚至幾千人在線,頻繁的進行數據庫鏈接操做將佔用不少的系統資源,嚴重的甚至會形成服務器的崩潰。
對於每一次數據庫鏈接,使用完後都得斷開。不然,若是程序出現異常而未能關閉,將會致使數據庫系統中的內存泄漏,最終將致使重啓數據庫。
這種開發不能控制被建立的鏈接對象數,系統資源會被毫無顧及的分配出去,如鏈接過多,也可能致使內存泄漏,服務器崩潰。
爲解決傳統開發中的數據庫鏈接問題,能夠採用數據庫鏈接池技術。
2. 數據庫鏈接池(connection pool)
數據庫鏈接池的基本思想就是爲數據庫鏈接創建一個「緩衝池」。預先在緩衝池中放入必定數量的鏈接,當須要創建數據庫鏈接時,只需從「緩衝池」中取出一個,使用完畢以後再放回去。
數據庫鏈接池負責分配、管理和釋放數據庫鏈接,它容許應用程序重複使用一個現有的數據庫鏈接,而不是從新創建一個。
數據庫鏈接池在初始化時將建立必定數量的數據庫鏈接放到鏈接池中,這些數據庫鏈接的數量是由最小數據庫鏈接數來設定的。不管這些數據庫鏈接是否被使用,鏈接池都將一直保證至少擁有這麼多的鏈接數量。鏈接池的最大數據庫鏈接數量限定了這個鏈接池能佔有的最大鏈接數,當應用程序向鏈接池請求的鏈接數超過最大鏈接數量時,這些請求將被加入到等待隊列中。
3. 數據庫鏈接池工做原理
4. 使用數據庫鏈接池的優勢
(1)資源重用:
因爲數據庫鏈接得以重用,避免了頻繁建立,釋放鏈接引發的大量性能開銷。在減小系統消耗的基礎上,另外一方面也增長了系統運行環境的平穩性。
(2)更快的系統反應速度
數據庫鏈接池在初始化過程當中,每每已經建立了若干數據庫鏈接置於鏈接池中備用。此時鏈接的初始化工做均已完成。對於業務請求處理而言,直接利用現有可用鏈接,避免了數據庫鏈接初始化和釋放過程的時間開銷,從而減小了系統的響應時間。
(3)新的資源分配手段
對於多應用共享同一數據庫的系統而言,可在應用層經過數據庫鏈接池的配置,實現某一應用最大可用數據庫鏈接數的限制,避免某一應用獨佔全部的數據庫資源。
(4)統一的鏈接管理,避免數據庫鏈接泄露
在較爲完善的數據庫鏈接池實現中,可根據預先的佔用超時設定,強制回收被佔用鏈接,從而避免了常規數據庫鏈接操做中可能出現的資源泄露。
5. 經常使用數據庫鏈接池介紹
JDBC 的數據庫鏈接池使用 javax.sql.DataSource 來表示,DataSource 只是一個接口,該接口一般由服務器(Weblogic, WebSphere, Tomcat)提供實現,也有一些開源組織提供實現,如:
其中,DBCP和C3P0用得比較多。
Tomcat 在 7.0 之前的版本都是使用 commons-dbcp 作爲鏈接池的實現。
數據源和數據庫鏈接不一樣,數據源無需建立多個,它是產生數據庫鏈接的工廠,所以整個應用只須要一個數據源便可。
當數據庫訪問結束後,程序仍是像之前同樣關閉數據庫鏈接:conn.close();
但它並無關閉數據庫的物理鏈接,它僅僅把數據庫鏈接釋放,歸還給了數據庫鏈接池。
大概基本的就是這麼多了,但願可以幫助到你們,有問題能夠交流溝通。
文章有不當之處,歡迎指正,若是喜歡微信閱讀,你也能夠關注個人微信公衆號:
好好學java
,獲取優質學習資源。