java數據庫連接(一)--從最簡單地jdbc連接說起

      JDBCJDBC全稱"Java DataBase Connectivity",它是一套面向對象的應用程序接口(API),並且制定了統一的訪問各類關係數據庫的標準接口,爲各個數據庫廠商提供了標準的接口實現。通過使用JDBC技術,開發人員可以用純Java語言和標準的SQL語句編寫完整的數據庫應用程序,真正地實現軟件的跨平臺。

       

        本文地址:http://blog.csdn.net/chen_zw/article/details/18514723

       JDBC對多種關係型數據庫的驅動和操作都進行了封裝,因此,開發者不需要編寫額外的程序來兼容不同的數據庫連接,只需要通過加載不同的數據庫驅動程序即可完成連接,我們首先簡單地封裝JDBC連接類:

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import oracle.jdbc.driver.OracleConnection;

/**
 * @Description: JDBC連接類(示例連接Oralce)
 * @CreateTime: 2014-1-19 下午9:46:44
 * @author: chenzw 
 * @version V1.0
 */
public class JdbcUtil {
    //驅動名  
    private static String DRIVER = "oracle.jdbc.driver.OracleDriver";  
    //獲得url  
    private static String URL = "admin";  
    //獲得連接數據庫的用戶名  
    private static String USER = "jdbc:oracle:thin:@localhost:7001:test";  
    //獲得連接數據庫的密碼  
    private static String PASS = "";  

    static {  
        try {   
            //1.初始化JDBC驅動並讓驅動加載到jvm中,加載JDBC驅動後,會將加載的驅動類註冊給DriverManager類。
            Class.forName(DRIVER);  
        } catch (ClassNotFoundException e) {  
            e.printStackTrace();  
        }  
    }  
	
     public static Connection getConnection(){  
        Connection conn = null;  
        try {   
            //2.取得連接數據庫  
            conn = DriverManager.getConnection(URL,USER,PASS);  
            //3.開啓自動提交
            conn.setAutoCommit(true);
        } catch (SQLException e) {  
            e.printStackTrace();  
        }  
        return conn;  
     }  

    //開啓事務
    public static void beginTransaction(Connection conn) {  
        if (conn != null) {  
            try {  
                if (conn.getAutoCommit()) {  
                	conn.setAutoCommit(false);  
                }  
            } catch (SQLException e) {  
                e.printStackTrace();  
            }  
        }  
    }  
  
    //提交事務
    public static void commitTransaction(Connection conn) {  
        if (conn != null) {  
            try {  
                if (!conn.getAutoCommit()) {  
                	conn.commit();  
                }  
            } catch (SQLException e) {  
                e.printStackTrace();  
            }  
        }  
    }  
  
    //回滾事務
    public static void rollBackTransaction(Connection conn) {  
        if (conn != null) {  
            try {  
                if (!conn.getAutoCommit()) {  
                	conn.rollback();  
                }  
            } catch (SQLException e) {  
                e.printStackTrace();  
            }  
        }  
    }  
	
     //關閉連接
     public static void close(Object o){  
        if (o == null){  
            return;  
        }  
        if (o instanceof ResultSet){  
            try {  
                ((ResultSet)o).close();  
            } catch (SQLException e) {  
                e.printStackTrace();  
            }  
        } else if(o instanceof Statement){  
            try {  
                ((Statement)o).close();  
            } catch (SQLException e) {  
                e.printStackTrace();  
            }  
        } else if (o instanceof Connection){  
            Connection c = (Connection)o;  
            try {  
                if (!c.isClosed()){  
                    c.close();  
                }  
            } catch (SQLException e) {  
                e.printStackTrace();  
            }  
        }    
    }  

    //重載關閉連接
    public static void close(ResultSet rs, Statement stmt,   
            Connection conn){  
    	close(rs);  
    	close(stmt);  
    	close(conn);  
    }  
    //重載關閉連接
    public static void close(ResultSet rs,   
            Connection conn){  
    	close(rs);   
    	close(conn);  
    } 
    //重載關閉連接
    public static void close(ResultSet rs, PreparedStatement ps,   
            Connection conn){  
    	close(rs);  
    	close(ps);  
    	close(conn);  
    }  
}

        JDBC數據庫連接的查詢主要通過Statement對象來執行各種SQL語句。Statement對象主要分爲以下3種類型:

      1. Statement :執行靜態SQL語句的對象。

             Statement接口常用的2個方法:

             (1) executeUpdate(String sql) :執行insert / update / delete 等SQL語句,成功返回影響數據庫記錄行數的int整數型。

             (2) executeQuery(String sql) : 執行查詢語句,成功返回一個ResultSet類型的結果集對象。

