JDBC

1. JDBC概述

    JDBC全稱爲:JavaDataBase Connectivity(java數據庫連接)。SUN公司爲了簡化、統一對數據庫的操作,定義了一套Java操作數據庫的規範,稱之爲JDBC。

2. JDBC開發步驟

    (1)註冊驅動

    (2)獲得連接

    (3)獲取執行SQL語句的對象

    (4)執行SQL語句

    (5)獲得結果集

    (6)釋放資源

JDBC入門示例1:

        @Test
	public void test01() throws Exception{
		//註冊驅動
		DriverManager.registerDriver(new com.mysql.jdbc.Driver());
		//獲取數據庫連接
		Connection conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "root");
		//創建執行SQL語句的對象
		Statement st=conn.createStatement();
		//執行SQL語句
		ResultSet rs = st.executeQuery("select * from stu");
		//遍歷結果集
		while(rs.next()){
			System.out.println(rs.getString("sid"));
			System.out.println(rs.getString("sname"));
			System.out.println(rs.getInt("age"));
			System.out.println(rs.getString("gender"));
		}
		//釋放資源
		rs.close();
		st.close();
		conn.close();
		
	}
2.1 導入驅動jar包

    創建lib目錄,用於存放當前項目需要的所有jar包

    選擇jar包,右鍵執行buildpath / Add to Build Path

2.2 DriverManager類

    static void registerDriver(Driver driver)  用於註冊JDBC驅動程序

    注意:DriverManager中可以同時註冊多個JDBC驅動 例如:同時註冊 mysql、oralce、db2 驅動 ,通過對JDBC URL分析,決定採用哪個驅動

    static Connection getConnection(String url, String user,String password)  根據jdbc url 和 用戶名、密碼獲得一個數據庫連接


    實際開發中,不推薦使用DriverManager.registerDriver 會導致驅動註冊兩次、會使得程序依賴 具體數據庫API 推薦使用 :Class.forName("com.mysql.jdbc.Driver"); 加載Driver類時完成驅動註冊,使得程序不依賴MySQL的API

2.3 JDBC  URL

    jdbc:mysql://localhost:3306/test

                jdbc: 是JDBC連接協議

                mysql:// 是mysql數據庫連接協議,JDBC子協議

                localhost:3306主機名和端口號

                test數據庫名稱

 

    MySQL 如果連接localhost:3306 可以省略

    jdbc:mysql://localhost:3306/day13--------------- jdbc:mysql:///day11

    JDBCURL 可以通過?和& 攜帶參數

    常用屬性:useUnicode=true&characterEncoding=UTF-8 ----------- 解決操作數據庫亂碼問題

 

    常用數據庫URL寫法:

        MYSQLjdbc:mysql://localhost:3306/test

        ORACLEjdbc:oracle:thin:@localhost:1521:sid

2.4 Connection接口

(1)獲得SQL的操作對象

    Statement          conn.createStatement() 該對象可以將SQL發送給數據庫進行執行

    PreparedStatement        conn.prepareStatement(sql) 對SQL語句進行預編譯,防止SQL注入

    CallableStatement        conn.prepareCall(sql); 該對象可以調用數據庫中存儲過程

(2)對數據庫事務進行管理

        conn.setAutoCommit(boolean); 設置事務是否自動提交

        conn.commit(); 提交數據庫事務

        conn.rollback(); 回滾數據庫事務

2.5 Statement

    用於將SQL發送給數據庫,獲得操作結果

發送單條SQL :

    executeUpdate 用於向數據庫發送 insertupdate delete 語句,返回int 類型參數,代表影響記錄行數

    executeQuery  用於向數據庫發送 select 語句,返回ResultSet 結果集對象

    execute 用於數據庫發送任何SQL語句(包括 DDL DML DCL) 返回boolean ,SQL執行結果是ResultSet 返回true,否則 false

發送多條SQL:

    addBatch(sql) 將SQL加入批處理隊列

    executeBatch() 執行隊列中所有SQL語句 ,一次性向數據庫發送多條SQL

2.6 ResultSet遍歷結果集

while(rs.next()){

   // 根據數據庫內部 列類型,選擇相應 getXXX方法

   int ---- getInt

   varchart ----getString

   date ----- getDate

}

    getXXX 有兩種寫法 

        第一種 getString(index) 結果集中列索引 

        第二種getString(列名)

    boolean next():遊標下移。返回值是有無記錄

    boolean previous():遊標上移。

    boolean absolute(int count):定位到指定的行。第一行是1。

    void beforeFirst():移動遊標到第一行的前面。

    void afterLast():移動遊標到最後一行的後面。

2.7 滾動結果集

    Connection 接口的 createStatement()  返回Statement對象,操作SQL後 產生ResultSet默認執行next 向前滾動,不支持在滾動中對數據進行修改 (只讀不執行滾動)

    Connection 接口還提供createStatement(int resultSetType, int resultSetConcurrency) 在創建Statement對象 設置結果集類型,併發策略


