0基礎之你不知道的JDBC

一個不會程式設計的發表於2020-11-14

一、JDBC入門

想要使用JDBC就得有一個資料庫,這裡以mysql為例子。
獲取一個mysql的jar包,複製貼上到該資料夾下,右鍵bulid path安裝到路徑內
在這裡插入圖片描述
然後最基本的操作順序就是載入驅動–>獲取連線–>執行SQL–>釋放資源。例子如下:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

import org.junit.Test;

public class JDBCDemo1 {
	@Test
	public void demo1() throws Exception{
		Class.forName("com.mysql.jdbc.Driver");//載入驅動
		
		Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db3", "root", "");
		//獲得連線
		
		Statement statement = conn.createStatement();//獲得可執行sql的物件
		
		String sql = "show tables";//sql語句
		
		ResultSet rs = statement.executeQuery(sql);//執行sql語句
		
		//遍歷結果集
		while (rs.next()) {
			System.out.println(rs.getString("Tables_in_db3"));
		}
		
		//釋放資源
		rs.close();
		statement.close();
		conn.close();
	}
}

二、API詳解和其他基本操作

DriverManager : 驅動管理類

作用:註冊驅動
這個類裡面有一個靜態註冊驅動的方法registerDriver (Driver driver)

這個方法可以完成驅動的註冊,但是實際開發中一般不會使用這個方法完成驅動的註冊!
原因:
如果需要註冊驅動,就會使用DriverManager.registerDriver(new Driver());
但是這個Driver類在程式碼中有一段靜態程式碼塊,靜態程式碼塊已經呼叫了註冊驅動的方法。
如果再手動呼叫則會導致註冊兩次。
所以一般使用Class.forName(“com.mysql.jdbc.Driver”); 去註冊驅動。

作用:獲得與資料庫的連線
用這個方法獲得與資料庫連線:getConnection (String url,String user,String password);
這個裡面url的寫法需要重點關注。入門中的例子裡是這樣的:
jdbc:mysql://localhost:3306/db3

各個的含義是:
jdbc : 連線資料庫的協議
mysql : 是jdbc的子協議
localhost : 連線的MysQL 資料庫伺服器的主機地址。(連線是本機就可以寫成localhost),如果連線不是本機的,就需要寫上連線主機的IP地址。
3306 : MySQL資料庫伺服器的埠號+
db3 : 資料庫名稱

url如果連線的是本機的路徑,可以簡化為如下格式:
jdbc:mysql:///db3;
注意:是3個/

Connection :與資料庫連線物件

作用:建立執行sql語句的物件
createStatement() : 建立一個 Statement物件來將SQL語句傳送到資料庫。

prepareCall(String sql) : 建立一個CallableStatement 物件來呼叫資料庫儲存過程。

prepareStatement(String sql) : 建立一個PreparedStatement物件來將引數化的SQL語句傳送到資料庫。

statement : 執行SQL
Callablestatement : 執行資料庫中儲存過程
PreparedStatement : 執行SQL對SQL進行預處理。解決SQL隱碼攻擊漏洞。

作用:管理事務
setAutoCommit (boolean autoCommit) : 將此連線的自動提交模式設定為給定狀態。

commit() : 使所有上一次提交/回滾後進行的更改成為持久更改,並釋放此Connection物件當前持有的所有資料庫鎖。

rollback() : 取消在當前事務中進行的所有更改,並釋放此Connection物件當前持有的所有資料庫鎖。

Statement : 執行SQL

作用:執行SQL
boolean execute(String sql) :執行給定的SQL語句(該語句可能返回多個結果),並通知驅動程式所有自動生成的鍵都應該可用於獲取。 執行查詢,修改,新增,刪除的SQL語句,如果是查詢語句返回true,如果是修改,新增,刪除的SQL語句,返回false。

一般使用下面兩個方法:
ResultSet executeQuery(String sql) : 執行給定的SQL語句,該語句返回單個ResultSet物件。 執行查詢
int executeUpdate(string sql) : 執行給定SQL語句,該語句可能為INSERT、UPDATE或 DELETE語句,或者不返回任何內容的SQL語句(如SQL DDL語句)。執行修改,新增,刪除的SQL語句

作用:執行批處理
addBatch (String sql) : 將給定的SQL命令新增到此Statement物件的當前命令列表中。
**clearBatch() **: 清空此 Statement 物件的當前SQL命令列表。
int[] executeBatch() : 將一批命令提交給資料庫來執行,如果全部命令執行成功,則返回更新計陣列成的陣列。

ResultSet : 結果集

通過查詢語句獲得的結果。
next() : 這個游標最早是在第一行之前,當呼叫了next()之後就會指向第一行。
那麼結果集的獲取就需要使用getxxx() 方法,如下:
getXxx(int columnlndex); 列號
getXxx(String columnName); 列名,通常是使用這個

資源釋放

