1. 程式人生 > 實用技巧 >JDBC核心技術(一)

JDBC核心技術(一)

技術體系

一、JDBC概述

1.資料的持久化

持久化(persistence):把資料儲存到可掉電式儲存裝置中以供之後使用。大多數情況下,特別是企業級應用,資料
●持久化意味著將記憶體中的資料儲存到硬碟上加以”固化”,而持久化的實現過程大多通過各種關係資料庫來完成。
●持久化的主要應用是將記憶體中的資料儲存在關係型資料庫中,當然也可以儲存在磁碟檔案、XML資料檔案中。

2.Java中的資料儲存技術

●在Java中,資料庫存取技術可分為如下幾類:

➢JDBC直接訪問資料庫
➢JDO (Java Data Object )技術
➢第三方O/R工具,如Hibernate, Mybatis等

●JDBC是java訪問資料庫的基石, JDO、Hibernate、 MyBatis等只是 更好的封裝了JDBC。

3.JDBC介紹

●JDBC(Java Database Connectivity)是一個獨立於特定資料庫 管理系統、通用的SQL資料庫存取和操作的公共介面(一組API) , 定義了用來訪問資料庫的標準Java類庫,( java.sql.javax.sql )使用這些類庫可以以一種標準的方法、方便地訪問資料庫資源。
●JDBC為訪問不同的資料庫提供了一種統一的途徑 ,為開發者遮蔽了一些細節問題。
●JDBC的目標是使Java程式設計師使用JDBC可以連線任何提供了JDBC驅動程式的資料庫系統,這樣就使得程式設計師無需對特定的資料庫系統的特點有過多的瞭解,從而大大簡化和加快了開發過程。

4.JDBC體系結構

JDBC介面( API)包括兩個層次:

面向應用的API : Java API ,抽象介面,供應用程式開發人員使用(連線資料庫,執行SQL語句,獲得結果)。
面向資料庫的API : Java Driver API ,供開發商開發資料庫驅動程式用。

JDBC是sun公司提供一套用於資料庫操作的介面, java程式設計師只需要面向這套介面程式設計即可。不同的資料庫廠商,需要針對這套介面,提供不同實現。不同的實現的集合,即為不同資料庫的驅動。---面向介面程式設計

5.JDBC程式編寫步驟

二、獲取資料庫連線

package com.xudong.connection;

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

import org.junit.Test;

public class ConnectionTest2 {
	// 方式一:
	@Test
	public void testConnectionTest_1() throws SQLException {
		// 獲取Driver的實現類物件
		Driver driver = new com.mysql.jdbc.Driver();

		String url = "jdbc:mysql://127.0.0.1:3306/test?characterEncoding=UTF-8";
		Properties info = new Properties();
		info.setProperty("user", "root");
		info.setProperty("password", "123456");

		Connection conn = driver.connect(url, info);

		System.out.println(conn);
	}

	// 方式二:對方式一的迭代,在如下程式中不出現第三方安裝API,使程式具有更好的移植性
	@Test
	public void testConnectionTest_2() throws Exception {
		// 通過 反射 獲取Driver實現類物件
		Class<?> clazz = Class.forName("com.mysql.jdbc.Driver");
		Driver driver = (Driver) clazz.newInstance();

		// 提供要連線的資料庫
		String url = "jdbc:mysql://127.0.0.1:3306/test?characterEncoding=UTF-8";

		// 提供連線需要的使用者名稱和密碼
		Properties info = new Properties();
		info.setProperty("user", "root");
		info.setProperty("password", "123456");

		// 獲取連線
		Connection conn = driver.connect(url, info);
		System.out.println(conn);
	}

	// 方式三:使用DriverManager替換Driver
	@Test
	public void testConnectionTest_3() throws Exception {
		// 1.通過 反射 獲取Driver實現類物件
		Class<?> clazz = Class.forName("com.mysql.jdbc.Driver");
		Driver driver = (Driver) clazz.newInstance();

		// 2.提供另外三個連線的基本資訊
		String url = "jdbc:mysql://127.0.0.1:3306/test?characterEncoding=UTF-8";
		String user = "root";
		String password = "123456";

		// 註冊驅動
		DriverManager.registerDriver(driver);
		// 獲取連線
		Connection conn = DriverManager.getConnection(url, user, password);
		System.out.println(conn);
	}

	// 方式四:
	@Test
	public void testConnectionTest_4() throws Exception {
		// 1.提供另外三個連線的基本資訊
		String url = "jdbc:mysql://127.0.0.1:3306/test?characterEncoding=UTF-8";
		String user = "root";
		String password = "123456";

		// 2.載入Driver
		Class.forName("com.mysql.jdbc.Driver");

		// 3.獲取連線
		Connection conn = DriverManager.getConnection(url, user, password);
		System.out.println(conn);
	}
	
