JDBC 筆記(二)Statement、PreparedStatement預編譯與原生增刪查改、通用增刪查改

個人博客網:https://wushaopei.github.io/    (你想要這裏多有)

1、預編譯:statement 與 PreparedStatement

1.1 sql提供訪問的接口:

  • 數據庫連接被用於向數據庫服務器發送命令和 SQL 語句,在連接建立後,需要對數據庫進行訪問,執行 sql 語句
  • 在 java.sql 包中有 3 個接口分別定義了對數據庫的調用的不同方式:
     |-----   Statement

            |------PreparedStatement

                   |------CallableStatement

使用statement 進行預編譯可能會導致SQL注入的發生

1.2 SQL 注入攻擊

  • SQL 注入是利用某些系統沒有對用戶輸入的數據進行充分的檢查,而在用戶輸入數據中注入非法的 SQL 語句段或命令(如:SELECT user, password FROM user_table WHERE user='a' OR 1 = ' AND password = ' OR '1' = '1') ,從而利用系統的 SQL 引擎完成惡意行爲的做法
  • 對於 Java 而言,要防範 SQL 注入,只要用 PreparedStatement(從Statement擴展而來) 取代 Statement 就可以了

1.3 PreparedStatement

  • 可以通過調用 Connection 對象的 preparedStatement() 方法獲取 PreparedStatement 對象
  • PreparedStatement 接口是 Statement 的子接口,它表示一條預編譯過的 SQL 語句
  • PreparedStatement 對象所代表的 SQL 語句中的參數用問號(?)來表示,調用 PreparedStatement 對象的 setXxx() 方法來設置這些參數. setXxx() 方法有兩個參數,第一個參數是要設置的 SQL 語句中的參數的索引(從 1 開始),第二個是設置的 SQL 語句中的參數的值

1.4 PreparedStatement vs Statement

  • 代碼的可讀性和可維護性.
  • PreparedStatement 能最大可能提高性能:
  1. DBServer會對預編譯語句提供性能優化。因爲預編譯語句有可能被重複調用,所以語句在被DBServer的編譯器編譯後的執行代碼被緩存下來,那麼下次調用時只要是相同的預編譯語句就不需要編譯,只要將參數直接傳入編譯過的語句執行代碼中就會得到執行。

  2. 在statement語句中,即使是相同操作但因爲數據內容不一樣,所以整個語句本身不能匹配,沒有緩存語句的意義.事實是沒有數據庫會對普通語句編譯後的執行代碼緩存.這樣每執行一次都要對傳入的語句編譯一次.  

  3. (語法檢查,語義檢查,翻譯成二進制命令,緩存)

  • PreparedStatement 可以防止 SQL 注入防止 SQL 注入

1.5 數據類型轉換表

             

1.6 連接數據庫、操作表的步驟:

           

注意:

  • 釋放ResultSet, Statement,Connection。
  • 數據庫連接(Connection)是非常稀有的資源,用完後必須馬上釋放,如果Connection不能及時正確的關閉將導致系統宕機。Connection的使用原則是儘量晚創建,儘量早的釋放。

2 、ResultSet

2.1 ResultSet 的作用:

  • 通過調用 PreparedStatement 對象的 excuteQuery() 方法創建該對象
  • ResultSet 對象以邏輯表格的形式封裝了執行數據庫操作的結果集,ResultSet 接口由數據庫廠商實現
  • ResultSet 對象維護了一個指向當前數據行的遊標,初始的時候,遊標在第一行之前,可以通過 ResultSet 對象的 next() 方法移動到下一行

ResultSet 接口的常用方法:

  1. boolean next()
    getString( )

2.2 案例

(1)

(2)

    

2.3 ResultSet的說明

   1. 查詢需要調用Prepared Statement 的 executeQuery() 方法,查詢結果是一個 ResultSet 對象
   2. 關於 ResultSet:代表結果集

  • ResultSet: 結果集. 封裝了使用 JDBC 進行查詢的結果.
  • 調用 PreparedStatement 對象的 executeQuery() 可以得到結果集.
  • ResultSet 返回的實際上就是一張數據表. 有一個指針指向數據表的第一條記錄的前面.

   3.可以調用 next() 方法檢測下一行是否有效. 若有效該方法返回 true, 且指針下移. 相當於Iterator 對象的 hasNext() 和 next() 方法的結合體
   4.當指針指向一行時, 可以通過調用 getXxx(int index) 或 getXxx(int columnName) 獲取每一列的值.
         例如: getInt(1), getString("name")
   5.ResultSet 當然也需要進行關閉.