ResultSet rs = null;
Statement stmt = null;
try {
	stmt = conn.createStatement();
	int num = stmt.executeUpdate("insert into company values('No.1','CSDN')");
	rs = stmt.executeQuery("select * from company");
} catch (SQLException e) {
	// TODO Auto-generated catch block
	e.printStackTrace();
        conn.rollback();
}finally{
	JdbcUtil.close(rs, stmt, conn);
}

         1.1 Statement批量處理函數:

               (1) addBatch(String sql) :添加批量處理的數據;

               (2) executeBatch():提交批量數據;

               (3) clearBatch():清空已添加的批量數據;

/**
  * 實驗一:Statement.executeBatch();
  */
ResultSet rs = null;
Statement stmt = null;
try {
	conn.setAutoCommit(false); //切記:必須設置成手動提交模式,否則每次addBatch都會提交一次,而不是批量提交
	stmt = conn.createStatement();
	Long startMemory  = Runtime.getRuntime().freeMemory();
	Long startTime = System.currentTimeMillis(); 
	for(int i = 0; i < 10000; i++){
		stmt.addBatch("insert into t_dept values('No.1','CSDN')");
		//stmt.executeUpdate("insert into t_dept values('No.1','CSDN')");
	}
	stmt.executeBatch();
	conn.commit();
	Long endMemory = Runtime.getRuntime().freeMemory();
	Long endTime = System.currentTimeMillis(); 
	System.out.println("使用內存大小:"+ (startMemory-endMemory)/1024 + "KB");
	System.out.println("用時:"+ (endTime-startTime)/1000 + "s");
} catch (SQLException e) {
	e.printStackTrace();
	conn.rollback();
}finally{
	JdbcUtil.close(rs, stmt, conn);
}

//執行結果:
使用內存大小:488KB
用時:116s   --汗,原諒我的電腦龜速。
/**
  * 實驗二:Statement.executeUpdate();
  */
//而如果直接使用executeUpdate更新(其實一樣是批量提交),竟然比executeBatch速度更快,效率更高,這個真的百思不得其解,留待以後解決。。
ResultSet rs = null;
Statement stmt = null;
try {
	conn.setAutoCommit(false); //切記:必須設置成手動提交模式,否則每次addBatch都會提交一次,而不是批量提交
	stmt = conn.createStatement();
	Long startMemory  = Runtime.getRuntime().freeMemory();
	Long startTime = System.currentTimeMillis(); 
	for(int i = 0; i < 10000; i++){
	        //stmt.addBatch("insert into t_dept values('No.1','CSDN')");
		stmt.executeUpdate("insert into t_dept values('No.1','CSDN')");
	}
	//stmt.executeBatch();
	conn.commit();
	Long endMemory = Runtime.getRuntime().freeMemory();
	Long endTime = System.currentTimeMillis(); 
	System.out.println("使用內存大小:"+ (startMemory-endMemory)/1024 + "KB");
	System.out.println("用時:"+ (endTime-startTime)/1000 + "s");
} catch (SQLException e) {
	e.printStackTrace();
	conn.rollback();
}finally{
	JdbcUtil.close(rs, stmt, conn);
}
//執行結果:
<span style="font-family: Arial, Helvetica, sans-serif;">使用內存大小:329KB</span>
用時:98s
Note:本次實驗得出的結論是使用executeUpdate的批量提交法比executeBatch效率高,速度更快,佔用內存更小,如果有朋友有不同結論的話,歡迎留言交流。

       1.3 Statement可滾動ResultSet配置:

              (1) java.sql.Connection.creatStatement(int resultSetType,int resultSetConcurrency);

              (2) java.sql.Connection.creatStatement(int resultSetType,int resultSetConcurrency,int resultSetHoldability);

              參數:

              [1] resultSetType: -- 生成的ResultSet的類型

                   ResultSet.TYPE_FORWARD_ONLY  -- 結果集只允許向前滾動,默認缺省值

                   ResultSet.TYPE_SCROLL_INSENSITIVE  --  結果集對數據庫中的的數據變動是不敏感的

                   ResultSet.TYPE_SCROLL_SENSITIVE  --  結果集對數據庫中的的數據變動是敏感的(實際上只對Update的數據敏感)

                  Note:ResultSet爲TYPE_SCROLL_INSENSITIVE時,我們取出的是數據庫中真實數據,並且保存在內存中,一旦取出後,數據庫中數據進行變動將不能及時更新到ResultSet中; 而ResultSet爲TYPE_SCROLL_SENSITIVE時,我們取出的是數據庫中數據的索引值(類似於rowid),每次rs.next()時將根據該索引時重新從數據庫中取出數據,所以說是敏感的,但是如果數據庫中進行的是添加或是刪除操作,那敏感性也將無效。(自己思考,索引值)

              [2] resultSetConcurrency: -- 生成的ResultSet的併發性(指定是否可修改ResultSet結果集)

                    ResultSet.CONCUR_READ_ONLY  -- 結果集不可修改(只讀),默認缺省值

                    ResultSet.CONCUR_UPDATABLE  -- 結果集可修改