JDBC程式執行結束後,將與資料庫進行互動的物件釋放掉,通常ResultSet,Statement,Connection。這幾個物件中尤其是Connection物件是非常稀有的。這個物件一定要做到儘量晚建立,儘早釋放掉。
將資源釋放的程式碼寫入到 finally 的程式碼塊中。
資源釋放的程式碼應該寫的標準:手動置為null的用處是讓gc更早回收

			//釋放資源
			if (rs!=null) {
				try {
					rs.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
				rs = null;
			}
			if (statement!=null) {
				try {
					statement.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
				statement = null;
			}
			if (conn!=null) {
				try {
					conn.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
				conn = null;
			}

CRUD

1.利用statement儲存、修改、刪除、查詢操作:

儲存操作:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

import org.junit.Test;

public class JDBCDemo2 {
	@Test
	public void demo1() {
		Connection conn = null;
		Statement statement = null;
		try {
			Class.forName("com.mysql.jdbc.Driver");
			conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc", "root", "");
			statement = conn.createStatement();
			
			String sql = new String("insert into user values(null,'小明','10086',18,'1999-01-02')");
			
			int num = statement.executeUpdate(sql);
			if (num>0) {
				System.out.println("資料儲存成功");
			}
			
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			if (statement!=null) {
				try {
					statement.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
				statement = null;
			}
			if (conn!=null) {
				try {
					conn.close();
				} catch (SQLException e) {	
					e.printStackTrace();
				}
				conn = null;
			}
		}
	}
}

此時去資料庫查詢表可以發現資料已經插入成果!如果出現中文亂碼問題應該是資料庫字符集未設定好,去手動設定一下就OK了。
在這裡插入圖片描述
修改操作:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

import org.junit.Test;

public class JDBCDemo2 {
	@Test
	public void demo1() {
		Connection conn = null;
		Statement statement = null;
		try {
			Class.forName("com.mysql.jdbc.Driver");
			conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc", "root", "");
			statement = conn.createStatement();
			
			String sql = "update user set username = '小美',password = '110',age=20,birthday='1999-12-05' where id = 1";
			
			int num = statement.executeUpdate(sql);
			if (num>0) {
				System.out.println("資料更新成功");
			}
			
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			if (statement!=null) {
				try {
					statement.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
				statement = null;
			}
			if (conn!=null) {
				try {
					conn.close();
				} catch (SQLException e) {	
					e.printStackTrace();
				}
				conn = null;
			}
		}
	}
}

執行結果查詢資料庫如下圖:
在這裡插入圖片描述

刪除操作:

@Test
	public void demo2() {
		Connection conn = null;
		Statement statement = null;
		try {
			Class.forName("com.mysql.jdbc.Driver");
			conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc", "root", "");
			statement = conn.createStatement();
			
			String sql = "delete from user where id = 2";
			
			int num = statement.executeUpdate(sql);
			if (num>0) {
				System.out.println("資料刪除成功");
			}
			
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			if (statement!=null) {
				try {
					statement.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
				statement = null;
			}
			if (conn!=null) {
				try {
					conn.close();
				} catch (SQLException e) {	
					e.printStackTrace();
				}
				conn = null;
			}
		}
	}

執行結果查詢資料庫如下圖:
在這裡插入圖片描述
查詢操作:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import org.junit.Test;

public class JDBCDemo2 {
	@Test
	public void demo1() {
		Connection conn = null;
		Statement statement = null;
		ResultSet rs = null;
		try {
			Class.forName("com.mysql.jdbc.Driver");
			conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc", "root", "");
			statement = conn.createStatement();
			
			String sql = "select * from user";
			rs = statement.executeQuery(sql);
			
			while (rs.next()) {
				System.out.println(rs.getInt("id")+" "+rs.getString("username")+" "+rs.getString("password")+" "+rs.getInt("age")+" "+rs.getString("birthday"));
				
			}
			
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			if (rs!=null) {
				try {
					rs.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
				rs = null;
			}
			if (statement!=null) {
				try {
					statement.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
				statement = null;
			}
			if (conn!=null) {
				try {
					conn.close();
				} catch (SQLException e) {	
					e.printStackTrace();
				}
				conn = null;
			}
		}
	}
}

程式執行結果如下:
在這裡插入圖片描述

2.工具類的抽取

寫出上述程式碼後發現,程式碼重複且無趣,於是可以編寫一個工具類方便書寫,程式碼如下:


import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class JDBCUtils {
	private static final String driverClassName;
	private static final String url;
	private static final String username;
	private static final String password;
	
	static {
		driverClassName = "com.mysql.jdbc.Driver";
		url = "jdbc:mysql:///jdbc";
		username = "root";
		password = "";
	}
	
	public static void loadDriver() {
		try {
			Class.forName(driverClassName);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
	
	public static Connection getConnection() {
		Connection conn = null;
		try {
			loadDriver();
			conn = DriverManager.getConnection(url,username,password);
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return conn;
	}
	public static void release(ResultSet rs,Statement statement,Connection conn) {
		if (rs!=null) {
			try {
				rs.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			rs = null;
		}
		if (statement!=null) {
			try {
				statement.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			statement = null;
		}
		if (conn!=null) {
			try {
				conn.close();
			} catch (SQLException e) {	
				e.printStackTrace();
			}
			conn = null;
		}
	}
	public static void release(Statement statement,Connection conn) {
		if (statement!=null) {
			try {
				statement.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			statement = null;
		}
		if (conn!=null) {
			try {
				conn.close();
			} catch (SQLException e) {	
				e.printStackTrace();
			}
			conn = null;
		}
	}
	
	
}

以後就可以利用自己的工具類去編寫程式。

2.1配置資訊提取到配置檔案

配置檔案有兩種,屬性檔案和XML
本文采用屬性檔案
檔案格式 : 副檔名是properties
內容: key=values
如下圖:
在這裡插入圖片描述
有了配置檔案後,就可以在檔案中修改,那麼修改上述我們建立的工具類中static的靜態程式碼塊中的部分就可以了。

	static {
		Properties properties = new Properties();
		try {
			properties.load(new FileInputStream("src/db.properties"));
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		driverClassName = properties.getProperty("driverClassName");
		url = properties.getProperty("url");
		username = properties.getProperty("username");
		password = properties.getProperty("password");
	}

3.sql注入漏洞

sql注入漏洞就是不知道你密碼,但是知道使用者名稱也可以登入你的賬戶,下面寫一個簡單的登入程式,演示這一個漏洞

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;

import com.jdbc.utils.JDBCUtils;

public class UserDemo {
	private boolean login(String username,String password) {
		Connection conn= null;
		Statement statement= null;
		ResultSet rs= null;
		boolean flag = false;
		try {
			conn = JDBCUtils.getConnection();
			statement = conn.createStatement();
			String sql = "select * from user where username = '"+username+"'and password = '"+password+"'";
			rs = statement.executeQuery(sql);
			if (rs.next()) {
				flag = true;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			JDBCUtils.release(rs, statement, conn);
		}
		
		return flag;
	}
}

乍一看是沒有問題,實際上只需要

輸入使用者名稱 aaa' or '1=1  或者  aaa' --  
密碼 隨意

就可以登入aaa的賬戶,這就是sql注入漏洞
那麼如何解決呢?
其實產生這個漏洞的原因是因為使用者名稱使用了sql的關鍵字造成的
那麼我們只需要採用 PreparedStatement 物件解決SQL隱碼攻擊漏洞。這個物件將SQL預先進行編譯,使用?作為佔位符。?所代表內容是SQL所固定。再次傳入變數(包含SQL的關鍵字)。這個時候也不會識別這些關鍵字。程式碼如下:

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

import com.jdbc.utils.JDBCUtils;

public class UserDemo {
	private boolean login(String username,String password) {
		Connection conn= null;
		PreparedStatement pStatement = null;
		ResultSet rs= null;
		boolean flag = false;
		try {
			conn = JDBCUtils.getConnection();
			String sql = "select * from user where username = ? and password = ?";
			pStatement = conn.prepareStatement(sql);
			pStatement.setString(1, username);
			pStatement.setString(2, password);
			rs = pStatement.executeQuery();
			if (rs.next()) {
				flag = true;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			JDBCUtils.release(rs, pStatement, conn); //pStatement 是 statement的子介面,所以利用了多型,可以釋放
		}
		
		return flag;
	}
}

4.利用PreparedStatement儲存、修改、刪除、查詢操作:

儲存:

import java.sql.Connection;
import java.sql.PreparedStatement;

import org.junit.Test;

import com.jdbc.utils.JDBCUtils;//自己寫的工具類

public class JDBCDemo {
	@Test
	public void demo() {
		Connection conn = null;
		PreparedStatement pStatement = null;
		try {
			conn = JDBCUtils.getConnection();
			String sql = "insert into user values(?,?,?,?,?)";
			pStatement = conn.prepareStatement(sql);
			pStatement.setInt(1, 2);
			pStatement.setString(2, "小趙");
			pStatement.setString(3, "123456");
			pStatement.setInt(4, 19);
			pStatement.setString(5, "2001-01-01");
			int num = pStatement.executeUpdate();
			if (num>0) {
				System.out.println("插入成功");
			}
			
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			JDBCUtils.release(pStatement, conn);
		}
	}
}

在這裡插入圖片描述
修改:

import java.sql.Connection;
import java.sql.PreparedStatement;

import org.junit.Test;

import com.jdbc.utils.JDBCUtils;

public class JDBCDemo {
	@Test
	public void demo() {
		Connection conn = null;
		PreparedStatement pStatement = null;
		try {
			conn = JDBCUtils.getConnection();
			String sql = "update user set password = ? where id = ?";
			pStatement = conn.prepareStatement(sql);
			pStatement.setString(1, "987654");
			pStatement.setInt(2, 1);

			int num = pStatement.executeUpdate();
			if (num>0) {
				System.out.println("修改成功");
			}
			
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			JDBCUtils.release(pStatement, conn);
		}
	}
}

刪除:

import java.sql.Connection;
import java.sql.PreparedStatement;

import org.junit.Test;

import com.jdbc.utils.JDBCUtils;

public class JDBCDemo {
	@Test
	public void demo() {
		Connection conn = null;
		PreparedStatement pStatement = null;
		try {
			conn = JDBCUtils.getConnection();
			String sql = "delete from user where id = ?";
			pStatement = conn.prepareStatement(sql);
			pStatement.setInt(1, 3);

			int num = pStatement.executeUpdate();
			if (num>0) {
				System.out.println("刪除成功");
			}
			
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			JDBCUtils.release(pStatement, conn);
		}
	}
}

在這裡插入圖片描述
查詢:

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

import org.junit.Test;

import com.jdbc.utils.JDBCUtils;

public class JDBCDemo {
	@Test
	public void demo() {
		Connection conn = null;
		PreparedStatement pStatement = null;
		ResultSet rs = null;
		try {
			conn = JDBCUtils.getConnection();
			String sql = "select * from user";
			pStatement = conn.prepareStatement(sql);
			rs = pStatement.executeQuery(sql);
			while(rs.next()) {
				System.out.println(rs.getInt("id")+" "+rs.getString("username")+" "+rs.getString("password")+" "+rs.getInt("age")+" "+rs.getString("birthday"));
			}
			
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			JDBCUtils.release(rs,pStatement, conn);
		}
	}
}

批處理

批處理在預設情況下是關閉的,需要再url後面新增上 ?rewriteBatchedStatements=true 如下圖:
在這裡插入圖片描述
批處理適合那種重複的插入視情況而看。下列是一個插入多個引數的例子:

import java.sql.Connection;
import java.sql.PreparedStatement;

import org.junit.Test;

import com.jdbc.utils.JDBCUtils;

public class JDBCDemo5 {
	@Test
	public void demo() {
		long begin = System.currentTimeMillis();//測試耗時
		Connection conn = null;
		PreparedStatement pStatement = null;
		try {
			conn = JDBCUtils.getConnection();
			String sql = "insert into user values(null,?,?,?,?)";
			pStatement = conn.prepareStatement(sql);
			for (int i = 1; i <= 1000; i++) {
				pStatement.setString(1,"name"+i);
				
				pStatement.setString(2,i+"");
				
				pStatement.setInt(3,i);
				
				pStatement.setString(4, "1999-01-01");
				pStatement.addBatch();
				if (i%100==0) {
					pStatement.executeBatch();
					pStatement.clearBatch();
				}
			}						
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			JDBCUtils.release(pStatement, conn);
		}
		long end = System.currentTimeMillis();
		System.out.println("耗時(秒):"+(end-begin)/1000);
	}
}

此圖僅僅展示部分
在這裡插入圖片描述

JDBC的事務管理

就是一組邏輯,要麼一起成功要麼一起失敗,首先先在資料庫中建立一個賬目表,如下:

use jdbc;
create table account(
	id int primary key auto_increment,
	name varchar(20),
	money double
);
insert into account values(null,'aaa',10000);
insert into account values(null,'bbb',10000);
insert into account values(null,'ccc',10000);

在轉賬中沒有新增事務的管理,假如轉賬時候出現異常,會出現 aaa賬號的錢被轉丟了,但是 bbb.賬號的錢沒有任何變化。需要給轉賬的功能新增事務的管理。程式碼如下:

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

import org.junit.Test;

import com.jdbc.utils.JDBCUtils;

public class TransactionDemo1 {
	@Test
	public void demo1() {
		Connection conn = null;
		PreparedStatement pStatement = null;
		try {
			conn = JDBCUtils.getConnection();
			conn.setAutoCommit(false);
			String sql = "update account set money = money + ? where name = ?";
			pStatement = conn.prepareStatement(sql);
			pStatement.setDouble(1, -1000);
			pStatement.setString(2, "aaa");
			pStatement.executeUpdate();
			/*假如這裡有個異常,資料庫的資料就不會發生變化,因為沒提交,被回滾了*/
			pStatement.setDouble(1, 1000);
			pStatement.setString(2, "bbb");
			pStatement.executeUpdate();
			conn.commit();
			
			
		} catch (Exception e) {
			try {
				conn.rollback();
			} catch (SQLException e1) {
				e1.printStackTrace();
			}
			e.printStackTrace();
		}finally {
			JDBCUtils.release(pStatement, conn);
		}
	}
}

三、連線池

連線池是建立和管理一個連線的緩衝池的技術,這些連線準備好被任何需要它們的執行緒使用。連線池是裝有連線的容器,使用連線的話,可以從連線池中進行獲取,使用完成之後將連線歸還給連線池。

作用:連線物件建立和銷燬是需要耗費時間的,在伺服器初始化的時候就初始化一些連線。把這些連線放入到記憶體中,使用的時候可以從記憶體中獲取,使用完成之後將連線放入連線池中。從記憶體中獲取和歸還的效率要遠遠高於建立和銷燬的效率。(提升效能)。

自定義連線池的實現

1.步驟:

1.編寫一個類實現DataSource
2.重寫getConnection方法
3.初始化多個連線在記憶體中
4.編寫歸還連線的方法
第一個例子:

import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

import javax.sql.DataSource;

import com.jdbc.utils.JDBCUtils;

public class MyDataSource implements DataSource {
	public List<Connection> connList = new ArrayList<Connection>();
	
	//初始化提供連線
	public MyDataSource(){
		for (int i = 1; i <= 3; i++) {
			connList.add(JDBCUtils.getConnection());
		}
	}
	
	//從連線池獲得連線
	@Override
	public Connection getConnection() throws SQLException {
		
		return connList.remove(0);
	}
	
	//編寫一個歸還連線的方法
	public void addBack(Connection conn) {
		connList.add(conn);
	}
	
	
	

	//下列程式碼不用管
	@Override
	public Logger getParentLogger() throws SQLFeatureNotSupportedException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public <T> T unwrap(Class<T> iface) throws SQLException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public boolean isWrapperFor(Class<?> iface) throws SQLException {
		// TODO Auto-generated method stub
		return false;
	}

	

	@Override
	public Connection getConnection(String username, String password) throws SQLException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public PrintWriter getLogWriter() throws SQLException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void setLogWriter(PrintWriter out) throws SQLException {
		// TODO Auto-generated method stub

	}

	@Override
	public void setLoginTimeout(int seconds) throws SQLException {
		// TODO Auto-generated method stub

	}

	@Override
	public int getLoginTimeout() throws SQLException {
		// TODO Auto-generated method stub
		return 0;
	}

}

測試:

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.junit.Test;

public class DataSourceDemo {
	@Test
	public void demo1() {
		Connection conn = null;
		PreparedStatement pStatement = null;
		ResultSet rs = null;
		MyDataSource dataSource = null;
		try {
			dataSource = new MyDataSource();
			conn = dataSource.getConnection();
			
			String sql = "select * from account";
			pStatement = conn.prepareStatement(sql);
			rs = pStatement.executeQuery();
			while (rs.next()) {
				System.out.println(rs.getInt("id")+" "+rs.getString("name")+" "+rs.getDouble("money"));
				
			}
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			if (rs!=null) {
				try {
					rs.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
				rs = null;
			}
			if (pStatement!=null) {
				try {
					pStatement.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
				pStatement = null;
			}
			dataSource.addBack(conn);
		}
	}
}

執行結果如下圖:
在這裡插入圖片描述
說明可以使用自己建立的連線池。雖然可以使用但是還是有很多不足,這種寫法不方便擴充,若使用者想使用還得知道我的連線池的方法,增加了使用者使用的難度。那麼我們可以使用裝飾者模式,對close() 進行增強.

2.利用裝飾者模式對自定義連線池進行優化

裝飾者模式使用條件:
增強雖的類和被增強的類實現相同的介面
在增強的類中獲得被增強的類的引用

裝飾者模式達到對類增強的簡單例子:

interface Waiter{
	public void server();
}
class Waitress implements Waiter{
	public void server() {
		System.out.println("服務中ing");
		
	}
}
class WaitressWrapper implements Waiter{
	private Waiter waiter;
	public WaitressWrapper(Waiter waiter) {
		this.waiter = waiter;
	}
	public void server() {
		System.out.println("微笑");
		waiter.server();
	}
}

這樣就達到了對server()方法的一個增強,服務中要先一個微笑。利用這種思路,就可以對我們的連線池進行優化。但是缺點很明顯,假設介面需要重寫的方法很多,為了簡化程式設計,可以提供一個模板類(模板類原封不動的將介面中的所有方法都實現,但是都沒有增強)。編寫一個裝飾類繼承模板類。在裝飾類中只需要增強某一個方法即可。
為了編寫方便,我編寫了一個模板類,只需要讓裝飾類繼承模板類然後重寫close方法達到不銷燬,而是歸還的目的。
模板類:
ConnectionWrapper

package com.datasource.demo1;

import java.sql.Array;
import java.sql.Blob;
import java.sql.CallableStatement;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.NClob;
import java.sql.PreparedStatement;
import java.sql.SQLClientInfoException;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.SQLXML;
import java.sql.Savepoint;
import java.sql.Statement;
import java.sql.Struct;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executor;

public class ConnectionWrapper implements Connection{
	private Connection conn;
	public ConnectionWrapper(Connection conn) {
		this.conn = conn;
	}
	
	@Override
	public <T> T unwrap(Class<T> iface) throws SQLException {
		// TODO Auto-generated method stub
		return conn.unwrap(iface);
	}

	@Override
	public boolean isWrapperFor(Class<?> iface) throws SQLException {
		// TODO Auto-generated method stub
		return conn.isWrapperFor(iface);
	}

	@Override
	public Statement createStatement() throws SQLException {
		// TODO Auto-generated method stub
		return conn.createStatement();
	}

	@Override
	public PreparedStatement prepareStatement(String sql) throws SQLException {
		// TODO Auto-generated method stub
		return conn.prepareStatement(sql);
	}

	@Override
	public CallableStatement prepareCall(String sql) throws SQLException {
		// TODO Auto-generated method stub
		return conn.prepareCall(sql);
	}

	@Override
	public String nativeSQL(String sql) throws SQLException {
		// TODO Auto-generated method stub
		return conn.nativeSQL(sql);
	}

	@Override
	public void setAutoCommit(boolean autoCommit) throws SQLException {
		// TODO Auto-generated method stub
		conn.setAutoCommit(autoCommit);
	}

	@Override
	public boolean getAutoCommit() throws SQLException {
		// TODO Auto-generated method stub
		return conn.getAutoCommit();
	}

	@Override
	public void commit() throws SQLException {
		// TODO Auto-generated method stub
		conn.commit();
	}

	@Override
	public void rollback() throws SQLException {
		// TODO Auto-generated method stub
		conn.rollback();
	}

	@Override
	public void close() throws SQLException {
		// TODO Auto-generated method stub
		conn.close();
	}

	@Override
	public boolean isClosed() throws SQLException {
		// TODO Auto-generated method stub
		return conn.isClosed();
	}

	@Override
	public DatabaseMetaData getMetaData() throws SQLException {
		// TODO Auto-generated method stub
		return conn.getMetaData();
	}

	@Override
	public void setReadOnly(boolean readOnly) throws SQLException {
		// TODO Auto-generated method stub
		conn.setReadOnly(readOnly);
	}

	@Override
	public boolean isReadOnly() throws SQLException {
		// TODO Auto-generated method stub
		return conn.isReadOnly();
	}

	@Override
	public void setCatalog(String catalog) throws SQLException {
		// TODO Auto-generated method stub
		conn.setCatalog(catalog);
	}

	@Override
	public String getCatalog() throws SQLException {
		// TODO Auto-generated method stub
		return conn.getCatalog();
	}

	@Override
	public void setTransactionIsolation(int level) throws SQLException {
		// TODO Auto-generated method stub
		conn.setTransactionIsolation(level);
	}

	@Override
	public int getTransactionIsolation() throws SQLException {
		// TODO Auto-generated method stub
		return conn.getTransactionIsolation();
	}

	@Override
	public SQLWarning getWarnings() throws SQLException {
		// TODO Auto-generated method stub
		return conn.getWarnings();
	}

	@Override
	public void clearWarnings() throws SQLException {
		// TODO Auto-generated method stub
		conn.clearWarnings();
	}

	@Override
	public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
		// TODO Auto-generated method stub
		return conn.createStatement(resultSetType, resultSetConcurrency);
	}

	@Override
	public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency)
			throws SQLException {
		// TODO Auto-generated method stub
		return conn.prepareStatement(sql, resultSetType, resultSetConcurrency);
	}

	@Override
	public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
		// TODO Auto-generated method stub
		return conn.prepareCall(sql, resultSetType, resultSetConcurrency);
	}

	@Override
	public Map<String, Class<?>> getTypeMap() throws SQLException {
		// TODO Auto-generated method stub
		return conn.getTypeMap();
	}

	@Override
	public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
		// TODO Auto-generated method stub
		conn.setTypeMap(map);
	}

	@Override
	public void setHoldability(int holdability) throws SQLException {
		// TODO Auto-generated method stub
		conn.setHoldability(holdability);
	}

	@Override
	public int getHoldability() throws SQLException {
		// TODO Auto-generated method stub
		return conn.getHoldability();
	}

	@Override
	public Savepoint setSavepoint() throws SQLException {
		// TODO Auto-generated method stub
		return conn.setSavepoint();
	}

	@Override
	public Savepoint setSavepoint(String name) throws SQLException {
		// TODO Auto-generated method stub
		return conn.setSavepoint(name);
	}

	@Override
	public void rollback(Savepoint savepoint) throws SQLException {
		// TODO Auto-generated method stub
		conn.rollback();
	}

	@Override
	public void releaseSavepoint(Savepoint savepoint) throws SQLException {
		// TODO Auto-generated method stub
		conn.releaseSavepoint(savepoint);
	}

	@Override
	public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability)
			throws SQLException {
		// TODO Auto-generated method stub
		return conn.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability);
	}

	@Override
	public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency,
			int resultSetHoldability) throws SQLException {
		// TODO Auto-generated method stub
		return conn.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
	}

	@Override
	public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency,
			int resultSetHoldability) throws SQLException {
		// TODO Auto-generated method stub
		return conn.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
	}

	@Override
	public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
		// TODO Auto-generated method stub
		return conn.prepareStatement(sql, autoGeneratedKeys);
	}

	@Override
	public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
		// TODO Auto-generated method stub
		return conn.prepareStatement(sql, columnIndexes);
	}

	@Override
	public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
		// TODO Auto-generated method stub
		return conn.prepareStatement(sql, columnNames);
	}

	@Override
	public Clob createClob() throws SQLException {
		// TODO Auto-generated method stub
		return conn.createClob();
	}

	@Override
	public Blob createBlob() throws SQLException {
		// TODO Auto-generated method stub
		return conn.createBlob();
	}

	@Override
	public NClob createNClob() throws SQLException {
		// TODO Auto-generated method stub
		return conn.createNClob();
	}

	@Override
	public SQLXML createSQLXML() throws SQLException {
		// TODO Auto-generated method stub
		return conn.createSQLXML();
	}

	@Override
	public boolean isValid(int timeout) throws SQLException {
		// TODO Auto-generated method stub
		return conn.isValid(timeout);
	}

	@Override
	public void setClientInfo(String name, String value) throws SQLClientInfoException {
		// TODO Auto-generated method stub
		conn.setClientInfo(name, value);
	}

	@Override
	public void setClientInfo(Properties properties) throws SQLClientInfoException {
		// TODO Auto-generated method stub
		conn.setClientInfo(properties);
	}

	@Override
	public String getClientInfo(String name) throws SQLException {
		// TODO Auto-generated method stub
		return conn.getClientInfo(name);
	}

	@Override
	public Properties getClientInfo() throws SQLException {
		// TODO Auto-generated method stub
		return conn.getClientInfo();
	}

	@Override
	public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
		// TODO Auto-generated method stub
		return conn.createArrayOf(typeName, elements);
	}

	@Override
	public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
		// TODO Auto-generated method stub
		return conn.createStruct(typeName, attributes);
	}

	@Override
	public void setSchema(String schema) throws SQLException {
		// TODO Auto-generated method stub
		conn.setSchema(schema);
	}

	@Override
	public String getSchema() throws SQLException {
		// TODO Auto-generated method stub
		return conn.getSchema();
	}

	@Override
	public void abort(Executor executor) throws SQLException {
		// TODO Auto-generated method stub
		conn.abort(executor);
	}

	@Override
	public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException {
		// TODO Auto-generated method stub
		conn.setNetworkTimeout(executor, milliseconds);
	}

	@Override
	public int getNetworkTimeout() throws SQLException {
		// TODO Auto-generated method stub
		return conn.getNetworkTimeout();
	}

}

裝飾類:MyConnectionWrapper

import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;

public class MyConnectionWrapper extends ConnectionWrapper{

	private Connection conn;
	private List<Connection> connList;
	public MyConnectionWrapper(Connection conn,List<Connection> connList) {
		super(conn);
		this.conn = conn;
		this.connList = connList;
	}
	
	@Override
	public void close() throws SQLException {
		connList.add(conn);
	}
	
}

MyDatesource

package com.datasource.demo1;

import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

import javax.sql.DataSource;

import com.jdbc.utils.JDBCUtils;

public class MyDataSource implements DataSource {
	public List<Connection> connList = new ArrayList<Connection>();
	
	//初始化提供連線
	public MyDataSource(){
		for (int i = 1; i <= 3; i++) {
			connList.add(JDBCUtils.getConnection());
		}
	}
	
	//從連線池獲得連線
	@Override
	public Connection getConnection() throws SQLException {
		Connection conn = connList.remove(0);
		MyConnectionWrapper connWraper = new MyConnectionWrapper(conn, connList);
		return connWraper;
	}
	
	//下列程式碼不用管
	@Override
	public Logger getParentLogger() throws SQLFeatureNotSupportedException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public <T> T unwrap(Class<T> iface) throws SQLException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public boolean isWrapperFor(Class<?> iface) throws SQLException {
		// TODO Auto-generated method stub
		return false;
	}

	

	@Override
	public Connection getConnection(String username, String password) throws SQLException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public PrintWriter getLogWriter() throws SQLException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void setLogWriter(PrintWriter out) throws SQLException {
		// TODO Auto-generated method stub

	}

	@Override
	public void setLoginTimeout(int seconds) throws SQLException {
		// TODO Auto-generated method stub

	}

	@Override
	public int getLoginTimeout() throws SQLException {
		// TODO Auto-generated method stub
		return 0;
	}

}

DataSourceDemo

package com.datasource.demo1;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.junit.Test;

import com.jdbc.utils.JDBCUtils;

public class DataSourceDemo {
	@Test
	public void demo1() {
		Connection conn = null;
		PreparedStatement pStatement = null;
		ResultSet rs = null;
		MyDataSource dataSource = null;
		try {
			dataSource = new MyDataSource();
			conn = dataSource.getConnection();
			
			String sql = "select * from account";
			pStatement = conn.prepareStatement(sql);
			rs = pStatement.executeQuery();
			while (rs.next()) {
				System.out.println(rs.getInt("id")+" "+rs.getString("name")+" "+rs.getDouble("money"));
				
			}
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			JDBCUtils.release(rs, pStatement, conn);//這個時候的conn傳入呼叫是歸還而不是銷燬。
			
		}
	}
}

開源連線池的使用

使用maven倉庫去下載且導包,本文暫不演示。

Druid

手動設定配置資訊方式:

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

import org.junit.Test;

import com.alibaba.druid.pool.DruidDataSource;
import com.jdbc.utils.JDBCUtils;

public class DruidDemo1 {
	@Test
	public void name() {
		Connection conn = null;
		PreparedStatement pStatement = null;
		ResultSet rs = null;
		try {
			DruidDataSource dataSource = new DruidDataSource();
			/*---手動設定資料庫的資訊---*/
			dataSource.setDriverClassName("com.mysql.jdbc.Driver");
			dataSource.setUrl("jdbc:mysql:///jdbc");
			dataSource.setUsername("root");
			dataSource.setPassword("");
			conn = dataSource.getConnection();
			String sql = "select * from account";
			pStatement = conn.prepareStatement(sql);
			rs = pStatement.executeQuery();
			while (rs.next()) {
				System.out.println(rs.getInt("id")+" "+rs.getString("name")+" "+rs.getDouble("money"));
			}
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			JDBCUtils.release(rs,pStatement ,conn);//自己的工具類,但是這些開源資料庫內部都是進行歸還而不是銷燬。
		}
	}
}

使用屬性檔案去配置資訊方式:
注意:屬性檔案中的key要求一致

import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Properties;

import javax.sql.DataSource;

import org.junit.Test;

import com.alibaba.druid.pool.DruidDataSourceFactory;
import com.jdbc.utils.JDBCUtils;

public class DruidDemo1 {
	@Test
	public void name() {
		Connection conn = null;
		PreparedStatement pStatement = null;
		ResultSet rs = null;
		try {
			Properties p = new Properties();
			p.load(new FileInputStream("src/db.properties"));
			
			DataSource dataSource = DruidDataSourceFactory.createDataSource(p);
			
			conn = dataSource.getConnection();
			String sql = "select * from account";
			pStatement = conn.prepareStatement(sql);
			rs = pStatement.executeQuery();
			while (rs.next()) {
				System.out.println(rs.getInt("id")+" "+rs.getString("name")+" "+rs.getDouble("money"));
			}
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			JDBCUtils.release(rs,pStatement ,conn);
		}
	}
}

C3P0

手動配置:

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

import org.junit.Test;

import com.jdbc.utils.JDBCUtils;
import com.mchange.v2.c3p0.ComboPooledDataSource;

public class C3P0Demo1 {
	@Test
	public void demo1() {
		Connection conn = null;
		PreparedStatement pStatement = null;
		ResultSet rs = null;
		try {
			ComboPooledDataSource dataSource = new ComboPooledDataSource();
			/*---手動設定資料庫的資訊---*/
			dataSource.setDriverClass("com.mysql.jdbc.Driver");
			dataSource.setJdbcUrl("jdbc:mysql:///jdbc");
			dataSource.setUser("root");
			dataSource.setPassword("");
			conn = dataSource.getConnection();
			String sql = "select * from account";
			pStatement = conn.prepareStatement(sql);
			rs = pStatement.executeQuery();
			while (rs.next()) {
				System.out.println(rs.getInt("id")+" "+rs.getString("name")+" "+rs.getDouble("money"));
			}
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			JDBCUtils.release(rs,pStatement ,conn);//自己的工具類,但是這些開源資料庫內部都是進行歸還而不是銷燬。
		}
	}
}

使用XML去配置資訊方式:
檔案資訊:

<?xml version="1.0" encoding="UTF-8"?>

<c3p0-config>
	<default-config>
		<property name="driverClass">com.mysql.jdbc.Driver</property>
		<property name="jdbcUrl">jdbc:mysql:///jdbc</property>
		<property name="user">root</property>
		<property name="password"></property>
		<property name="initialPoolSize">5</property>
		<property name="minPoolSize">5</property>
		<property name="maxPoolSize">20</property>
	</default-config>

	<!-- This app is massive! -->
	<named-config name="oracle">
		<property name="driverClass">com.mysql.jdbc.Driver</property>
		<property name="jdbcUrl">jdbc:mysql::///jdbc</property>
		<property name="user">root</property>
		<property name="password"></property>
	</named-config>
</c3p0-config>

測試方法

	@Test
	public void demo2() {
		Connection conn = null;
		PreparedStatement pStatement = null;
		ResultSet rs = null;
		try {
			ComboPooledDataSource dataSource = new ComboPooledDataSource();
			//如果ComboPooledDataSource dataSource = new ComboPooledDataSource("oracle");
			//則可以使用oracle的配置資訊,若引數錯誤則使用預設,不會報錯
			/*建立連線池會自動去src下尋找c3p0-config檔案*/
			conn = dataSource.getConnection();
			String sql = "select * from account";
			pStatement = conn.prepareStatement(sql);
			rs = pStatement.executeQuery();
			while (rs.next()) {
				System.out.println(rs.getInt("id")+" "+rs.getString("name")+" "+rs.getDouble("money"));
			}
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			JDBCUtils.release(rs,pStatement ,conn);//自己的工具類,但是這些開源資料庫內部都是進行歸還而不是銷燬。
		}
	}

根據開源連線池,優化自己寫的工具類

以c3p0為例子:可以進行如下簡單改寫

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;


import javax.sql.DataSource;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class JDBCUtils2 {
	private static final ComboPooledDataSource dataSoure = new ComboPooledDataSource();
	
	public static Connection getConnection() throws SQLException {
		return dataSoure.getConnection();
	}
	
	//獲得連線池
	public static DataSource getDataSource() {
		return dataSoure;
	}
	
	
	public static void release(ResultSet rs,Statement statement,Connection conn) {
		if (rs!=null) {
			try {
				rs.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			rs = null;
		}
		if (statement!=null) {
			try {
				statement.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			statement = null;
		}
		if (conn!=null) {
			try {
				conn.close();
			} catch (SQLException e) {	
				e.printStackTrace();
			}
			conn = null;
		}
	}
	public static void release(Statement statement,Connection conn) {
		if (statement!=null) {
			try {
				statement.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			statement = null;
		}
		if (conn!=null) {
			try {
				conn.close();
			} catch (SQLException e) {	
				e.printStackTrace();
			}
			conn = null;
		}
	}
	
	
}

測試方法,如下:

@Test
	public void demo3() {
		Connection conn = null;
		PreparedStatement pStatement = null;
		ResultSet rs = null;
		try {
			conn = JDBCUtils2.getConnection();
			String sql = "select * from account";
			pStatement = conn.prepareStatement(sql);
			rs = pStatement.executeQuery();
			while (rs.next()) {
				System.out.println(rs.getInt("id")+" "+rs.getString("name")+" "+rs.getDouble("money"));
			}
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			JDBCUtils2.release(rs,pStatement ,conn);//自己的工具類,但是這些開源資料庫內部都是進行歸還而不是銷燬。
		}
	}

四、Dbutils

是一個對JDBC進行簡單封裝的開源工具類庫,使用它能夠簡化JDBC應用程式的開發,同時也不會影響程式的效能。
在一般情況下如果執行CRUD的操作:
構造:
QueryRunner(Datasource ds) ;
方法:
int update(String sql , Object…args) ;
T query(string sql , ResultSetHandler rsh,Object…args);

如果有事務管理的話使用另一套完成CRUD的操作
因為需要獲取連線進行提交操作。
構造:
QueryRunner();
方法:
int update(Connection conn , string sql , Object…args) ;
T query(Connection conn, string sql , ResultSetHandler rsh,Object…args) ;

增刪改操作

增加:

import java.sql.SQLException;

import org.apache.commons.dbutils.QueryRunner;
import org.junit.Test;

import com.jdbc.utils.JDBCUtils2;

public class DBUtilsDemo1 {
	@Test
	public void demo1() throws SQLException {
		QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
		queryRunner.update("insert into account values(null,?,?)","ddd",10000);
		
	}
}

執行後檢視sql如圖,插入成功:
在這裡插入圖片描述
修改: 原理如上只需修改即可。

queryRunner.update("update account set name=?,money=? where id=?","eee",20000,4);

刪除: 同上

queryRunner.update("delete from account where id = ?",4);

查詢操作

通常我們查詢之後目的不僅僅是列印到控制檯,而是把資料儲存在物件中或者是儲存到物件的集合中。
建立一個儲存的實體類: Account

public class Account {
	private Integer id;
	private String name;
	private Double money;
	
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Double getMoney() {
		return money;
	}
	public void setMoney(Double money) {
		this.money = money;
	}
	public String toString() {
		return "Account [id=" + id + ", name=" + name + ", money=" + money + "]";
	}
	
}

查詢一條記錄:

import java.sql.ResultSet;
import java.sql.SQLException;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.ResultSetHandler;
import org.junit.Test;

import com.jdbc.utils.JDBCUtils2;

/*查詢*/
public class DBUtilsDemo2 {
	@Test
	public void demo() throws SQLException {
		QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
		Account account = queryRunner.query("select * from account where id = ?", new ResultSetHandler<Account>(){

			@Override
			public Account handle(ResultSet rs) throws SQLException {
				Account account = new Account();
				while (rs.next()) {
					account.setId(rs.getInt("id"));
					account.setName(rs.getString("name"));
					account.setMoney(rs.getDouble("money"));
				}
				return account;
			}
			
		},1);
		
		System.out.println(account);
		
	}
}

查詢多條語句並且遍歷:
測試方法如下:

	@Test
	public void demo2() throws SQLException {
		QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
		List<Account> list= queryRunner.query("select * from account", new ResultSetHandler<List<Account>>(){
			@Override
			public List<Account> handle(ResultSet rs) throws SQLException {
				List<Account> list = new ArrayList<Account>();
				while (rs.next()) {
					Account account = new Account();
					account.setId(rs.getInt("id"));
					account.setName(rs.getString("name"));
					account.setMoney(rs.getDouble("money"));
					list.add(account);
				}
				return list;
			}

			
		});
		
		for(Account a : list) {
			System.out.println(a);
		}
		
	}

執行如下圖:
在這裡插入圖片描述

ResultSetHandler的實現類

利用這個知識可以幫助我們快速完成資料的封裝。

1.ArrayHandler和ArrayListHandler

ArrayHandler : 將一條記錄封裝到一個陣列當中。這個陣列是object[]。
ArrayListHandler : 將多條記錄封裝到一個裝有object[]的List集合中,即list集合中存放object[]。
測試程式碼如下:


import java.sql.SQLException;
import java.util.Arrays;
import java.util.List;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.ArrayHandler;
import org.apache.commons.dbutils.handlers.ArrayListHandler;
import org.junit.Test;

import com.jdbc.utils.JDBCUtils2;

public class DButilsDemo3 {
	@Test     //查詢一條記錄
	public void demo1() throws SQLException {
		QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
		Object[] objs = queryRunner.query("select * from account where id = ?", new ArrayHandler() ,1);
		System.out.println(Arrays.toString(objs));
		System.out.println("------------");
	}
	
	@Test     //查詢多條記錄
	public void demo2() throws SQLException {
		QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
		List<Object[]> list = queryRunner.query("select * from account", new ArrayListHandler());
		for(Object[] obj : list) {
			System.out.println(Arrays.toString(obj));
		}
		
	}
}

執行結果如下:
在這裡插入圖片描述

2.BeanHandler和BeanListHandler

BeanHandler : 將一條記錄封裝到一個JavaBean 中。
BeanListHandler : 將多條記錄封裝到一個裝有JavaBean的 List集合中。
下列僅給出測試方法程式碼:

	@Test     //查詢一條記錄
	public void demo3() throws SQLException {
		QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
		Account account = queryRunner.query("select * from account where id = ?", new BeanHandler<Account>(Account.class),2);
		System.out.println(account);
	}
	@Test     //查詢多條記錄
	public void demo4() throws SQLException {
		QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
		List<Account> list = (List<Account>) queryRunner.query("select * from account", new BeanListHandler<Account>(Account.class));
		for(Account acc : list) {
			System.out.println(acc);
		}
	}

3.MapHandler和MapListHandler

MapHandler : 將一條記錄封裝到一個Map集合中,Map的 key是列名,Map的value就是表中列的記錄值。
MapListHandler :將多條記錄封裝到一個裝有Map的List集合中。

	@Test     //查詢一條記錄
	public void demo5() throws SQLException {
		QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
		Map<String, Object> map = queryRunner.query("select * from account where id = ?", new MapHandler(),2);
		System.out.println(map);
	}
	@Test     //查詢多條記錄
	public void demo6() throws SQLException {
		QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
		List<Map<String, Object>> list = queryRunner.query("select * from account", new MapListHandler());
		for(Map<String, Object> map : list) {
			System.out.println(map);
		}
	}

4.ColumnListHandler、ScalarHandler、KeyedHandler

ColumnListHandler : 將資料中的某列封裝到List集合中
ScalarHandler : 將單個值封裝
KeyedHandler : 將一條記錄封裝到一個Map集合中。將多條記錄封裝到一個裝有Map集合的Map集合中。而且外面的Map的key是可以指定的

	@Test     //查詢某列記錄
	public void demo7() throws SQLException {
		QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
		List<Object> list = queryRunner.query("select * from account", new ColumnListHandler<>("name"));
		for(Object obj : list) {
			System.out.println(obj);
		}
	}
	
	@Test     //查詢單值記錄
	public void demo8() throws SQLException {
		QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
		Object obj = queryRunner.query("select count(*) from account", new ScalarHandler<>());
		System.out.println(obj);
	}
	
	@Test     //可以指定外面的key
	public void demo9() throws SQLException {
		QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
		Map<Object, Map<String, Object>> map = queryRunner.query("select * from account", new KeyedHandler<>("name"));
		for(Object o : map.keySet()) {
			System.out.println(o + "->" + map.get(o)); // 根據鍵值獲取values,這裡的values是map集合
		}
	}

總結

如果你看到這裡,恭喜你,你會JDBC的基礎操作了,多去實戰吧!

相關文章