Jdbc--具體代碼實現

1、獲取連接

1) 把jar包複製到項目目錄下
2) 把jar包加入項目的buildpath環境變量中, 使得classpath也和buildpath一致, 讓類加載器便於加載具體子類
3) 反射加載驅動程序類, 會自動註冊驅動程序

4) 通過驅動程序管理器獲取連接.

       在當前工程下創建jdbc.properties文件,其中內容如下(是連接Oracle、MySql數據庫所需要的四大參數分別是:加載驅動、獲取連接數據庫的IP及端口、用戶名,密碼),目的是方便調用和修改!
#driverClass = oracle.jdbc.driver.OracleDriver
#url = jdbc:oracle:thin:@127.0.0.1:1521:orcl
#user = scott
#password = tiger


driverClass = com.mysql.jdbc.Driver
url = jdbc:mysql://127.0.0.1:3306/company
user = root

password = 123456

獲取連接以及關閉連接的具體實現如下:

package com.atguigu.jdbc;

import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;

import org.junit.Test;
	
 /**
 * 獲取連接
 * 1) 把jar包複製到項目目錄下
 * 2) 把jar包加入項目的buildpath環境變量中, 使得classpath也和buildpath一致, 讓類加載器便於加載具體子類
 * 3) 反射加載驅動程序類, 會自動註冊驅動程序
 * 4) 通過驅動程序管理器獲取連接.
 * @author Administrator
 *
 */
public class DriverTest {
// 使用Properties類對象的getPropety方法與FileInputStream方法獲取文件中的內容,從而創建Connection對象
	@Test
	public void test5() throws Exception {
		Properties properties = new Properties();
		properties.load(new FileInputStream("jdbc.properties"));
		String driverClass = properties.getProperty("driverClass");
		String url = properties.getProperty("url");
		String user = properties.getProperty("user");
		String password = properties.getProperty("password");
		Class.forName(driverClass); // 只需要加載類, 類的靜態語句塊就會被執行, 創建驅動程序對象,並把此對象註冊到驅動程序管理器中.
		Connection connection = DriverManager.getConnection(url, user, password);
		System.out.println(connection);
		connection.close();
	}
}
鑑於每一次jdbc的操作都需要建立連接,我們將建立連接的方法、關閉資源的方法作爲固定方法放在JdbcUtil類中,方便使用:

package com.atguigu.jdbc;

import java.io.FileInputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

public class JdbcUtil {
	// 獲取建立連接對象
	public static Connection getConnection() throws IOException, 
													ClassNotFoundException, 
													SQLException {
		Properties properties = new Properties();
		properties.load(new FileInputStream("jdbc.properties"));
		String driverClass = properties.getProperty("driverClass");
		String url = properties.getProperty("url");
		String user = properties.getProperty("user");
		String password = properties.getProperty("password");
		Class.forName(driverClass); // 只需要加載類, 類的靜態語句塊就會被執行, 創建驅動程序對象,並把此對象註冊到驅動程序管理器中.
		Connection connection = DriverManager.getConnection(url, user, password);
		return connection;
	}

	// 關閉資源
	public static void close(Connection connection) {
		close(connection, null);
	}
	
	public static void close(Connection connection, Statement statement) {
		close(connection, statement, null);
	}
	
