Java—JDBC技術

JDBC技術: java數據庫連接技術!

接口:

Connection:  連接對象

Statement:   執行命令對象: 把SQL語句發送到數據庫執行

ResultSet:    (在線式)結果集接口, 必須要保持與數據庫的連接!

 

開發步驟:

1. 建項目,引入數據庫驅動包

2. 加載驅動   

Class.forName(..);

3. 獲取連接對象

4. 創建執行sql語句的stmt對象;  寫sql

5. 執行sql

a) 更新    delete/insert/update

i. executeUpdate();       

b) 查詢    select

i. executeQuery();

6. 關閉/異常

一: 預編譯sql處理(防止sql注入)

 

-- 創建數據庫

CREATE DATABASE jdbc_demo DEFAULT CHARACTER SET utf8;i

-- 創建表

USE jdbc_demo;

CREATE TABLE admin(

    id INT PRIMARY KEY AUTO_INCREMENT,

    userName VARCHAR(20),

    pwd VARCHAR(20)

)

-Statement      執行SQL命令

|-- CallableStatement,     執行存儲過程

|-- PreparedStatement    預編譯SQL語句執行

使用預編譯SQL語句的命令對象,好處:

1. 避免了頻繁sql拼接 (可以使用佔位符)

2. 可以防止sql注入

public class App {
	
	// 連接參數
	//private String url = "jdbc:mysql://localhost:3306/jdbc_demo";
	private String url = "jdbc:mysql:///jdbc_demo";
	private String user = "root";
	private String password = "root";
	
	private Connection con;
	private Statement stmt;
	private PreparedStatement pstmt;
	private ResultSet rs;
	

