1. 程式人生 > >PreparedStatement與SQL批處理

PreparedStatement與SQL批處理

1、伺服器如何執行SQL語句?

  當伺服器接收到一條SQL語句後,伺服器先要校驗這條SQL語句的語法格式是否正確,然後把SQL語句編譯成可執行的函式,最後執行這個函式就是執行了SQL語句。其中校驗語法,和編譯所花的時間比執行SQL語句花的時間還要多很多。比如我們要執行多次insert語句,如果按照平常方法,會每插入一條記錄就寫一條SQL語句,所以MySQL伺服器需要每次都去校驗每一條SQL語句的語法格式,然後編譯為函式,然後再執行,這就浪費了太多的時間。

2、PreparedStatement是什麼?

  PreparedStatement,預處理語句,通過使用PreparedStatement,伺服器只對SQL語句進行一次語法校驗和編譯,大大提高了執行效率。那麼它如何使用呢?

  首先我們要準備一個SQL模板,SQL模板是一條用“?”代替引數的待補全SQL語句,例如:

<span style="font-size:18px;">select * from t_book where bid=?</span>

  這就是一個查詢的SQL模板,我們通過PreparedStatement pstmt = con.prepareStatement(sql)使SQL模板和PreparedStatement物件繫結,然後呼叫PreparedStatement的setXxx()方法,為引數賦值(有幾個不明確的引數,就要賦幾個值),這樣就將模板補全成了一條完整的SQL語句, 呼叫PreparedStatement的executeUpdate()或executeQuery()方法完成執行sql語句,得到ResultSet物件。

  需要注意的是,Statement的executeQuery()和executeUpdate是需要引數(SQL語句)的;而PreparedStatement物件的executeUpdate和executeQuery方法沒有引數。這是因為建立PreparedStatement物件時已經與一條SQL模板繫結在一起了,所以在呼叫它的executeQuery()和executeUpdate()方法時就不再需要指定引數了。

  PreparedStatement最大的好處就是在於重複使用同一模板,這樣伺服器只對SQL模板進行一次語法校驗和函式編譯,其中引數編譯後都會成為函式的引數,執行SQL語句時只是把引數代入到函式中完成呼叫。這樣就能賦予其不同的引數來重複的使用它,這就是提高效率的原因。

3、預編譯的優點

  ●防止SQL攻擊:SQL攻擊的本質是使用者輸入了SQL語句,其輸入內容沒有被作為引數。而預編譯後,SQL語句的引數都會變成函式的引數,輸入內容就不能再行使SQL語句的職能了。

  ●提高程式碼的可讀性:相對於以前加上單引號、雙引號的直接輸入,確實提高了可讀性;

  ●提高效率。

4、批處理

  批處理就是將SQL語句一批一批的處理,而不是一條一條的處理,如果有10條SQL語句需要執行時,一次向伺服器傳送一條SQL語句,這麼做效率很差,這時候就可以使用批處理,即一次向伺服器傳送多條SQL語句,然後由伺服器一次性處理。批處理只針對更新(增、刪、改)語句,不能用於查詢語句。Statement和PreparedStatement都有批處理的方法。

  批處理常用方法:

    ●void addBatch():新增一條語句到“批”中;

    ●int[] executeBatch():執行“批”中所有語句。返回值表示每條語句所影響的行資料;

    ●void clearBatch():清空“批”中的所有語句。

  注意:Statement中的批處理方法中有引數,PreparedStatement批處理方法沒有引數。

5、簡單的批處理例項

  (1)Statement

    可以多次呼叫Statement類的addBatch(String sql)方法,把需要執行的所有SQL語句新增到一個“批”中,然後呼叫Statement類的executeBatch()方法來執行當前“批”中的語句。

<span style="font-size:18px;">			for(int i = 0; i < 10; i++) {
				String number = "S_10" + i;
				String name = "stu" + i;
				int age = 20 + i;
				String gender = i % 2 == 0 ? "male" : "female";
				String sql = "insert into stu values('" + number + "', '" + name + "', " + age + ", '" + gender + "')";
				stmt.addBatch(sql);
			}
			stmt.executeBatch();</span>

  (2)PreparedStatement

    PreparedStatement的批處理有所不同,因為每個PreparedStatement物件都繫結一條SQL模板。所以向PreparedStatement中新增的不是SQL語句,而是給“?”賦值。

<span style="font-size:18px;">package jdbc;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.junit.Test;

/**
 * 演示批處理
 */
public class Demo1 {
	@Test
	public void fun1() {
		Connection con = null;
		PreparedStatement pstmt = null;
		try {
			con = JdbcUtils.getConnection(); //得到連線物件
			String sql = "insert into tbl_student values(?,?,?,?)";//設定SQL模板
			pstmt = con.prepareStatement(sql);//繫結模板
			
			//新增批(100次)
			for(int i = 1; i <= 100; i++) {
				//每次都要設定引數,呼叫新增批方法
				pstmt.setInt(1, i);
				pstmt.setString(2, "stu_name_" + i);
				pstmt.setInt(3, i % 20 + 20);
				pstmt.setString(4, i%2==0 ? "male" : "female");
				
				pstmt.addBatch();//把上面設定的引數,新增到批中!
			}
			//執行批
			pstmt.executeBatch();//執行批
			
		} catch(Exception e) {
			throw new RuntimeException(e);
		} finally {
			try {   //關閉物件
				if(pstmt != null) pstmt.close();
				if(con != null) con.close();
			} catch(SQLException e) {}
		}
	}
}</span>

  小結:因為效率原因,建議使用PreparedStatement,而不是使用Statement。而且PreparedStatement是Statement的子介面,可以使用PreparedStatement來替換Statement。mysql伺服器是針對每個PreparedStatement物件只編譯一次,所以提高效率是必須使用同一個PreparedStatement物件。