jdbc就是這麼簡單

程式設計師歐陽思海發表於2019-03-03

文章有不當之處,歡迎指正,如果喜歡微信閱讀,你也可以關注我的微信公眾號:好好學java,獲取優質學習資源。

一、JDBC

JAVA Database Connectivity java 資料庫連線.

JDBC(Java DataBase Connectivity,java資料庫連線)是一種用於執行SQL語句的Java API,可以為多種關聯式資料庫提供統一訪問,它由一組用Java語言編寫的類和介面組成。JDBC提供了一種基準,據此可以構建更高階的工具和介面,使資料庫開發人員能夠編寫資料庫應用程式,同時,JDBC也是個商標名。

二、為什麼會出現JDBC

SUN公司提供的一種資料庫訪問規則、規範, 由於資料庫種類較多,並且java語言使用比較廣泛,sun公司就提供了一種規範,讓其他的資料庫提供商去實現底層的訪問規則。 我們的java程式只要使用sun公司提供的jdbc驅動即可。

三、資料庫驅動

我們安裝好資料庫之後,我們的應用程式也是不能直接使用資料庫的,必須要通過相應的資料庫驅動程式,通過驅動程式去和資料庫打交道。其實也就是資料庫廠商的JDBC介面實現,即對Connection等介面的實現類的jar檔案。

1.png

四、常用介面

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");

常用方法:

  • createStatement():建立向資料庫傳送sql的statement物件。
  • prepareStatement(sql) :建立向資料庫傳送預編譯sql的PrepareSatement物件。
  • prepareCall(sql):建立執行儲存過程的callableStatement物件。
  • setAutoCommit(boolean autoCommit):設定事務是否自動提交。
  • commit() :在連結上提交事務。
  • rollback() :在此連結上回滾事務。

3.Statement介面

用於執行靜態SQL語句並返回它所生成結果的物件。

三種Statement類:

  • Statement:由createStatement建立,用於傳送簡單的SQL語句(不帶引數)。
  • PreparedStatement :繼承自Statement介面,由preparedStatement建立,用於傳送含有一個或多個引數的SQL語句。PreparedStatement物件比Statement物件的效率更高,並且可以防止SQL隱碼攻擊,所以我們一般都使用PreparedStatement。
  • CallableStatement:繼承自PreparedStatement介面,由方法prepareCall建立,用於呼叫儲存過程。

常用Statement方法:

  • execute(String sql):執行語句,返回是否有結果集
  • executeQuery(String sql):執行select語句,返回ResultSet結果集。
  • executeUpdate(String sql):執行insert/update/delete操作,返回更新的行數。
  • addBatch(String sql) :把多條sql語句放到一個批處理中。
  • executeBatch():向資料庫傳送一批sql語句執行。

4.ResultSet介面

ResultSet提供檢索不同型別欄位的方法,常用的有:

  • getString(int index)、getString(String columnName):獲得在資料庫裡是varchar、char等型別的資料物件。
  • getFloat(int index)、getFloat(String columnName):獲得在資料庫裡是Float型別的資料物件。
  • getDate(int index)、getDate(String columnName):獲得在資料庫裡是Date型別的資料。
  • getBoolean(int index)、getBoolean(String columnName):獲得在資料庫裡是Boolean型別的資料。
  • getObject(int index)、getObject(String columnName):獲取在資料庫裡任意型別的資料。

ResultSet還提供了對結果集進行滾動的方法:

  • next():移動到下一行
  • Previous():移動到前一行
  • absolute(int row):移動到指定行
  • beforeFirst():移動resultSet的最前面。
  • afterLast() :移動到resultSet的最後面。

使用後依次關閉物件及連線:ResultSet → Statement → Connection

五、使用JDBC的基本步驟

1. 註冊驅動
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
複製程式碼
2. 建立連線
//DriverManager.getConnection("jdbc:mysql://localhost/test?user=SIHAI&password=SIHAI");
//2. 建立連線 引數一: 協議 + 訪問的資料庫 , 引數二: 使用者名稱 , 引數三: 密碼。

 conn = DriverManager.getConnection("jdbc:mysql://localhost/student", "root", "root");
複製程式碼
3. 建立statement
//3. 建立statement , 跟資料庫打交道,一定需要這個物件
st = conn.createStatement();
複製程式碼
4. 執行sql ,得到ResultSet
//4. 執行查詢 , 得到結果集
String sql = "select * from t_stu";
rs = st.executeQuery(sql);
複製程式碼
5. 遍歷結果集
		//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);
 				
 		}
複製程式碼
6. 釋放資源
	if (rs != null) {
       try {
            rs.close();
        } catch (SQLException sqlEx) { } // ignore 
        rs = null;
    }
	
複製程式碼