結果集類型:

    ResultSet.TYPE_FORWARD_ONLY 只能向前,只能調用next 不能向回滾動

    ResultSet.TYPE_SCROLL_INSENSITIVE 支持結果集向回滾動,不能查看修改結果

    ResultSet.TYPE_SCROLL_SENSITIVE  支持結果集向回滾動,查看修改結果

結果集併發策略:

    ResultSet.CONCUR_READ_ONLY 只讀

    ResultSet.CONCUR_UPDATABLE 支持修改

 

常見三種組合:

    ResultSet.TYPE_FORWARD_ONLY 和 ResultSet.CONCUR_READ_ONLY (默認) 只讀不支持向回滾動

    ResultSet.TYPE_SCROLL_INSENSITIVE 和ResultSet.CONCUR_READ_ONLY  只讀,支持向回滾動

    ResultSet.TYPE_SCROLL_SENSITIVE 和 ResultSet.CONCUR_UPDATABLE 支持向回滾動,支持對數據修改

3. SQL注入

        假設有登錄案例SQL語句如下SELECT * FROM 用戶表 WHERE NAME = 用戶輸入的用戶名 AND PASSWORD = 用戶輸的密碼;

        當用戶輸入正確的賬號與密碼後,查詢到了信息則讓用戶登錄。但是當用戶輸入的賬號爲XXX 密碼爲:XXX’  OR ‘1’=’1時,則真正執行的代碼變爲:SELECT* FROM 用戶表 WHERE NAME = ‘XXX’ AND PASSWORD =’ XXX’  OR ’1’=’1’;

        此時,上述查詢語句時永遠可以查詢出結果的。那麼用戶就直接登錄成功了,這便是SQL注入問題。可以使用PreparedStatement來解決對應的問題。

4. 預處理對象

    使用PreparedStatement預處理對象時,建議每條sql語句所有的實際參數,都使用逗號分隔。

    String sql = "insert into sort(sid,sname)values(?,?)";;

    PreparedStatement預處理對象代碼:

        PreparedStatement psmt = conn.prepareStatement(sql)

  常用方法:

(1)執行SQL語句:

     int executeUpdate(); //執行insert updatedelete語句.

     ResultSet executeQuery();//執行select語句.

     boolean execute(); //執行select返回true 執行其他的語句返回false.

(2)設置實際參數

     void setXxx(int index, Xxx xx) 將指定參數設置爲給定Java的xx值。在將此值發送到數據庫時,驅動程序將它轉換成一個 SQL Xxx類型值。例如:setString(2, "薛之謙") 把SQL語句中第2個位置的佔位符?替換成實際參數 "演員"

        @Test
	public void run02() throws Exception{
		//註冊驅動
		Class.forName("com.mysql.jdbc.Driver");
		//獲取連接
		Connection conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "root");
		//使用PreparedStatement預編譯SQL
		PreparedStatement ps=conn.prepareStatement("insert into stu(sid,sname,age) values(?,?,?)");
		ps.setString(1, "9527");
		ps.setString(2, "kobe");
		ps.setInt(3, 35);
		//執行SQL語句
		int i=ps.executeUpdate();
		System.out.println(i);
		
		//釋放資源
		ps.close();
		conn.close();
		
	}

    PreparedStatement 與Statement:

                  (1)語法不同:PreparedStatement可以使用預編譯的sql,而Statment只能使用靜態的sql

                  (2)效率不同:PreparedStatement可以使用sql緩存區,效率比Statment高

                  (3)安全性不同:PreparedStatement可以有效防止sql注入,而Statment不能防止sql注入。

 

5. properties配置文件

5.1 使用properties配置文件

    開發中獲得連接的4個參數(驅動、URL、用戶名、密碼)通常都存在配置文件中,方便後期維護,程序如果需要更換數據庫,只需要修改配置文件即可。

通常情況下,我們習慣使用properties文件,此文件將做如下要求:

(1)文件位置:任意,建議src下

(2)文件名稱:任意,擴展名爲properties

(3)文件內容:一行一組數據,格式是“key=value”.

        a) key命名自定義,如果是多個單詞,習慣使用點分隔。例如:jdbc.driver

        b) value值不支持中文,如果需要使用非英文字符,將進行unicode轉換。

5.2 創建配置文件

    在項目跟目錄下,創建文件,輸入“db.properties”文件名。

    文件中的內容

        driver=com.mysql.jdbc.Driver

        url=jdbc:mysql://localhost:3306/test

        user=root

        password=root

5.3 加載配置文件

    對應properties文件處理,開發中也使用Properties對象進行。將加載properties文件獲得流,然後使用Properties對象進行處理。