ResultSet rs = null;
Statement stmt = null;
try {
	stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_READ_ONLY);  //可滾動,
        rs = stmt.executeQuery("select * from staff");
        System.out.println(rs.isBeforeFirst()); //true,判斷指針是否在結果集的開頭
	rs.beforeFirst(); //指針定位到開頭,該行取不到數據
	rs.next(); //必須先把指針移到數據上,纔算是結果集的第一行
        System.out.println(rs.isFirst()); //true,判斷指針是否在結果集的第一行
	rs.first();   //指針重新定位到結果集的第一行
	rs.absolute(20);  //指針定位到結果集的第20行
	rs.previous();  //指針移動到上一行
	System.out.println(rs.getString(1));
	rs.last(); //指針定位到結果集的最後一行
	System.out.println(rs.isLast());  //true,判斷指針是否在結果集最後一行
	rs.afterLast(); //指針定位到末尾,該行取不到數據
        System.out.println(rs.isAfterLast()); //true,判斷指針是否在結果集的末尾
} catch (SQLException e) {
	e.printStackTrace();
	conn.rollback();
}finally{
	JdbcUtil.close(rs, stmt, conn);
}	<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">             </span>

      2. PrepareStatement: 執行預編譯SQL語句對象。

             PrepareStatement接口常用的2個方法:

             (1) executeUpdate(String sql) :執行insert / update / delete 等SQL語句,成功返回影響數據庫記錄行數的int整數型。

             (2) executeQuery(String sql) : 執行查詢語句,成功返回一個ResultSet類型的結果集對象。

ResultSet rs = null;
PreparedStatement ps;
try {
	ps = conn.prepareStatement("insert into company values(?,?)");
	ps.setString(1, "No1.");
	ps.setString(2, "csdn");
	int num = ps.executeUpdate();
} catch (SQLException e) {
	// TODO Auto-generated catch block
	e.printStackTrace();
        conn.rollback();
}finally{
	JdbcUtil.close(rs, stmt, conn);
}
		
try {
	ps = conn.prepareStatement("select * from company");
	ps.setString(1, "No1.");
	ps.setString(2, "csdn");
	rs = ps.executeQuery();
} catch (SQLException e) {
	// TODO Auto-generated catch block
	e.printStackTrace();
        conn.rollback();
}finally{
	JdbcUtil.close(rs, stmt, conn);
}

              優點:(1)可以有效SQL注入式攻擊,提高安全性。(2)使用預處理語句在性能上提升很多。

         2.1 PrepareStatement批量處理函數:

               (1) addBatch(String sql) :添加批量處理的數據;

               (2) executeBatch():提交批量數據;

               (3) clearBatch():清空已添加的批量數據;

/**
  * 實驗三:PreparedStatement.executeBatch();
  */
