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配置:
(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);
}