public void run03() throws Exception{
		FileInputStream fis=new FileInputStream("db.properties");
		Properties pps=new Properties();
		pps.load(fis);
		
		String driver=pps.getProperty("driver");
		String url=pps.getProperty("url");
		String username=pps.getProperty("username");
		String password=pps.getProperty("password");
		
		Connection conn=null;
		PreparedStatement ps=null;
		ResultSet rs=null;
		
		try {
			//加載驅動
			Class.forName(driver);
			//獲得連接
			conn=DriverManager.getConnection(url, username, password);
			//預編譯SQL
			String sql="select * from stu";
			ps=conn.prepareStatement(sql);
			//執行SQL獲得結果集
			rs=ps.executeQuery();
			//遍歷結果集
			while(rs.next()){
				System.out.println(rs.getString("sid"));
				System.out.println(rs.getString("sname"));
				System.out.println(rs.getInt("age"));
				System.out.println(rs.getString("gender"));
			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
			//釋放資源
			if(rs!=null){
				rs.close();
			}
			if(ps!=null){
				ps.close();
				}
			if(conn!=null){
				conn.close();
			}
		}

6. JDBC進行批處理

業務場景:當需要向數據庫發送一批SQL語句執行時,應避免向數據庫一條條的發送執行,而應採用JDBC的批處理機制,以提升執行效率。

實現批處理有兩種方式,第一種方式:

    •    Statement.addBatch(sql)

    •    執行批處理SQL語句

    •    executeBatch()方法:執行批處理命令

    •    clearBatch()方法:清除批處理命令

    採用Statement.addBatch(sql)方式實現批處理:

    •    優點:可以向數據庫發送多條不同的SQL語句。

    •    缺點:

        (1)SQL語句沒有預編譯。

        (2)當向數據庫發送多條語句相同,但僅參數不同的SQL語句時,需重複寫上很多條SQL語句。例如:

                  Insertinto user(name,password) values(‘aa’,’111’);

                  Insertinto user(name,password) values(‘bb’,’222’);

                  Insertinto user(name,password) values(‘cc’,’333’);

                  Insertinto user(name,password) values(‘dd’,’444’);

 

實現批處理的第二種方式:

•    PreparedStatement.addBatch()

     採用PreparedStatement.addBatch()實現批處理

        •    優點:發送的是預編譯後的SQL語句,執行效率高。

        •    缺點:只能應用在SQL語句相同,但參數不同的批處理中。因此此種形式的批處理經常用於在同一個表中批量插入數據,或批量更新表的數據. 

注:

默認使用PreparedStatement是不能執行預編譯的,這需要在url中給出useServerPrepStmts=true參數(MySQL Server4.1之前的版本是不支持預編譯的,而Connector/J5.0.5以後的版本,默認是沒有開啓預編譯功能的)。

例如:jdbc:mysql://localhost:3306/test?useServerPrepStmts=true

這樣才能保證mysql驅動會先把SQL語句發送給服務器進行預編譯,然後在執行executeQuery()時只是把參數發送給服務器。

 

當使用不同的PreparedStatement對象來執行相同的SQL語句時,還是會出現編譯兩次的現象,這是因爲驅動沒有緩存編譯後的函數key,導致二次編譯。如果希望緩存編譯後函數的key,那麼就要設置cachePrepStmts參數爲true。例如:

jdbc:mysql://localhost:3306/test?useServerPrepStmts=true&cachePrepStmts=true

 

MySQL的批處理也需要通過參數來打開:rewriteBatchedStatements=true

例如:jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true


Mysql驅動要使用mysql-connector-java-5.1.13以上

7. 使用CablleStatement調用存儲過程

        @Test
	public void test2(){
		Connection conn = null;
		CallableStatement stmt = null;
		ResultSet rs = null;
		try {
			//獲取連接
			conn = JdbcUtil.getConnection();
			//準備sql
			String sql = "CALL pro_findById2(?,?)"; //第一個?是輸入參數,第二個?是輸出參數
			//預編譯
			stmt = conn.prepareCall(sql);
			
			//設置輸入參數
			stmt.setInt(1, 6);
			//設置輸出參數(註冊輸出參數)
			/**
			 * 參數一: 參數位置
			 * 參數二: 存儲過程中的輸出參數的jdbc類型    VARCHAR(20)
			 */
			stmt.registerOutParameter(2, java.sql.Types.VARCHAR);
			
			//發送參數,執行
			stmt.executeQuery(); //結果不是返回到結果集中,而是返回到輸出參數中
			
			//得到輸出參數的值
			/**
			 * 索引值: 預編譯sql中的輸出參數的位置
			 */
			String result = stmt.getString(2); //getXX方法專門用於獲取存儲過程中的輸出參數
			
			System.out.println(result);

		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		} finally {
			JdbcUtil.close(conn, stmt ,rs);
		}
	}



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