ResultSet rs = null;
PreparedStatement ps = null;
try {
	conn.setAutoCommit(false); //切記:必須設置成手動提交模式,否則每次addBatch都會提交一次,而不是批量提交
	Long startMemory  = Runtime.getRuntime().freeMemory();
	Long startTime = System.currentTimeMillis(); 
	ps = conn.prepareStatement("insert into t_dept values(?,?)");
	for(int i = 0 ; i < 10000; i++){
		ps.setString(1, "No1.");
		ps.setString(2, "csdn");
		ps.addBatch();
		//ps.executeUpdate();
	}
	ps.executeBatch();
	conn.commit();
	Long endMemory = Runtime.getRuntime().freeMemory();
	Long endTime = System.currentTimeMillis(); 
	System.out.println("使用內存大小:"+ (startMemory-endMemory)/1024 + "KB");
	System.out.println("用時:"+ (endTime-startTime)/1000 + "s");
} catch (SQLException e) {
	// TODO Auto-generated catch block
	e.printStackTrace();
}finally{
	JdbcUtil.close(rs, ps, conn);
}

//執行結果:
使用內存大小:2110KB
用時:0s  --無語了,實在太快了,10000條。。。
/**
  * 實驗四:PreparedStatement.executeUpdate();
  */
//直接使用executeUpdate批量提交,速度慢的可怕。。
ResultSet rs = null;
PreparedStatement ps = null;
try {
     conn.setAutoCommit(false); //切記:必須設置成手動提交模式,否則每次addBatch都會提交一次,而不是批量提交
     Long startMemory  = Runtime.getRuntime().freeMemory();
     Long startTime = System.currentTimeMillis(); 
     ps = conn.prepareStatement("insert into t_dept values(?,?)");
     for(int i = 0 ; i < 10000; i++){
         ps.setString(1, "No1.");
         ps.setString(2, "csdn");
         //ps.addBatch();
         ps.executeUpdate();
     }
     //ps.executeBatch();
     conn.commit();
     Long endMemory = Runtime.getRuntime().freeMemory();
     Long endTime = System.currentTimeMillis(); 
     System.out.println("使用內存大小:"+ (startMemory-endMemory)/1024 + "KB");
     System.out.println("用時:"+ (endTime-startTime)/1000 + "s");
} catch (SQLException e) {
// TODO Auto-generated catch block
     e.printStackTrace();
}finally{
     JdbcUtil.close(rs, ps, conn);
}

//執行結果:
使用內存大小:1617KB
用時:190s  --內存佔用少了點,但是時間嘛,我只能說呵呵。。
總結:使用批量提交數據一定要選用預編譯的executeBatch()方法,雖然內存佔用多了點,但是速度快纔是王道。

         2.2 PrepareStatement可滾動ResultSet配置:

              (1) java.sql.Connection.PreparedStatement prepareStatement(String sql,int resultSetType,int resultSetConcurrency);

              (2) java.sql.Connection.PreparedStatement prepareStatement(String sql,int resultSetType,int resultSetConcurrency,int resultSetHoldability);

              參數及其用法詳見上面1.3。

      3. CallableStatement接口常用的2個方法:

             (1) executeUpdate(String sql) :執行insert / update / delete 等SQL語句,成功返回影響數據庫記錄行數的int整數型。

             (2) executeQuery(String sql) : 執行查詢語句,成功返回一個ResultSet類型的結果集對象。

ResultSet rs = null;
CallableStatement cs;
try {
	cs = conn.prepareCall("{call p_insert(?,?)}");
	cs.setString(1, "No1.");
	cs.setString(2, "csdn");
	int num = cs.executeUpdate();
} catch (SQLException e) {
	// TODO Auto-generated catch block
	e.printStackTrace();
        conn.rollback();
}finally{
	JdbcUtil.close(rs, stmt, conn);
}

try {
	cs = conn.prepareCall("{call p_select(?)}");
	cs.setString(1,  "No1.");
	rs = cs.executeQuery();
} catch (SQLException e) {
	// TODO Auto-generated catch block
	e.printStackTrace();
        conn.rollback();
}finally{
	JdbcUtil.close(rs, stmt, conn);
}

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