六、JDBC 工具類構建

1. 資源釋放工作的整合
/**
	 * 釋放資源
	 * @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;
		}
	}
複製程式碼
2. 驅動防二次註冊
/**
	 * 獲取連線物件
	 * @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;
	}
複製程式碼
3. 使用properties配置檔案
  • 在src底下宣告一個檔案 xxx.properties ,裡面的內容吐下:
     	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;
		}
	}
}

複製程式碼

七、資料庫的CRUD

  • insert操作
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操作
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("刪除失敗");
}
複製程式碼
  • query操作
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操作
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("更新失敗");
}
複製程式碼

八、使用單元測試,測試程式碼

1. 定義一個類, TestXXX , 裡面定義方法 testXXX.

這個命名不一定需要這樣,但是這樣的命名更容易懂得測試的意思,所以建議命名見名知意。

2. 新增junit的支援。

右鍵工程 --- add Library --- Junit --- Junit4

3. 在方法的上面加上註解 , 其實就是一個標記。
	/**
 * 使用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);
		}
		
	}
}
複製程式碼
4. 游標選中方法名字,然後右鍵執行單元測試。 或者是開啟outline檢視, 然後選擇方法右鍵執行。

九、Dao模式

Data Access Object 資料訪問物件

DAO(Data Access Object) 資料訪問物件是一個物件導向的資料庫介面,它顯露了 Microsoft Jet 資料庫引擎(由 Microsoft Access 所使用),並允許 Visual Basic 開發者通過 ODBC 像直接連線到其他資料庫一樣,直接連線到 Access 表。DAO 最適用於單系統應用程式或小範圍本地分佈使用。

1. 新建一個dao的介面, 裡面宣告資料庫訪問規則
	/**
	* 定義運算元據庫的方法
	 */
	public interface UserDao {
	
		/**
		 * 查詢所有
		 */
		void findAll();
	}
複製程式碼
2. 新建一個dao的實現類,具體實現早前定義的規則
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);
			}
		}
	
	}
複製程式碼
3. 直接使用實現
	@Test
   	public void testFindAll(){
   		UserDao dao = new UserDaoImpl();
   		dao.findAll();
   	}
複製程式碼

十、Statement安全問題

1. Statement執行 ,其實是拼接sql語句的。 先拼接sql語句,然後在一起執行。
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);
複製程式碼

PrepareStatement

該物件就是替換前面的statement物件。

  1. 相比較以前的statement, 預先處理給定的sql語句,對其執行語法檢查。 在sql語句裡面使用 ? 佔位符來替代後續要傳遞進來的變數。 後面進來的變數值,將會被看成是字串,不會產生任何的關鍵字。
String sql = "insert into t_user values(null , ? , ?)";
ps = conn.prepareStatement(sql);
 
 //給佔位符賦值 從左到右數過來,1 代表第一個問號, 永遠你是1開始。
 ps.setString(1, userName);
 ps.setString(2, password);
複製程式碼

PreparedStatement與Statement比較