	public static void close(Connection connection, Statement statement, ResultSet resultSet) {
		if (resultSet != null) {
			try {
				resultSet.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		
		if (statement != null) {
			try {
				statement.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		
		if (connection != null) {
			try {
				connection.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
}

Statement類

建立連接成功,現在進行與數據庫的交流,與數據庫交流需要使用到Statement類,首先我們需要建立一個User類以與Statement類中創建的在company數據庫中的的user表相對應:

package com.atguigu.jdbc;

public class User {

	private String user;
	private String password;

	public User() {
	}

	public User(String user, String password) {
		super();
		this.user = user;
		this.password = password;
	}

	public String getUser() {
		return user;
	}

	public void setUser(String user) {
		this.user = user;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	@Override
	public String toString() {
		return "User [user=" + user + ", password=" + password + "]";
	}

}

使用Statement執行創建user表,並插入三行數據:

package com.atguigu.jdbc;

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

import org.junit.Test;

// 使用Statement執行創建user表,並插入三行數據
public class StatementTest {
	
	@Test
	public void test1() {
		Connection connection = null;
		Statement statement = null;
		try {
			connection = JdbcUtil.getConnection();
			statement = connection.createStatement();// 獲取執行體對象
			// 執行SQL
			// 創建user表
			String sql = "create table if not exists user(user varchar(50), password varchar(100))";
			int rows = statement.executeUpdate(sql); // 執行的DDL語句, 還可以執行DML
			System.out.println(rows + " rows affected..");
			rows = statement.executeUpdate("insert into user values('admin','admin')");
			System.out.println(rows + " rows affected..");
			rows = statement.executeUpdate("insert into user values('user1','user1')");
			System.out.println(rows + " rows affected..");
			rows = statement.executeUpdate("insert into user values('user2','123456')");
			System.out.println(rows + " rows affected..");
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JdbcUtil.close(connection, statement);
		}
	}
}
然而Statement存在弊端:不僅需要拼寫sql語句,並且存在SQL注入的問題,具體問題在下面的小例子中體現

package com.atguigu.jdbc;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.Scanner;

import org.junit.Test;

import java.sql.Statement;

public class TestStatement {

	// 弊端:需要拼寫sql語句,並且存在SQL注入的問題
	@Test
	public void testLogin() {
		Scanner scan = new Scanner(System.in);
		System.out.print("用戶名:");
		String userName = scan.nextLine();
		System.out.print("密   碼:");
		String password = scan.nextLine();

		String sql = "select user, password from user where user = '"
				+ userName + "' and password = '" + password + "'"; 
		
		System.out.println(sql);
		
		User user = get(sql, User.class);
		if(user != null){
			System.out.println("登陸成功!");
		}else{
			System.out.println("用戶名或密碼錯誤!");
		}
	}

	public <T> T get(String sql, Class<T> clazz) {// (sql, Customer.class)
		T t = null;

		Connection conn = null;
		Statement stam = null;
		ResultSet rs = null;
		try {
			conn = JdbcUtil.getConnection();

			stam = conn.createStatement();

			rs = stam.executeQuery(sql);

			// 獲取結果集的元數據
			ResultSetMetaData rsmd = rs.getMetaData();

			// 獲取結果集的列數
			int columnCount = rsmd.getColumnCount();

			if (rs.next()) {

				t = clazz.newInstance();

				for (int i = 0; i < columnCount; i++) {
					// //1. 獲取列的名稱
					// String columnName = rsmd.getColumnName(i+1);

					// 1. 獲取列的別名
					String columnName = rsmd.getColumnLabel(i + 1); // 注意:
																	// 獲取結果集中(相當於數據表)列的名稱(別名)

					// 2. 根據列名獲取對應數據表中的數據
					Object columnVal = rs.getObject(columnName);

					// 3. 將數據表中得到的數據,封裝進對象
					Field field = clazz.getDeclaredField(columnName); // 注意:反射根據Java中類的屬性獲取Field對象
					field.setAccessible(true);
					field.set(t, columnVal);
				}

			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
		//JDBCUtils.close(rs, stam, conn);
		}

		return t;
	}

}

此程序我們需要使用正確的用戶名,和對應的密碼才能進入登陸成功,通過上面的程序我們可知用戶名和密碼分別是:

+-------+----------+
| user  | password |
+-------+----------+
| admin | admin    |
| user1 | user1    |
| user2 | 123456   |
+-------+----------+

正常情況下我們登陸情況如下圖:



但是SQL高手通過如下操作就能登陸成功,這就是SQL注入的問題


PreparedStatement類

爲了防止SQL注入問題,於是我們需要使用他的子類PreparedStatement預編譯執行體,下面我們進行PreparedStatement的練習:

首先先創建Customer類
// 寫一個類Customer, 包含屬性name, gender, phone

package com.atguigu.jdbc;

public class Customer {

	private String name;
	private String gender;
	private String phone;

	public Customer(String name, int age, String gender, String phone,) {
		super();
		this.name = name;
		this.age = age;
		this.gender = gender;
		this.phone = phone;
	}

	public Customer() {
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getGender() {
		return gender;
	}

	public void setGender(String gender) {
		this.gender = gender;
	}

	public String getPhone() {
		return phone;
	}

	public void setPhone(String phone) {
		this.phone = phone;
	}

	@Override
	public String toString() {
		return "Customer [name=" + name + ", gender=" + gender + ", phone=" + phone + "]";
	}

}
在測試類中使用PreparedStatment執行體對象創建相應的表, 並插入2條數據。
public class PreparedStatementTest {	
	@Test
	public void exer1() {
		Connection connection = null;
		PreparedStatement preparedStatement = null;
		try {
			connection = JdbcUtil.getConnection();
			String sql = "create table if not exists customer(name varchar(30), gender enum('男','女') default '男', phone varchar(20))";
			preparedStatement = connection.prepareStatement(sql);
			 
			preparedStatement.executeUpdate();
			
			JdbcUtil.close(null, preparedStatement); // 在獲取新的預編譯對象前,一定要先關閉原來的.
			
			String sql2 = "insert into customer(name, gender, phone) values(?, ?, ?)";
			preparedStatement = connection.prepareStatement(sql2); // 要想重新執行新的SQL,必須再次預編譯
			
			preparedStatement.setString(1, "張三");
			preparedStatement.setString(2, "男");
			preparedStatement.setString(3, "13343493434");
			
			int rows = preparedStatement.executeUpdate();
			System.out.println(rows + " rows affected.");
			
			preparedStatement.setString(1, "李四");
			preparedStatement.setString(2, "女");
			preparedStatement.setString(3, "1322243434");
			
			rows = preparedStatement.executeUpdate();
			System.out.println(rows + " rows affected.");
			
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JdbcUtil.close(connection, preparedStatement);
		}
		// 並通過客戶端驗證.
	}
}
在Customer類中添加屬性int age,double weight,給Customer表中添加對應的列,並添加記錄

public class PreparedStatementTest {

// 在Customer類中添加屬性int age,double weight,給Customer表中添加對應的列,並添加記錄
	@Test
	public void test3() {
		Connection connection = null;
		PreparedStatement preparedStatement = null;
		try {
			connection = JdbcUtil.getConnection();
			String sql = "alter table customer add age int after name";
			preparedStatement = connection.prepareStatement(sql);
			preparedStatement.executeUpdate();
			JdbcUtil.close(null, preparedStatement);
			sql = "alter table customer add weight double";
			preparedStatement = connection.prepareStatement(sql);
			preparedStatement.executeUpdate();
			JdbcUtil.close(null, preparedStatement);
			
			sql = "insert into customer(name, age, gender, phone, weight) values (?, ?, ?, ?, ?)";
			preparedStatement = connection.prepareStatement(sql);
			preparedStatement.setString(1, "王五");
			preparedStatement.setInt(2, 50);
			preparedStatement.setString(3, "男");
			preparedStatement.setString(4, "134234234234");
			preparedStatement.setDouble(5, 98.5);
			
			int rows = preparedStatement.executeUpdate();
			System.out.println(rows + " rows affected");
			
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		} finally {
			JdbcUtil.close(connection, preparedStatement);
		}
	}

	// 添加記錄
	@Test
	public void test4() {
		Connection connection = null;
		PreparedStatement preparedStatement = null;
		try {
			connection = JdbcUtil.getConnection();
			String sql = "alter table customer add birthdate date";
			preparedStatement = connection.prepareStatement(sql);
			preparedStatement.executeUpdate();
			JdbcUtil.close(null, preparedStatement);
			
			sql = "insert into customer(name, age, gender, phone, weight, birthdate) values (?, ?, ?, ?, ?, ?)";
			preparedStatement = connection.prepareStatement(sql);
			preparedStatement.setString(1, "趙六");
			preparedStatement.setInt(2, 60);
			preparedStatement.setString(3, "女");
			preparedStatement.setString(4, "13882342323");
			preparedStatement.setDouble(5, 40);
			preparedStatement.setString(6, "1960-2-3");
			
			int rows = preparedStatement.executeUpdate();
			System.out.println(rows + " rows affected");
			
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		} finally {
			JdbcUtil.close(connection, preparedStatement);
		}
	}

	// 再添加記錄
	@Test
	public void test5() {
		Connection connection = null;
		PreparedStatement preparedStatement = null;
		try {
			connection = JdbcUtil.getConnection();
			String sql = "insert into customer(name, age, gender, phone, weight, birthdate) values (?, ?, ?, ?, ?, ?)";
			preparedStatement = connection.prepareStatement(sql);
			preparedStatement.setObject(1, "張七");
			preparedStatement.setObject(2, 20);
			preparedStatement.setObject(3, "女");
			preparedStatement.setObject(4, "1343434343");
			preparedStatement.setObject(5, 58.8);
			preparedStatement.setObject(6, "1980-3-8");
			
			int rows = preparedStatement.executeUpdate();
			if (rows == 1) {
				System.out.println("插入成功");
			}
			
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		} finally {
			JdbcUtil.close(connection, preparedStatement);
		}
	}
}
像上面那樣添加每次重複同樣的代碼,我們這裏設置通用的更新操作,並存入CommonUtil類中,作爲工具類通用更新操作。具體實現如下:

package com.atguigu.jdbc;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class CommonUtil {
	
	/**
	 * 通用更新操作
	 * @param sql 一個可以包含?的SQL語句
	 * @param values SQL中有多少個?,可變參數就有多少個具體值
	 * @return 更新以後影響的記錄數
	 */
	public static int commonUpdate(String sql, Object... values) throws  FileNotFoundException, 													ClassNotFoundException,												IOException, 
											SQLException {
		Connection connection = null;
		PreparedStatement preparedStatement = null;
		try {
			connection = JdbcUtil.getConnection(); // 獲取連接
			preparedStatement = connection.prepareStatement(sql); // 把帶有?的SQL編譯
			for (int i = 0; i < values.length; i++) { // 可變參數的長度就是?的個數
				preparedStatement.setObject(i + 1, values[i]); // 統一給所有的?替換成具體的值
			}
			return preparedStatement.executeUpdate(); // 替換完?或沒有?, 執行更新操作
		} finally {
			JdbcUtil.close(connection, preparedStatement); // 關閉資源
		}
	}
}

ResultSet類

至此我們實現了數據庫數據的建表,插入,更新,刪除,下面進行數據庫操作中比較難的查詢,需要使用到查詢結果集ResultSet類,循序漸進的練習實現如下:

package com.atguigu.jdbc;

import java.util.List;
import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;

import org.junit.Test;

public class ResultSetTest {
	@Test
	public void test1() {
		// select * from customer; 結果如下
		/*
		+------+------+--------+------------+--------+------------+
		| name | age  | gender | phone      | weight | birthdate  |
		+------+------+--------+------------+--------+------------+
		| 張七       20 | 女         | 1343434343 |   58.8 | 1980-03-08 |
		| 張九   |    40 | 男        | 1349932423 |   78.2 | 1977-08-08 |
		| 張三    |    3  | 女       | 1332324234 |     70 | 1979-08-08 |
		+------+------+--------+------------+--------+------------+
		*/
		Connection connection = null;
		PreparedStatement prepareStatment = null;
		ResultSet resultSet = null;
		try {
			connection = JdbcUtil.getConnection();
			String sql = "select * from customer";
			prepareStatment = connection.prepareStatement(sql);
			// 調用PreparedStatement對象的executeQuery方法產生ResultSet對象
			resultSet = prepareStatment.executeQuery();
			System.out.println(resultSet);
			while (resultSet.next()) { // 根據列的<span style="color:#ff0000;">序號</span>,先檢測當前遊標的下一行是否有數據, 如果有數據則移動遊標,並返回true, 如果沒有數據,直接返回false
				String name = resultSet.getString(1); // 獲取到的是當前遊標指向的行的第1列的值
				int age = resultSet.getInt(2);// 獲取到的是當前遊標指向的行的第2列的值
				String gender = resultSet.getString(3);// 獲取到的是當前遊標指向的行的第3列的值
				String phone = resultSet.getString(4);// 獲取到的是當前遊標指向的行的第4列的值
				double weight = resultSet.getDouble(5);// 獲取到的是當前遊標指向的行的第5列的值
				Date birthDate = resultSet.getDate(6);// 獲取到的是當前遊標指向的行的第6列的值
				System.out.println(name + "\t" + age + "\t" + gender + "\t" + phone + "\t" + weight + "\t" + birthDate);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JdbcUtil.close(connection, prepareStatment, resultSet);
		}
	}

	// 我們改變查詢列名的順序進行查詢操作,使用下標不能實現了,我們需要根據虛表中的<span style="color:#ff0000;">列名</span>進行查詢,並將查詢結果封裝成對象,添加到list集合中
	@Test
	public void test2() {
		Connection connection = null;
		PreparedStatement prepareStatment = null;
		ResultSet resultSet = null;
		try {
			connection = JdbcUtil.getConnection();
			// name | age  | gender | phone      | weight | birthdate  |
			String sql = "select name custName, gender, age, weight, phone, birthdate birth from customer";
			prepareStatment = connection.prepareStatement(sql);
			resultSet = prepareStatment.executeQuery();
			List<Customer> list = new ArrayList<Customer>();
			// 基於虛表的結果集
			while (resultSet.next()) {
				String name = resultSet.getString("custName");// 獲取到的是當前遊標指向的行的虛表中名字爲name列的值
				int age = resultSet.getInt("age");// 獲取到的是當前遊標指向的行的虛表中名字爲age列的值
				String gender = resultSet.getString("gender");// 獲取到的是當前遊標指向的行的虛表中名字爲gender列的值
				String phone = resultSet.getString("phone");
				double weight = resultSet.getDouble("weight");
				Date birthDate = resultSet.getDate("birth");
				Customer customer = new Customer(name, age, gender, phone, weight, birthDate); // 封裝成對象 O/R mapping
				list.add(customer);
			}
			
			// 遍歷集合
			for (Customer cust : list) {
				System.out.println(cust);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JdbcUtil.close(connection, prepareStatment, resultSet);
		}
	}
	// 我們改變查詢列名的順序進行查詢年齡大於任意數的操作,使用下標不能實現了,我們需要根據<span style="color:#ff0000;">虛表中的列名</span>進行查詢,並將查詢結果封裝成對象,添加到list集合中
	@Test
	public void test3() {
		Connection connection = null;
		PreparedStatement prepareStatment = null;
		ResultSet resultSet = null;
		try {
			connection = JdbcUtil.getConnection();
			// name | age  | gender | phone      | weight | birthdate  |
			String sql = "select name custName, gender, age, weight, phone, birthdate birth from customer where age > ?";
			prepareStatment = connection.prepareStatement(sql);
			
			prepareStatment.setObject(1, 20);
			
			resultSet = prepareStatment.executeQuery();
			List<Customer> list = new ArrayList<Customer>();
			// <span style="color:#ff0000;">基於虛表的結果集</span>
			while (resultSet.next()) {
				String name = resultSet.getString("custName");// 獲取到的是當前遊標指向的行的<span style="color:#ff0000;">虛表</span>中名字爲name列的值
				int age = resultSet.getInt("age");// 獲取到的是當前遊標指向的行的虛表中名字爲age列的值
				String gender = resultSet.getString("gender");// 獲取到的是當前遊標指向的行的虛表中名字爲gender列的值
				String phone = resultSet.getString("phone");
				double weight = resultSet.getDouble("weight");
				Date birthDate = resultSet.getDate("birth");
				Customer customer = new Customer(name, age, gender, phone, weight, birthDate); // 封裝成對象 O/R mapping
				list.add(customer);
			}
			
			// 遍歷集合
			for (Customer cust : list) {
				System.out.println(cust);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JdbcUtil.close(connection, prepareStatment, resultSet);
		}
	}

	//  獲取查詢結果,若查詢結果並沒有類與其對應,就需要獲取虛表的表結構進行查詢操作的打印輸出
	@Test
	public void test4() {
		Connection connection = null;
		PreparedStatement prepareStatment = null;
		ResultSet resultSet = null;
		try {
			connection = JdbcUtil.getConnection();
			// name | age  | gender | phone      | weight | birthdate  |
			String sql = "select name custName, gender, age, weight, phone, birthdate birth from customer";
			prepareStatment = connection.prepareStatement(sql);
			resultSet = prepareStatment.executeQuery();
			/*
			+----------+--------+------+--------+------------+------------+
			| custName | gender | age  | weight | phone      | birth      |
			+----------+--------+------+--------+------------+------------+
			| 張七     		| 女           |   20 |   58.8 | 1343434343 | 1980-03-08 |
			| 張九     		| 男          |   40 |   78.2 | 1349932423 | 1977-08-08 |
			| 張三   	    | 女          |   30  |     70 | 1332324234 | 1979-08-08 |
			+----------+--------+------+--------+------------+------------+
			*/
			ResultSetMetaData metaData = resultSet.getMetaData(); // 獲取虛表的表結構對象
			int cols = metaData.getColumnCount(); // 獲取虛表共有多少列
			//System.out.println(cols);
			
			String colName = metaData.getColumnName(3);// 獲取基表的列名, 參數中的是第幾列
			//System.out.println(colName);
			
			colName = metaData.getColumnName(6);// 獲取基表列名, 參數中的是第幾列
			//System.out.println(colName);
			
			String colLabel = metaData.getColumnLabel(6); // 獲取虛表列名, 參數是第6列
			//System.out.println(colLabel);
			
			System.out.println("---------------------------------");
			
			for (int i = 0; i < cols; i++) {
				String columnLabel = metaData.getColumnLabel(i + 1); // 列的索引總是從1開始 
				System.out.print(columnLabel + "\t");
			}
			System.out.println();
			
			while (resultSet.next()) {
				for (int i = 0; i < cols; i++) {
					String columnLabel = metaData.getColumnLabel(i + 1); // 先獲取列的別名
					Object value = resultSet.getObject(columnLabel); // 根據列的別名再獲取對應的值
					System.out.print(value + "\t");
				}
				System.out.println();
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JdbcUtil.close(connection, prepareStatment, resultSet);
		}
	}	
}



















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