	//方式五:通過讀取配置檔案的方式獲取連線。部署伺服器時,若要修改可避免程式重新打包。
	@Test
	public void testConnectionTest() throws Exception{
		//1.讀取配置檔案中4個基本資訊
		InputStream is = ConnectionTest2.class.getClassLoader().getResourceAsStream("jdbc.properties");
		Properties pros = new Properties();
		pros.load(is);
		
		String url = pros.getProperty("url");
		String user = pros.getProperty("user");
		String password = pros.getProperty("password");
		String driverClass = pros.getProperty("driverClass");
		
		//2.載入驅動
		Class.forName(driverClass);
		
		//3.獲取連線
		Connection conn = DriverManager.getConnection(url, user, password);
		System.out.println(conn);
	}
}

三、使用PreparedStatement實現CRUD操作

1.操作和訪問資料庫

●資料庫連線被用於向資料庫伺服器傳送命令和SQL語句,並接受資料庫伺服器返回的結果。其實一個數據庫連線就是一個Socket連線。
●在java.sql包中有3個介面分別定義了對資料庫的呼叫的不同方式:

Statement:用於執行靜態SQL語句並返回它所生成結果的物件。
PreparedStatement:SQL語句被預編譯並存儲在此物件中,可以使用此物件多次高效地執行該語句。➢CallableStatement:用於執行SQL儲存過程

使用Statement操作資料表存在弊端:

➢問題一:存在拼串操作,繁瑣
➢問題二:存在SQL注入問題
SQL注入是利用某些系統沒有對使用者輸入的資料進行充分的檢查,而在使用者輸入資料中注入非法的SQL語句段或命令(如: SELECT user, password FROM user_table WHERE user='a' OR 1 = 'AND password= 'OR '1'='1') , 從而利用系統的SQL引擎完成惡意行為的做法。

PreparedStatement的使用

增刪改

package com.xudong.perparedstatement;

import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Properties;

import org.junit.Test;

import com.xudong.connection.ConnectionTest2;

