1.簡介 JDBC(Java DataBase Connectivity) 是一種可用於執行SQL語句的Java API,是一套面向對象的應用程式介面, 統一了資料庫的訪問方式,資料庫廠商提供了實現介面的類,稱為‘驅動程式’。因此JDBC並不能直接訪問資料庫, 需要依賴資料庫廠商提供的JDBC驅動程 ...
1.簡介
JDBC(Java DataBase Connectivity) 是一種可用於執行SQL語句的Java API,是一套面向對象的應用程式介面,
統一了資料庫的訪問方式,資料庫廠商提供了實現介面的類,稱為‘驅動程式’。因此JDBC並不能直接訪問資料庫,
需要依賴資料庫廠商提供的JDBC驅動程式。
--SQL語言:
數據定義語言(Data Definition Language,DDL)如:create,alter,drop等
數據操縱語言(Data Manipulation Language,DML)如update,delete等
數據查詢語言(Data Query language,DQL),查詢
數據控制語言(Data Control Language,DCL),如grant,revoke等
事務控制語言(Transaction Control Language,TCL),如commit,rollback等
2.準備工作,以Mysql為例
a.安裝了Mysql 資料庫
b.下載mysql驅動包併在項目中導入jar包:mysql-connector-java-5.1.42-bin.jar
3.JDBC的操作
步驟:
1.註冊驅動
2.連接資料庫
3.創建語句對象,用於執行SQL
4.執行SQL
5.處理執行結果
6.關閉連接
一、JDBC基本操作
1)在資料庫先建立一張表Student
CREATE TABLE student( id INT AUTO_INCREMENT, NAME VARCHAR(20), age INT , PRIMARY KEY(id) );
2)jdbc實現
package com.huan.jdbc; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class JDBCDemo01 { public static void main(String[] args) { //聲明連接 Connection connection = null; try { //載入驅動 Class.forName("com.mysql.jdbc.Driver"); //資料庫連接信息 //地址 jdbc:mysql://地址/資料庫 String url = "jdbc:mysql://localhost:3306/huan"; String user = "root"; String password = "root"; //獲得連接 connection = DriverManager.getConnection(url, user, password); //語句對象 Statement st = connection.createStatement(); String sql = "insert into student (name,age) values ('zhangsan',30)"; //執行sql DDL-execute()方法 DML-executeUpdate()方法 DQL-executeQuery()方法 int n = st.executeUpdate(sql); String querySql = "select id,name,age from student"; ResultSet rs = st.executeQuery(querySql); //處理結果 while(rs.next()){ int id = rs.getInt("id"); String name = rs.getString("name"); int age = rs.getInt("age"); System.out.println("id:"+id+" name:"+name+" age:"+age); } //釋放資源 rs.close(); st.close(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); }finally { //關閉連接 if(connection != null){ try { connection.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } }
上面先執行了一條插入語句,由於主鍵id是自增的,因此直插入了名字zhangsan和年齡30,然後查詢了表的數據
輸出如下:
id:1 name:zhangsan age:30
二、JDBC連接的封裝
由於連接資料庫的過程都是一樣,為了提高代碼的重用,可以將資料庫的連接封裝起來:
首先將資料庫的連接信息放到配置文件中,比如在項目根目錄下新建文件db.properties,如下:
#db.properties jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/huan jdbc.user=root jdbc.password=root
JDBCUtil類
package com.huan.jdbc; import java.io.FileInputStream; 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 JDBCUtil { private static String driverClass; private static String url; private static String user; private static String password; //靜態塊載入資料庫配置屬性 static{ Properties config = new Properties(); try { config.load(new FileInputStream("db.properties")); } catch (Exception e) { e.printStackTrace(); } driverClass = config.getProperty("jdbc.driver"); url = config.getProperty("jdbc.url"); user = config.getProperty("jdbc.user"); password =config.getProperty("jdbc.password"); } //獲取資料庫連接 public static Connection getConnection() throws SQLException { try { Class.forName(driverClass); Connection connection = DriverManager.getConnection(url, user, password); return connection; } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); throw new SQLException("沒有找到驅動",e); } } //關閉資源 public static void close(Connection connection,Statement st,ResultSet rs){ if(connection != null){ try { connection.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if(st != null){ try { st.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if(rs != null){ try { rs.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
測試JDBCUtil
package com.huan.jdbc; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class JDBCUtilTest { public static void main(String[] args) { Connection connection = null; Statement st = null; ResultSet rs =null; try { connection = JDBCUtil.getConnection(); String sql = "select id ,name ,age from student" ; st = connection.createStatement(); rs = st.executeQuery(sql); while(rs.next()){ int id = rs.getInt("id"); String name = rs.getString("name"); int age = rs.getInt("age"); System.out.println("id:"+id+" name:"+name+" age:"+age); } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally { JDBCUtil.close(connection, st, rs); } } }
輸出:
id:1 name:zhangsan age:30
三 SQL預處理及事務
SQL預處理:在之前的處理中都是將sql語句發送到資料庫,有資料庫的sql解釋器把sql語句生成底層的內部命令,然後執行命令,完成操作,當前不斷的
向資料庫發送sql語句,會增加資料庫sq解釋器的負擔,影響執行速度。
而Connection的prepareStatement(Stirng sql)方法能夠對sql語句進行預處理,生成數據底層的命令,並封裝在PrepareStatement對
象中,通過對應的方法執行底層資料庫的命令,從而減輕資料庫的負擔,提高訪問速度。
並且之前的sql都是拼接的,當帶參數拼接時容易造成SQL的註入(參數中含有sql成分,改變原有的sql語句邏輯),而PrepareStatement在
編譯之後,其中的sql執行計劃已經確定,當替換參數時不會改變執行計劃,因此可以避免sql註入。
事務控制: 當我們在編寫邏輯的時候,存在多條的插入或者更新語句,前面的sql成功執行之後出現錯誤,這時導致業務邏輯中斷,而執行成功的數據已經
存入資料庫,從而導致數據不完整。為避免這種事情的發生就需要手動進行事務控制。
1.關閉自動提交--connection.setAutoCommit(false);
2.業務執行完成後提交-connection.commit();
3.在遇到異常時回滾-connection.rollback();
package com.huan.jdbc; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Statement; public class PrepareStatementDemo { public static void main(String[] args) { Connection connection = null; //SQL預處理對象 PreparedStatement ps = null; ResultSet rs =null; try { connection = JDBCUtil.getConnection(); connection.setAutoCommit(false); //?代表參數 String sql = "insert into student (name,age) values(?,?)"; //編譯預執行計劃 在資料庫上創建執行計劃 //(第二個參數可以不要-Statement.RETURN_GENERATED_KEYS 代表返回主鍵,沒有的話下麵的要去掉ps.getGeneratedKeys()會) ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); //?的位置 和 替換的值 ps.setString(1, "lisi"); ps.setInt(2, 20); //n 更新結果的數量 int n = ps.executeUpdate(); System.out.println("成功插入"+n+"條數據"); //獲取插入成功數據的主鍵 ResultSet keySet = ps.getGeneratedKeys(); int key = -1; while(keySet.next()){ key = keySet.getInt(1); } System.out.println("插入數據的主鍵:"+key); //查詢 String querySql = "select id , name ,age from student"; ps = connection.prepareStatement(querySql); rs = ps.executeQuery(); //獲取結果集的元數據相關信息 ResultSetMetaData metaData = rs.getMetaData(); //列數 int count = metaData.getColumnCount(); for(int i = 1; i <= count; i++){ System.out.print(metaData.getColumnName(i)+" "); } System.out.println(); //查詢結果列印 while(rs.next()){ int id = rs.getInt("id"); String name = rs.getString("name"); int age = rs.getInt("age"); System.out.println(id+" "+name+" "+age); } //提交 connection.commit(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); if(connection != null){ try { //出現異常回滾 connection.rollback(); } catch (SQLException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } } }finally { JDBCUtil.close(connection, ps, rs); } } }
輸出結果:
成功插入1條數據 插入數據的主鍵:2 id name age 1 zhangsan 30 2 lisi 20
成功插入了 lisi 這條記錄,且返回主鍵為2.最後列印了列名和查詢得到的數據
這裡用了事務的回滾,如果保存完之後再查詢的中間出現異常,數據是不會存到資料庫的,可以動手試下。
四、JDBC的批量操作
PrepareStatement和Statement都提供了可以批量操作SQL的方法
1.將sql添加至緩存區
Statement: addBatch(sql)方法,可以將sql直接添加Statement緩存區;
PrepareStatement:需要先執行setxxx(n,參數)方法將參數賦值,然後調用addBatch(sql)方法
2.執行executeBatch()將參數批量發送到資料庫,並且執行
3.可以使用clearBatch()清空緩存區的參數或者sql
package com.huan.jdbc; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.Statement; import java.util.Arrays; import org.junit.Test; public class JDBCBatchDemo { @Test public void testJdbcPrepareBatch(){ Connection connection = null; PreparedStatement ps = null; try { connection = JDBCUtil.getConnection(); String sql = "insert into student (name,age) values (?,?)"; ps = connection.prepareStatement(sql); ps.setString(1, "ps1"); ps.setInt(2, 11); ps.addBatch(); ps.setString(1, "ps2"); ps.setInt(2, 12); ps.addBatch(); int [] n = ps.executeBatch(); System.out.println(Arrays.toString(n)); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Test public void testJdbcStatementBatch(){ Connection connection = null; Statement st = null; try { connection = JDBCUtil.getConnection(); String sql1 = "insert into student(name,age) values('statement1',20)"; String sql2 = "insert into student(name,age) values('statement2',30)"; String sql3 = "insert into student(name,age) values('statement3',40)"; st = connection.createStatement(); //批量添加 st.addBatch(sql1); st.addBatch(sql2); st.addBatch(sql3); //執行 int [] n = st.executeBatch(); System.out.println(Arrays.toString(n)); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ JDBCUtil.close(connection, st, null); } } }
testJdbcStatementBatch()測試方法完成後查看資料庫,數據成功添加:
testJdbcPrepareBatch()測試方法執行之後查看資料庫,數據成功添加:
五、連接池(數據源)的使用
連接池能夠重用資料庫的連接,控制資料庫的連接總數,當關閉從連接池中獲取的連接時,只是將此連接歸還給連接池,沒有真正的與資料庫斷開連接。
重用的連接池有:DBCP和c3p0
1)c3p0的使用
官網下載jar包,將c3p0-0.9.5.2.jar和mchange-commons-java-0.2.11.jar加入項目中。
package com.huan.jdbc; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import com.mchange.v2.c3p0.ComboPooledDataSource; public class C3p0DataSourceDemo { public static void main(String[] args) { Connection connection = null; try { ComboPooledDataSource cpds = new ComboPooledDataSource(); //配置連接池參數 cpds.setDriverClass("com.mysql.jdbc.Driver"); cpds.setJdbcUrl("jdbc:mysql://localhost:3306/huan"); cpds.setUser("root"); cpds.setPassword("root"); //連接池的管理策略 ... //最小連接數量 cpds.setMinPoolSize(5); //最大連接數量 cpds.setMaxPoolSize(20); //超時時間ms cpds.setCheckoutTimeout(10000); //獲取連接 connection = cpds.getConnection(); String sql = "select id,name,age from student"; PreparedStatement ps = connection.prepareStatement(sql); ResultSet rs = ps.executeQuery(); while(rs.next()){ int id = rs.getInt("id"); String name = rs.getString("name"); int age = rs.getInt("age"); System.out.println("id:"+id+" name:"+name+" age:"+age); } rs.close(); ps.close(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); }finally { try { connection.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
輸出結果:
id:1 name:zhangsan age:30 id:2 name:lisi age:20 id:3 name:statement1 age:20 id:4 name:statement2 age:30 id:5 name:statement3 age:40 id:6 name:ps1 age:11 id:7 name:ps2 age:12
2)DBCP的使用
官網下載jar包,將commons-logging-1.2.jar,commons-dbcp2-2.1.1.jar和commons-pool2-2.4.2.jar下載後導入項目
package com.huan.jdbc; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import org.apache.commons.dbcp2.BasicDataSource; public class DBCPDemo { public static void main(String[] args) { Connection connection = null; try { BasicDataSource bds = new BasicDataSource(); // 連接參數 bds.setDriverClassName("com.mysql.jdbc.Driver"); bds.setUrl("jdbc:mysql://localhost:3306/huan"); bds.setUsername("root"); bds.setPassword("root"); // 管理策略參數 bds.setInitialSize(5); bds.setMinIdle(5); bds.setMaxTotal(20); bds.setMaxWaitMillis(10000); connection = bds.getConnection(); String sql = "select id,name,age from student"; PreparedStatement ps = connection.prepareStatement(sql); ResultSet rs = ps.executeQuery(); while (rs.next()) { int id = rs.getInt("id"); String name = rs.getString("name"); int age = rs.getInt("age"); System.out.println("id:" + id + " name:" + name + " age:" + age); } rs.close(); ps.close(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { try { if (connection != null) { connection.close(); } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
輸出結果:
id:1 name:zhangsan age:30 id:2 name:lisi age:20 id:3 name:statement1 age:20 id:4 name:statement2 age:30 id:5 name:statement3 age:40 id:6 name:ps1 age:11 id:7 name:ps2 age:12
關於JDBC的一些總結就寫這麼多了~~