	// 1. 沒有使用防止sql注入的案例
	@Test
	public void testLogin() {
		
		// 1.0 模擬登陸的用戶名,密碼
		String userName = "tom";
		//String pwd = "8881";
		String pwd = " ' or 1=1 -- ";
		
		// SQL語句
		String sql = "select * from admin where userName='"+userName+"'  and pwd='"+pwd+"' ";
		System.out.println(sql);
		try {
			// 1.1 加載驅動,創建連接
			Class.forName("com.mysql.jdbc.Driver");		
			con = DriverManager.getConnection(url, user, password);
			// 1.2 創建stmt對象
			stmt = con.createStatement();
			// 1.3 執行查詢
			rs = stmt.executeQuery(sql);
			// 業務判斷
			if (rs.next()) {
				System.out.println("登陸成功, 編號:" + rs.getInt("id"));
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			// 1.4 關閉
			try {
				rs.close();
				stmt.close();
				con.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	
	
	// 2. 使用PreparedStatement, 防止sql注入
	@Test
	public void testLogin2() {
		
		// 1.0 模擬登陸的用戶名,密碼
		String userName = "tom";
		//String pwd = "8881";
		String pwd = " ' or 1=1 -- ";
		
		// SQL語句
		String sql = "select * from admin where userName=?  and pwd=? ";
		try {
			// 1.1 加載驅動,創建連接
			Class.forName("com.mysql.jdbc.Driver");		
			con = DriverManager.getConnection(url, user, password);
			// 1.2 創建pstmt對象
			pstmt = con.prepareStatement(sql);   // 對sql語句預編譯
			// 設置佔位符值
			pstmt.setString(1, userName);
			pstmt.setString(2, pwd);
			
			// 1.3 執行
			rs = pstmt.executeQuery();
			if (rs.next()) {
				System.out.println("登陸成功," + rs.getInt("id"));
			}
			
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			// 1.4 關閉
			try {
				rs.close();
				pstmt.close();
				con.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	
	
}

二:存儲過程

 


 

-- 定義分隔符

DELIMITER $$

CREATE PROCEDURE proc_login()

BEGIN

   SELECT * FROM admin;

END $$

 

-- 調用

CALL proc_login;

public class App_call {
	
	// 全局參數
	private Connection con;
	private Statement stmt;
	private PreparedStatement pstmt;
	private CallableStatement cstmt;  // 存儲過程
	private ResultSet rs;
	

	// 程序中調用存儲過程
	@Test
	public void testCall() throws Exception {
		
		try {
			//1 . 創建連接
			con = JdbcUtil.getConnection();
			//2.  創建執行存儲過程的stmt對象
			CallableStatement cstmt = con.prepareCall("CALL proc_login");
			//3.  執行(存儲過程)
			rs = cstmt.executeQuery();
			
			// 遍歷結果,測試
			if (rs.next()) {
				String name = rs.getString("userName");
				String pwd = rs.getString("pwd");
				// 測試
				System.out.println(name + pwd);
			}
			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

 


 

三:批處理

 

 

 

很多時候,需要批量執行sql語句!

需求:批量保存信息!  

設計:

AdminDao

Public  void  save(List<Admin list){    // 目前用這種方式

// 循環

// 保存  (批量保存)

}

技術:

|-- Statement

批處理相關方法

void addBatch(String sql)     添加批處理

void clearBatch()            清空批處理

int[] executeBatch()         執行批處理

 

 

實現:
	Admin.java         實體類封裝數據
	AdminDao.java      封裝所有的與數據庫的操作
	App.java           測試
public class Admin {

	private String userName;
	private String pwd;

public class App {
	// 測試批處理操作
	@Test
	public void testBatch() throws Exception {
		
		// 模擬數據
		List<Admin> list = new ArrayList<Admin>();
		for (int i=1; i<21; i++) {
			Admin admin = new Admin();
			admin.setUserName("Jack" + i);
			admin.setPwd("888" + i);
			list.add(admin);
		}
		
		// 保存
		AdminDao dao = new AdminDao();
		dao.save(list);
	}
}



// 封裝所有的與數據庫的操作
public class AdminDao {
	
	// 全局參數
	private Connection con;
	private PreparedStatement pstmt;
	private ResultSet rs;

	// 批量保存管理員
	public void save(List<Admin> list) {
		// SQL
		String sql = "INSERT INTO admin(userName,pwd) values(?,?)";
		
		try {
			
			// 獲取連接
			con = JdbcUtil.getConnection();
			// 創建stmt 
			pstmt = con.prepareStatement(sql);   		// 【預編譯SQL語句】
			
			for (int i=0; i<list.size(); i++) {
				Admin admin = list.get(i);
				// 設置參數
				pstmt.setString(1, admin.getUserName());
				pstmt.setString(2, admin.getPwd());
				
				// 添加批處理
				pstmt.addBatch();						// 【不需要傳入SQL】
				
				// 測試:每5條執行一次批處理
				if (i % 5 == 0) {
					// 批量執行 
					pstmt.executeBatch();
					// 清空批處理
					pstmt.clearBatch();
				}
				
			}
			
			// 批量執行 
			pstmt.executeBatch();
			// 清空批處理
			pstmt.clearBatch();
			
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JdbcUtil.closeAll(con, pstmt, rs);
		}
	}
}

 

 

 

 

 

 

四:插入數據,獲取自增長值

 

部門與員工,

一對多的關係

 

ü 設計數據庫:

員工表 (外鍵表) 【員工表有一個外鍵字段,引用了部門表的主鍵】

部門表(主鍵表)

總體思路:

保存員工及其對應的部門!

步驟:

1. 先保存部門

2. 再得到部門主鍵,再保存員工

部門
CREATE TABLE dept(
   deptId INT PRIMARY KEY AUTO_INCREMENT,
   deptName VARCHAR(20)
);
-- 員工
CREATE TABLE employee(
   empId INT PRIMARY KEY AUTO_INCREMENT,
   empName VARCHAR(20),
   dept_id  INT   --  外鍵字段   
);
-- 給員工表添加外鍵約束
ALTER TABLE employee ADD CONSTRAINT FK_employee_dept_deptId
	FOREIGN KEY(dept_id) REFERENCES dept(deptId) ;




public class EmpDao {
	
	private Connection con;
	private PreparedStatement pstmt;
	private ResultSet rs;

	// 保存員工,同時保存關聯的部門
	public void save(Employee emp){
		
		// 保存部門
		String sql_dept = "insert into dept(deptName) values(?)";
		// 保存員工
		String sql_emp = "INSERT INTO employee (empName,dept_id) VALUES (?,?)";
		// 部門id
		int deptId = 0;
		
		try {
			// 連接
			con = JdbcUtil.getConnection();
			
			/*****保存部門,獲取自增長*******/
			// 【一、需要指定返回自增長標記】
			pstmt = con.prepareStatement(sql_dept,Statement.RETURN_GENERATED_KEYS);
			// 設置參數
			pstmt.setString(1, emp.getDept().getDeptName());
			// 執行
			pstmt.executeUpdate();
			
			// 【二、獲取上面保存的部門子增長的主鍵】
			rs =  pstmt.getGeneratedKeys();
			// 得到返回的自增長字段
			if (rs.next()) {
				deptId = rs.getInt(1);
			}
			
			/*****保存員工*********/
			pstmt = con.prepareStatement(sql_emp);
			// 設置參數
			pstmt.setString(1, emp.getEmpName());
			pstmt.setInt(2, deptId);
			pstmt.executeUpdate();
			
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JdbcUtil.closeAll(con, pstmt, rs);
		}
	}
}

五:事物

事務使指一組最小邏輯操作單元,裏面有多個操作組成。 組成事務的每一部分必須要同時提交成功,如果有一個操作失敗,整個操作就回滾。

事務ACID特性

l原子性(Atomicity)
原子性是指事務是一個不可分割的工作單位,事務中的操作要麼都發生,要麼都不發生。 

l一致性(Consistency)
事務必須使數據庫從一個一致性狀態變換到另外一個一致性狀態。

隔離性(Isolation)
事務的隔離性是多個用戶併發訪問數據庫時,數據庫爲每一個用戶開啓的事務,不能被其他事務的操作數據所幹擾,多個併發事務之間要相互隔離。

持久性(Durability)
持久性是指一個事務一旦被提交,它對數據庫中數據的改變就是永久性的,接下來即使數據庫發生故障也不應該對其有任何影響

 事務的特性:

原子性,是一個最小邏輯操作單元 !

一致性,事務過程中,數據處於一致狀態。

持久性, 事務一旦提交成功,對數據的更改會反映到數據庫中。

隔離性, 事務與事務之間是隔離的。

 事物所需的方法:

 

void setAutoCommit(boolean autoCommit) ;  設置事務是否自動提交,如果設置爲false,表示手動提交事務。

void commit() ();   手動提交事務

void rollback() ;   回滾(出現異常時候,所有已經執行成功的代碼需要回退到事務開始前的狀態。)

Savepoint setSavepoint(String name) 

 賬戶表
CREATE TABLE account(
   id INT PRIMARY KEY AUTO_INCREMENT,
   accountName VARCHAR(20),
   money DOUBLE
);
-- 轉賬
UPDATE account SET money=money-1000 WHERE accountName='張三';
UPDATE account SET money=money+1000 WHERE accountName='李四';
public class AccountDao {

	// 全局參數
	private Connection con;
	private PreparedStatement pstmt;

	// 1. 轉賬,沒有使用事務
	public void trans1() {

		String sql_zs = "UPDATE account SET money=money-1000 WHERE accountName='張三';";
		String sql_ls = "UPDATE account SET money=money+1000 WHERE accountName='李四';";

		try {
			con = JdbcUtil.getConnection(); // 默認開啓的隱士事務
			con.setAutoCommit(true);

			/*** 第一次執行SQL ***/
			pstmt = con.prepareStatement(sql_zs);
			pstmt.executeUpdate();

			/*** 第二次執行SQL ***/
			pstmt = con.prepareStatement(sql_ls);
			pstmt.executeUpdate();

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JdbcUtil.closeAll(con, pstmt, null);
		}

	}

	// 2. 轉賬,使用事務
	public void trans2() {

		String sql_zs = "UPDATE account SET money=money-1000 WHERE accountName='張三';";
		String sql_ls = "UPDATE1 account SET money=money+1000 WHERE accountName='李四';";

		try {
			con = JdbcUtil.getConnection(); // 默認開啓的隱士事務
			// 一、設置事務爲手動提交
			con.setAutoCommit(false);

			/*** 第一次執行SQL ***/
			pstmt = con.prepareStatement(sql_zs);
			pstmt.executeUpdate();

			/*** 第二次執行SQL ***/
			pstmt = con.prepareStatement(sql_ls);
			pstmt.executeUpdate();

		} catch (Exception e) {
			try {
				// 二、 出現異常,需要回滾事務
				con.rollback();
			} catch (SQLException e1) {
			}
			e.printStackTrace();
		} finally {
			try {
				// 三、所有的操作執行成功, 提交事務
				con.commit();
				JdbcUtil.closeAll(con, pstmt, null);
			} catch (SQLException e) {
			}
		}

	}

	// 3. 轉賬,使用事務, 回滾到指定的代碼段
	public void trans() {
		// 定義個標記
		Savepoint sp = null;
		
		// 第一次轉賬
		String sql_zs1 = "UPDATE account SET money=money-1000 WHERE accountName='張三';";
		String sql_ls1 = "UPDATE account SET money=money+1000 WHERE accountName='李四';";
		
		// 第二次轉賬
		String sql_zs2 = "UPDATE account SET money=money-500 WHERE accountName='張三';";
		String sql_ls2 = "UPDATE1 account SET money=money+500 WHERE accountName='李四';";

		try {
			con = JdbcUtil.getConnection(); // 默認開啓的隱士事務
			con.setAutoCommit(false);       // 設置事務手動提交

			/*** 第一次轉賬 ***/
			pstmt = con.prepareStatement(sql_zs1);
			pstmt.executeUpdate();
			pstmt = con.prepareStatement(sql_ls1);
			pstmt.executeUpdate();
			
			// 回滾到這個位置?
			sp = con.setSavepoint(); 
			
			
			/*** 第二次轉賬 ***/
			pstmt = con.prepareStatement(sql_zs2);
			pstmt.executeUpdate();
			pstmt = con.prepareStatement(sql_ls2);
			pstmt.executeUpdate();
			

		} catch (Exception e) {
			try {
				// 回滾 (回滾到指定的代碼段)
				con.rollback(sp);
			} catch (SQLException e1) {
			}
			e.printStackTrace();
		} finally {
			try {
				// 提交
				con.commit();
			} catch (SQLException e) {
			}
			JdbcUtil.closeAll(con, pstmt, null);
		}

	}
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章