public class PreparedStatementCRUDTest {
	@Test
	public void testInsert() {
		// 3.獲取連線
		Connection conn = null;
		PreparedStatement ps = null;
		try {
			// 1.讀取配置檔案中4個基本資訊
			InputStream is = ConnectionTest2.class.getClassLoader().getResourceAsStream("jdbc.properties");
			Properties pros = new Properties();
			pros.load(is);

			String url = pros.getProperty("url");
			String user = pros.getProperty("user");
			String password = pros.getProperty("password");
			String driverClass = pros.getProperty("driverClass");

			// 2.載入驅動
			Class.forName(driverClass);

			conn = DriverManager.getConnection(url, user, password);

			// 4.預編譯SQL語句,返回prepareStatement例項
			String sql = "INSERT INTO customers(id,nam,email,birth) VALUES(?,?,?,?)";// 佔位符
			ps = conn.prepareStatement(sql);

			// 5.填充佔位符
			ps.setString(1, "11");
			ps.setString(2, "曹洪");
			ps.setString(3, "[email protected]");
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
			java.util.Date date = sdf.parse("2000-01-11");
			ps.setDate(4, new java.sql.Date(date.getTime()));

			// 6.執行操作
			ps.execute();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			// 7.資源關閉
			try {
				if(ps != null)
					ps.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			try {
				if(conn != null)
					conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}

}


查詢

package com.xudong.perparedstatement;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import org.junit.Test;

import com.xudong.bean.Customer;
import com.xudong.util.JDBCUtils;

public class CustomerForQuery {
	/*@Test
	public void testQuery(){
		Connection conn = null;
		PreparedStatement ps = null;
		ResultSet resultSet = null;
		try {
			conn = JDBCUtils.getConnection();
			String sql = "select nam,email,birth from customers where id = ?";
			ps = conn.prepareStatement(sql);
			ps.setObject(1, 5);
			
			//執行並返回結果集
			resultSet = ps.executeQuery();
			//處理結果集
			if(resultSet.next()){
				
				String name = resultSet.getString(1);
				String email = resultSet.getString(2);
				Date birth = resultSet.getDate(3);
				
				Customer customer = new Customer(name, email, birth);
				System.out.println(customer);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally{
			//關閉資源
			JDBCUtils.closeResource(conn, ps, resultSet);
		}
		
	}
	*/
	public <T> List<T> queryForCustomers(Class<T> clazz,String sql,Object ...args){
		Connection conn = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		try {
			conn = JDBCUtils.getConnection();
			ps = conn.prepareStatement(sql);
			
			for(int i = 0;i < args.length; i++){
				ps.setObject(i + 1, args[i]);
			}
			
			rs = ps.executeQuery();
			//獲取結果集的原資料
			ResultSetMetaData rsmd = rs.getMetaData();
			//獲取結果集列數
			int columnCount = rsmd.getColumnCount();
			//建立集合物件
			ArrayList<T> list = new ArrayList<>();
			while(rs.next()){
				T t = clazz.newInstance();
				for(int i = 0;i < columnCount;i++){
					Object columvalue = rs.getObject(i + 1);
					//獲取每個列的列名
					//String columnName = rsmd.getColumnName(i + 1);
					//獲取每個列的別名
					String columnLabel = rsmd.getColumnLabel(i + 1);
					//給cust物件指定columnName屬性,賦值columnLabel
					Field field = clazz.getDeclaredField(columnLabel);
					field.setAccessible(true);
					field.set(t, columvalue);
				}
				list.add(t);
			}
			return list;
		} catch (Exception e) {
			e.printStackTrace();
		} finally{
			JDBCUtils.closeResource(conn, ps, rs);
		}
		
		return null;
	}
	
	@Test
	public void testQueryForCustomers(){
		String sql = "select name,email,birth from customers where id > ?";
		List<Customer> list = queryForCustomers(Customer.class,sql, 6);
		list.forEach(System.out::println);
	}
}

javabean

package com.xudong.bean;

import java.sql.Date;

/**
 * ORM程式設計思想(object relational mapping) 一個數據表對應一個java類 表中的一條記錄對應java類的一個物件
 * 表中的一個欄位對應java類的一個屬性
 */
public class Customer {

	private String name;
	private String email;
	private Date birth;

	public Customer() {
		super();
	}

	public Customer( String name, String email, Date birth) {
		super();

		this.name = name;
		this.email = email;
		this.birth = birth;
	}


	public String getName() {
		return name;
	}

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

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public Date getBirth() {
		return birth;
	}

	public void setBirth(Date birth) {
		this.birth = birth;
	}

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

}

工具

package com.xudong.util;

import java.io.InputStream;
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 JDBCUtils {
	// 獲取資料庫的連線
	public static Connection getConnection() throws Exception {
		// 1.讀取配置檔案中4個基本資訊
		InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");
		Properties pros = new Properties();
		pros.load(is);

		String url = pros.getProperty("url");
		String user = pros.getProperty("user");
		String password = pros.getProperty("password");
		String driverClass = pros.getProperty("driverClass");

		// 2.載入驅動
		Class.forName(driverClass);

		Connection conn = DriverManager.getConnection(url, user, password);

		return conn;
	}

	// 關閉資料庫連線
	public static void closeResource(Connection conn, Statement ps) {
		// 資源關閉
		try {
			if (ps != null)
				ps.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		try {
			if (conn != null)
				conn.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}

	public static void closeResource(Connection conn, Statement ps, ResultSet rs) {
		// 資源關閉
		try {
			if (ps != null)
				ps.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		try {
			if (conn != null)
				conn.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		try {
			if (rs != null)
				rs.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
}

PreparedStatement的好處:

書寫sql更方便、解決了sql注入問題、可以操作Blob資料,可實現更高效的批量操作

四、操作BLOB型別欄位

1.MySQL BLOB型別

●MySQL中,BLOB是一個二進位制大型物件,是一個可以儲存大量資料的容器,它能容納不同大小的資料。
●插入BLOB型別的資料必須使用PreparedStatement ,因為BLOB型別的資料無法使用字串拼接寫的。
●MySQL的四種BLOB型別(除了在儲存的最大資訊量上不同外,他們是等同的)

●實際使用中根據需要存入的資料大小定義不同的BLOB型別。
●需要注意的是:如果儲存的檔案過大,資料庫的效能會下降。
●如果在指定了相關的Blob型別以後,還報錯: xxx too large ,那麼在mysq|的安裝目錄下,找my.ini檔案加上如下的配置引數: max_allowed_packet=16M。同時注意:修改了my.ini檔案之後,需要重新啟動mysql服務

批量插入

修改url:
url=jdbc:mysql://127.0.0.1:3306/test?characterEncoding=UTF-8&rewriteBatchedStatements=true

package com.xudong.perparedstatement;

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

import org.junit.Test;

import com.xudong.util.JDBCUtils;

public class InsertTest {
	@Test
	public void testInsert(){
		Connection conn = null;
		PreparedStatement ps = null;
		try {
			
			long start = System.currentTimeMillis();
			
			conn = JDBCUtils.getConnection();
			
			conn.setAutoCommit(false);
			
			String sql = "insert into goods(name) values(?)";
			ps = conn.prepareStatement(sql);
		
			for(int i = 1;i < 1000000;i++){
				ps.setObject(1, "name_" + i);
				ps.addBatch();//攢sql
				if(i % 500 == 0){
					ps.executeBatch();//執行
					ps.clearBatch();//清空
				}
			}
			conn.commit();
			
			long end = System.currentTimeMillis();
			
			System.out.println("花費時間:" + (end - start));
			
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			JDBCUtils.closeResource(conn, ps);
		}
	
	}
}