3、 普通的增刪查改

3.1 創建一個類customer

public class Customer {

	private int id;
	private String name;
	private String email;
	private Date birth;

	public Customer() {
		super();
	}
		
	public Customer(int id, String name, String email, Date birth) {
		super();
		this.id = id;
		this.name = name;
		this.email = email;
		this.birth = birth;
	}

3.2 創建jdbc連接,並封裝爲方法

/*	 * 獲取數據庫的連接
	 */
	public static Connection getConnection() {
		// 數據庫的連接
		Connection connection = null;
		InputStream is = null;
		try {
			is = JDBCUtils.class.getClassLoader().getResourceAsStream("sqlDriver.properties");
			Properties ps = new Properties();
			ps.load(is);
			String user = ps.getProperty("user");
			String password = ps.getProperty("password");
			String driverClass = ps.getProperty("driverClass");
			String url = ps.getProperty("url");

			// 創建Driver對象
			Class clazz = Class.forName(driverClass);
			Object obj = clazz.newInstance();
			Driver driver = (Driver) obj;
			// 註冊驅動
			DriverManager.registerDriver(driver);
			connection = DriverManager.getConnection(url, user, password);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (is != null) {
				try {
					is.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}

		return connection;
	}

3.3 在操作完成後要進行連接的關閉

	public static void close(Connection connection, PreparedStatement ps) {
		if (connection != null) {
			try {
				connection.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}

		if (ps != null) {
			try {
				ps.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}

	public static void close(Connection connection, PreparedStatement ps, ResultSet rs) {
		close(connection,ps);
		
		if(rs != null){
			try {
				rs.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}		
	}

3.4 增刪查改操作

/*
 * 對數據庫的  增 ,刪,改,查
 */
public class CRUDTest {

	/*
	 * 從數據庫中查找一條數據
	 */
	@Test
	public void select() throws Exception {

		Customer customer = getCustomerById();
		System.out.println(customer);
		
		List<Customer> customers = getCustomers();
		for (Customer customer2 : customers) {
			System.out.println(customer2);
		}

	}

	/*
	 * 查詢多條數據
	 */
	public List<Customer> getCustomers() throws Exception {
		// 1.獲取數據庫的連接
		Connection connection = JDBCUtils.getConnection();

		String sql = "select * from customers";

		// 2.預編譯  -- 如果沒有佔位符可以不填充數據
		PreparedStatement ps = connection.prepareStatement(sql);

		// 4.執行sql語句
		ResultSet rs = ps.executeQuery(); // 查找的結果都已經放到ResultSet中了

		List<Customer> list = new ArrayList<>();
	
		while(rs.next()){
			//獲取數據
			int id = rs.getInt("id");
			String name = rs.getString("name");
			String email = rs.getString("email");
			Date birth = rs.getDate("birth");
			
			//將每條數據放入集合中
			list.add(new Customer(id, name, email, birth));
		}

		// 6.關閉資源
		JDBCUtils.close(connection, ps, rs);
		return list;
	}

	/*
	 * 查詢一條數據
	 */
	public Customer getCustomerById() throws Exception {
		// 1.獲取數據庫的連接
		Connection connection = JDBCUtils.getConnection();

		String sql = "select * from customers where id = ?";

		// 2.預編譯
		PreparedStatement ps = connection.prepareStatement(sql);

		// 3.填充數據
		ps.setInt(1, 20);

		// 4.執行sql語句
		ResultSet rs = ps.executeQuery(); // 查找的結果都已經放到ResultSet中了

		Customer customer = null;

		// 5.取數據
		if (rs.next()) { // 如果有數據就返回true,否則返回false
			// 獲取列上值的第一種方式
			// int id = rs.getInt(1); //根據列的索引獲取對應的列上的值
			// String name = rs.getString(2);
			// String email = rs.getString(3);
			// Date date = rs.getDate(4);

			// 獲取列上值的第二種方式
			int id = rs.getInt("id");
			String name = rs.getString("name");
			String email = rs.getString("email");
			Date birth = rs.getDate("birth");

			// 考慮將數據進行封裝
			customer = new Customer(id, name, email, birth);

			// 輸出獲取到的內容
			System.out.println(id + " " + name + " " + email + " " + birth);
		}

		// 6.關閉資源
		JDBCUtils.close(connection, ps, rs);
		return customer;
	}

	/*
	 * 向數據庫中插入一條數據
	 */
	@Test
	public void insert() {
		Connection connection = null;
		PreparedStatement ps = null;
		try {
			// 1 : 連接數據庫
			connection = JDBCUtils.getConnection();
			// sql語句
			String sql = "INSERT INTO customers(NAME,email,birth) " + "VALUES(?,?,?);";
			// 2.預編譯
			ps = connection.prepareStatement(sql);
			// 3.設置內容
			ps.setString(1, "強哥"); // 第一個參數:第幾個問號 第二個參數:內容
			ps.setString(2, "[email protected]");
			// 獲取一個sql下的Date
			Date date = new Date(new java.util.Date().getTime());
			ps.setDate(3, date);
			// 4.執行sql語句
			ps.execute(); // 如果是查詢語句返回true
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			// 5.關閉資源
			JDBCUtils.close(connection, ps);
		}
	}

	/*
	 * 刪除一條件數
	 */
	@Test
	public void delete() throws Exception {
		// 1. 獲取數據庫的連接
		Connection connection = JDBCUtils.getConnection();

		String sql = "DELETE FROM customers WHERE id = ?";
		// 2. 預編譯
		PreparedStatement ps = connection.prepareStatement(sql);

		// 3.填充數據
		ps.setInt(1, 19);

		// 4.執行sql語句
		int executeUpdate = ps.executeUpdate();
		System.out.println(executeUpdate + "條受到影響");

		// 5.關閉資源
		JDBCUtils.close(connection, ps);
	}

	/*
	 * 修改一條件數
	 */
	@Test
	public void update() throws Exception {

		Connection connection = null;
		PreparedStatement ps = null;
		try {
			connection = JDBCUtils.getConnection();

			String sql = "UPDATE customers SET NAME = ? WHERE id = ?";
			ps = connection.prepareStatement(sql);

			ps.setString(1, "成哥");
			ps.setInt(2, 18);

			int executeUpdate = ps.executeUpdate();
			System.out.println(executeUpdate + "條數據受到影響");
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.close(connection, ps);
		}
	}
}

4、通用增刪查改

創建類 User.java

public class User {

	private int id;
	private String name;
	private String address;
	private String phone;
	

4.1 創建通用連接方案 - 通用增刪改

	/*
	 * 通用的增刪改
	 */
	public static int UID(String sql,Object ...objects) {
		// 1. 獲取數據庫的連接
		Connection connection = null;
		// 2. 預編譯
		PreparedStatement ps = null;
		// 4.執行sql語句
		int executeUpdate = -1;
		try {
			connection = JDBCUtils.getConnection();
			ps = connection.prepareStatement(sql);
			// 3.填充數據
			for (int i = 0; i < objects.length; i++) { // i 表示索引
				ps.setObject(i + 1, objects[i]); // i + 1表示的是第幾個佔位符
			}
			executeUpdate = ps.executeUpdate();
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			// 5.關閉資源
			JDBCUtils.close(connection, ps);
		}
		return executeUpdate;
	}

測試代碼:

        /*
	 * 測試通用的增刪改
	 */
	@Test
	public void insert(){
		String sql = "INSERT INTO customers(NAME,email,birth) VALUES(?,?,?)";
		Date date = new java.sql.Date(new java.util.Date().getTime());
		int uid = JDBCUtils.UID(sql, "強哥","[email protected]",date);
		System.out.println(uid + "條數據受到影響");
	}

4.2 通用查找(一條或多條)

只查一條數據:

/*
	 * 通用的查找 (只有一條數據)
	 * 
	 * Class<T> clazz : 運行時類的對象
	 */
	public static <T> T getObject(Class<T> clazz,String sql,Object ...objects) throws Exception{
		
		//1.獲取數據庫的連接
		Connection connection = JDBCUtils.getConnection();
		
		//2.預編譯
		PreparedStatement ps = connection.prepareStatement(sql);
		
		//3.填充數據
		for (int i = 0; i < objects.length; i++) {
			ps.setObject(i + 1, objects[i]);
		}
		
		//4.執行sql語句
		ResultSet rs = ps.executeQuery();
		
		ResultSetMetaData md = rs.getMetaData(); //獲取元數據。
		//獲取列的數量
		int columnCount = md.getColumnCount();
		//創建對象
		T t = clazz.newInstance();
		while(rs.next()){ //遍歷每一條數據
			/*
			 * 第一個問題 : 如何獲取到表的列數
			 * 第二個問題 : 需要知道類中的屬性
			 * 第三個問題 : 對象中屬性的名字怎麼來
			 */
			for (int i = 0; i < columnCount; i++) {
				//獲取某列的列名(如果有別名獲取的是別名)
				String columnLabel = md.getColumnLabel(i + 1); 
				 //通過列名獲取列中的數據。
				Object value = rs.getObject(columnLabel);

				//封裝
				//通過反射 - 給對象中的屬性進行賦值
				//將表中的列名當作類中的屬性名。如果列名和屬性名不一樣,可以通過別名的方式(別名 = 屬性名)
				//通過列名就獲取到了類中的對應的屬性
				Field declaredField = clazz.getDeclaredField(columnLabel);
				declaredField.setAccessible(true);
				/*
				 * 第一個參數 : 是給哪個對象進行賦值
				 * 第二個參數 : 屬性值
				 */
				declaredField.set(t, value);
			}
		}
		//關閉資源
		JDBCUtils.close(connection, ps, rs);
		
		return t;
	}

查多條數據:

/*
	 * 通用的查找 (返回多條數據)
	 * 
	 */
	public static <T> List<T> getObjects(Class<T> clazz,String sql,Object ...objects) throws Exception{
		
		//1.獲取數據庫的連接
		Connection connection = JDBCUtils.getConnection();
		
		//2.預編譯
		PreparedStatement ps = connection.prepareStatement(sql);
		
		//3.填充數據
		for (int i = 0; i < objects.length; i++) {
			ps.setObject(i + 1, objects[i]);
		}
		
		//4.執行sql語句
		ResultSet rs = ps.executeQuery();
		
		ResultSetMetaData md = rs.getMetaData(); //獲取元數據。
		//獲取列的數量
		int columnCount = md.getColumnCount();
		//創建一個集合
		List<T> list = new ArrayList<>();
		
		while(rs.next()){ //遍歷每一條數據
			//創建對象
			T t = clazz.newInstance();
			/*
			 * 第一個問題 : 如何獲取到表的列數
			 * 第二個問題 : 需要知道類中的屬性
			 * 第三個問題 : 對象中屬性的名字怎麼來
			 */
			for (int i = 0; i < columnCount; i++) {
				//獲取某列的列名(如果有別名獲取的是別名)
				String columnLabel = md.getColumnLabel(i + 1); 
				 //通過列名獲取列中的數據。
				Object value = rs.getObject(columnLabel);

				//封裝
				//通過反射 - 給對象中的屬性進行賦值
				//將表中的列名當作類中的屬性名。如果列名和屬性名不一樣,可以通過別名的方式(別名 = 屬性名)
				//通過列名就獲取到了類中的對應的屬性
				Field declaredField = clazz.getDeclaredField(columnLabel);
				declaredField.setAccessible(true);
				/*
				 * 第一個參數 : 是給哪個對象進行賦值
				 * 第二個參數 : 屬性值
				 */
				declaredField.set(t, value);
			}
			
			//將對象放入到集合中
			list.add(t);
		}
		//關閉資源
		JDBCUtils.close(connection, ps, rs);
		
		return list;
	}

測試代碼:

                String sql = "select id,name,email,birth from customers where id = ?";
		Customer customer = getObject(Customer.class, sql, 5);
		System.out.println(customer);
		
		
		sql = "select * from users where id = ?";
		User object = getObject(User.class, sql, 1);
		System.out.println(object);
		
		sql = "select * from users";
		List<User> users = getObjects(User.class, sql);
		for (User user : users) {
			System.out.println(user);
		}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章