(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不支援事務。

十二、批量處理JDBC語句

1. 概述

當需要批量插入或者更新記錄時。可以採用Java的批量更新機制,這一機制允許多條語句一次性提交給資料庫批量處理。通常情況下比單獨提交處理更有效率。

JDBC的批量處理語句包括下面兩個方法:

  • addBatch(String):新增需要批量處理的SQL語句或是引數;
  • executeBatch();執行批量處理語句;

通常我們會遇到兩種批量執行SQL語句的情況:

  • 多條SQL語句的批量處理;
  • 一個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匯入資料的時候會用到批處理。

十三、使用 JDBC 處理後設資料

1. 概述

Java 通過JDBC獲得連線以後,得到一個Connection 物件,可以從這個物件獲得有關資料庫管理系統的各種資訊,包括資料庫中的各個表,表中的各個列,資料型別,觸發器,儲存過程等各方面的資訊。根據這些資訊,JDBC可以訪問一個實現事先並不瞭解的資料庫。

獲取這些資訊的方法都是在DatabaseMetaData類的物件上實現的,而DataBaseMetaData物件是在Connection物件上獲得的。

2. 獲取資料庫後設資料

DatabaseMetaData 類中提供了許多方法用於獲得資料來源的各種資訊,通過這些方法可以非常詳細的瞭解資料庫的資訊:

  • getURL():返回一個String類物件,代表資料庫的URL。
  • getUserName():返回連線當前資料庫管理系統的使用者名稱。
  • isReadOnly():返回一個boolean值,指示資料庫是否只允許讀操作。
  • getDatabaseProductName():返回資料庫的產品名稱。
  • getDatabaseProductVersion():返回資料庫的版本號。
  • getDriverName():返回驅動驅動程式的名稱。
  • getDriverVersion():返回驅動程式的版本號。

3. ResultSetMetaData

可用於獲取關於 ResultSet 物件中列的型別和屬性資訊的物件:

  • getColumnName(int column):獲取指定列的名稱
  • getColumnCount():返回當前 ResultSet 物件中的列數。
  • getColumnTypeName(int column):檢索指定列的資料庫特定的型別名稱。
  • getColumnDisplaySize(int column):指示指定列的最大標準寬度,以字元為單位。
  • isNullable(int column):指示指定列中的值是否可以為 null。
  • isAutoIncrement(int column):指示是否自動為指定列進行編號,這樣這些列仍然是隻讀的。

十四、建立可滾動、更新的記錄集

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

十五、JDBC連線池

1. 為什麼要使用JDBC連線池

普通的JDBC資料庫連線使用 DriverManager 來獲取,每次向資料庫建立連線的時候都要將 Connection 載入到記憶體中,再驗證使用者名稱和密碼。需要資料庫連線的時候,就向資料庫要求一個,執行完成後再斷開連線。這樣的方式將會消耗大量的資源和時間。資料庫的連線資源並沒有得到很好的重複利用.若同時有幾百人甚至幾千人線上,頻繁的進行資料庫連線操作將佔用很多的系統資源,嚴重的甚至會造成伺服器的崩潰。

對於每一次資料庫連線,使用完後都得斷開。否則,如果程式出現異常而未能關閉,將會導致資料庫系統中的記憶體洩漏,最終將導致重啟資料庫。

這種開發不能控制被建立的連線物件數,系統資源會被毫無顧及的分配出去,如連線過多,也可能導致記憶體洩漏,伺服器崩潰。

為解決傳統開發中的資料庫連線問題,可以採用資料庫連線池技術。

2. 資料庫連線池(connection pool)

資料庫連線池的基本思想就是為資料庫連線建立一個“緩衝池”。預先在緩衝池中放入一定數量的連線,當需要建立資料庫連線時,只需從“緩衝池”中取出一個,使用完畢之後再放回去。

資料庫連線池負責分配、管理和釋放資料庫連線,它允許應用程式重複使用一個現有的資料庫連線,而不是重新建立一個。

資料庫連線池在初始化時將建立一定數量的資料庫連線放到連線池中,這些資料庫連線的數量是由最小資料庫連線數來設定的。無論這些資料庫連線是否被使用,連線池都將一直保證至少擁有這麼多的連線數量。連線池的最大資料庫連線數量限定了這個連線池能佔有的最大連線數,當應用程式向連線池請求的連線數超過最大連線數量時,這些請求將被加入到等待佇列中。

3. 資料庫連線池工作原理

2.png

4. 使用資料庫連線池的優點

(1)資源重用:

由於資料庫連線得以重用,避免了頻繁建立,釋放連線引起的大量效能開銷。在減少系統消耗的基礎上,另一方面也增加了系統執行環境的平穩性。

(2)更快的系統反應速度

資料庫連線池在初始化過程中,往往已經建立了若干資料庫連線置於連線池中備用。此時連線的初始化工作均已完成。對於業務請求處理而言,直接利用現有可用連線,避免了資料庫連線初始化和釋放過程的時間開銷,從而減少了系統的響應時間。

(3)新的資源分配手段

對於多應用共享同一資料庫的系統而言,可在應用層通過資料庫連線池的配置,實現某一應用最大可用資料庫連線數的限制,避免某一應用獨佔所有的資料庫資源。

(4)統一的連線管理,避免資料庫連線洩露 在較為完善的資料庫連線池實現中,可根據預先的佔用超時設定,強制回收被佔用連線,從而避免了常規資料庫連線操作中可能出現的資源洩露。

5. 常用資料庫連線池介紹

JDBC 的資料庫連線池使用 javax.sql.DataSource 來表示,DataSource 只是一個介面,該介面通常由伺服器(Weblogic, WebSphere, Tomcat)提供實現,也有一些開源組織提供實現,如:

  • DBCP 資料庫連線池
  • C3P0 資料庫連線池
  • Proxpool 資料庫連線池

其中,DBCP和C3P0用得比較多。

Tomcat 在 7.0 以前的版本都是使用 commons-dbcp 做為連線池的實現。

資料來源和資料庫連線不同,資料來源無需建立多個,它是產生資料庫連線的工廠,因此整個應用只需要一個資料來源即可。

當資料庫訪問結束後,程式還是像以前一樣關閉資料庫連線:conn.close(); 但它並沒有關閉資料庫的物理連線,它僅僅把資料庫連線釋放,歸還給了資料庫連線池。

大概基本的就是這麼多了,希望能夠幫助到大家,有問題可以交流溝通。

相關文章