一、JDBC
JDBC的全稱是java database connection java數據庫連接。
在java中需要對數據庫進行一系列的操作,這時就需要使用JDBC。
sun公司制定了關於數據庫操作的組接口,數據庫廠商需要按照這個接口編寫對應的實現類。
數據庫廠商編寫的實現類就稱為數據庫驅動。
java訪問數據庫流程:
1.加載驅動:加載數據庫廠商提供的實現類。
2.建立連接:建立程序與數據庫的連接
3.SQL語句:執行相應SQL語句
4.結果集:得到查詢結果。
二、JDBC訪問數據庫
為了便於查看和操作數據庫可以下載數據庫可視化軟件(例如Navicat)。
2.1准備工作:
2.1.1 下載驅動
首先我們需要下載數據庫廠商提供的JDBC驅動。
2.1.2 導入驅動
由於這些驅動是數據庫廠商實現的,並不在java本身的庫中,所以需要從外部導入到項目中。
鼠標右擊項目名稱--->Bulid Path-->Configure Build Path... --> Libraries --> Add External JARs...
-->
-->
-->
然后找到下載的JDBC驅動所在文件夾,選擇里面的mysql-connector-java-x.x.xx.jar,然后點擊OK即可。
添加完畢后項目顯示添加的驅動。
2.2加載驅動
public class TestJDBC{ public static void main(String[] args){ try { //加載驅動 System.out.println(Class.forName("com.mysql.cj.jdbc.Driver")); //調用froName("x")會初始化名為x的類 } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
運行結果: class com.mysql.cj.jdbc.Driver
這樣就代表驅動已經加載好了,這里的“com.mysql.cj.jdbc”根據下載的JDBC版本不同可能會略有不同。
一開始我寫的是com.mysql.jdbc,運行后控制台提示:
Loading class `com.mysql.jdbc.Driver'.
This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'.
后來修改為com.mysql.cj.jdbc.Driver就沒有問題了,這個在網上查下就可以解決了。
2.3建立連接:建立程序與數據庫的連接
建立數據庫連接首先我們需要了解一個類:DriverManager。
DriverManager:管理JDBC驅動的基本服務,作用於用戶和驅動程序之間。
追蹤可用的驅動程序,並在數據庫和相關的驅動程序之間建立連接。
主要方法:
public static Connection getConnection(String url,String user, String password);
//根據給定的url地址,嘗試建立數據庫連接。返回一個Connection對象。
//user,password為用戶名和密碼。
import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class TestJDBC{ public static void main(String[] args){ final String connectionUrl = "jdbc:mysql://localhost:3306/mybatis"; String userName = "root"; String passWord = "123456"; try { //加載驅動 System.out.println(Class.forName("com.mysql.cj.jdbc.Driver")); Connection conn = DriverManager.getConnection(connectionUrl,userName,passWord); //調用froName("x")會初始化名為x的類 } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
運行后沒有報錯,代表基本的連接沒有問題,報錯的原因有很多,一般是url格式錯誤,或用戶名密碼錯誤。
2.4SQL語句:執行相應SQL語句
本例所用表結構: (數據庫名:mybatis,表名:tadd)
CREATE TABLE `tadd` ( `id` varchar(50) NOT NULL, `tname` varchar(50) DEFAULT NULL, `tpwd` varchar(50) DEFAULT NULL, `tstudentnum` varchar(50) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
上面通過getConnection()建立了連接,並且返回了一個Connection對象。
我們來看下Connection對象的作用及方法。
Connection:代表了一個與指定數據庫的連接,執行SQL語句返回並在連接上下文中返回結果。
主要方法:
Statement createStatement();//創建將SQL語句發送到數據庫的語句對象。
PreparedStatement(String sql);//創建PreparedStatement對象。
2.4.1 Statement
Statement:主要用於執行靜態的SQL語句,並返回其生成的結果對象。
主要方法:
boolean execute(String sql);執行給定的sql語句,可能返回多個結果。
import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; public class TestJDBC{ public static void main(String[] args){ final String connectionUrl = "jdbc:mysql://localhost:3306/mybatis"; String userName = "root"; String passWord = "123456"; try { //加載驅動 System.out.println(Class.forName("com.mysql.cj.jdbc.Driver")); Connection conn = DriverManager.getConnection(connectionUrl,userName,passWord); Statement sta = conn.createStatement(); boolean b = sta.execute("INSERT INTO `mybatis`.`tadd`" + "(`id`, `tname`, `tpwd`, `tstudentnum`) " + "VALUES ('17', '17', '77', '777');"); System.out.println("執行成功"); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
運行結果: class com.mysql.cj.jdbc.Driver 執行成功
我們使用可視化軟件查看,發現對應數據庫(mybatis)中,對應的表(tadd)里面添加了一條記錄。
Statement有個缺點,不能防止SQL注入。
例如:
import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; public class TestJDBC{ public static void main(String[] args){ final String connectionUrl = "jdbc:mysql://localhost:3306/mybatis"; String userName = "root"; String passWord = "123456"; try { //加載驅動 System.out.println(Class.forName("com.mysql.cj.jdbc.Driver"));
//建立連接 Connection conn = DriverManager.getConnection(connectionUrl,userName,passWord); // Statement sta = conn.createStatement(); String sql = "delete from tadd where id = 1 or 1=1"; Statement ps = conn.createStatement();//創建Statement對象 ps.execute(sql);//執行SQL語句 System.out.println("執行成功"); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
運行結果: class com.mysql.cj.jdbc.Driver 執行成功
使用可視化軟件我們可以看到整個表的信息都被刪除了。
這時因為 where id = 1 or 1=1,關鍵就在后面的or 1=1,在滿足id = 1 或者 1=1的條件下刪除。
可1=1恆成立,加上兩者又是or的關系,所以所有信息都滿足刪除都被刪除了。
2.4.2PreparedStatement
上面我們是使用Statments來執行SQL語句,但它存在一定的缺點。
這時我們可以使用PreparedStatement,它可以防止SQL注入。
PreparedStatement:表示預編譯SQL語句的對象。
SQL語句預編譯並存儲在PreparedStatement對象中。然后可以使用此對象多次高效地執行此語句。
繼承自Statement接口,用於發送一個或多個待輸入參數的sql語句。
主要方法:
void setObject(int parameterIndex, Object x);//給指定的參數索引設置指定對象x。
使用Oject的話不用在意參數類型,比較方便。
除了Object作為參數類型外,還有一些設置具體類型的函數。
void setNString(int parameterIndex, String value);給指定的參數索引設置指定的String類型的值。
void setInt(int parameterIndex, int x);給指定的參數索引設置指定的int類型的值。
void setXXX..(int parameterIndex, XXX x);設置對應類型的值,此處就不一一舉例了,詳見API。
此處的參數索引代表占位符的位序,具體含義詳見下列代碼注釋。
boolean execute();執行SQL語句
import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.SQLException; public class TestJDBC{ public static void main(String[] args){ final String connectionUrl = "jdbc:mysql://localhost:3306/mybatis"; String userName = "root"; String passWord = "123456"; try { //加載驅動 System.out.println(Class.forName("com.mysql.cj.jdbc.Driver")); Connection conn = DriverManager.getConnection(connectionUrl,userName,passWord); // Statement sta = conn.createStatement(); String sql = "INSERT INTO `mybatis`.`tadd`" + "(`id`, `tname`, `tpwd`, `tstudentnum`) " + "VALUES (?, ?, ?, ?)";//?代表占位符, parameterIndex分別為1,2,3,4 PreparedStatement ps = conn.prepareStatement(sql); ps.setObject(1, "p1");//此處的1代表第一個占位符,即sql語句中第一個問號 ps.setObject(2, "p2");//第二個占位符,后面參數代表該占位符所代表的值 ps.setObject(3, "p3");//可用setNString(3,"p3")代替 ps.setObject(4, "p4"); ps.execute();//執行sql語句 System.out.println("執行成功"); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
運行結果: class com.mysql.cj.jdbc.Driver 執行成功
可以將sql語句設置為 delete from tadd where id = ?
再將?設置為 1 or 1= 1,可以發現表中數據並沒有被全部刪除。
2.5結果集:得到查詢結果。
boolean excute();返回類型是布爾,如果執行SQL語句返回的是結果集則為true,其他防護false;
一般select語句才會返回結果集,所以執行select語句execute()返回true.其余語句一般為false.
excuteQuery();返回結果集,主要用於運行select語句。
excuteUpdata():返回改變的行數,主要用於insert,update,delete語句。
我們可以根據我們執行的語句,選擇合適的方法得到對應的返回值。
Resultset:表示數據庫結果集的數據表,通常通過執行查詢數據庫的語句生成。
主要方法:
boolean next();//將游標移動一行,判斷當前移動后的一行是否有數據。
例如,返回的結果集有,3行(編號依次為1,2,3),游標初始值為0,
調用next()方法后移動到第一行,如果第一行有數據返回true,反之返回false.
String getString(int columnLabel);//將當前行中指定列的內容轉換為字符串形式。
注意這里的列從1開始計數,即數據庫表中第一列數字為1,第二列數字為2.
import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; public class TestJDBC{ public static void main(String[] args){ final String connectionUrl = "jdbc:mysql://localhost:3306/mybatis"; String userName = "root"; String passWord = "123456"; try { //加載驅動 Class.forName("com.mysql.cj.jdbc.Driver"); Connection conn = DriverManager.getConnection(connectionUrl,userName,passWord); // Statement sta = conn.createStatement(); String sql = "select * from tadd where id = ?"; PreparedStatement ps = conn.prepareStatement(sql); ps.setObject(1,"p1"); ResultSet rs = ps.executeQuery();//返回一個結果集對象 while(rs.next()){//判斷是否有數據 System.out.println("查詢結果:" +//獲取對應列數據 rs.getString(1) + "---" + rs.getString(2) + "---" + rs.getString(3) + "---" + rs.getString(4)); } System.out.println("執行成功"); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
運行結果: 查詢結果:p1---p2---p3---p4 執行成功
數據庫查詢完畢后要將創建的各個對象依次關閉,一般是先創立的后關閉。
以上述代碼為例,關閉順序應該是Resultset-->PreparedStatement-->Connection.
關閉時最好為每一個對象添加一個try catch語句,不要將三個對象的關閉放在一個try catch中,
避免關閉其中一個出現異常時,導致其它對象沒有關閉。
三、批處理
當我們需要大量(比如上萬條)執行某一語句時,建議采用批處理這樣可以提高執行效率。
由於PreparedStatement預編譯空間有限,當數據量較大時可能出現異常,所以建議使用Statement。
基本操作和之前的沒有太大區別,只是使用了幾個方法。
setAutoCommit(boolean autoCommit);設置自動提交,默認為true即自動提交,false不自動提交。
addBatch(String sql);添加處理語句到批中。
excuteBatch();提交批。
commit();手動提交
import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class TestJDBC{ public static void main(String[] args){ final String connectionUrl = "jdbc:mysql://localhost:3306/mybatis"; String userName = "root"; String passWord = "123456"; Connection conn = null; Statement ps = null; ResultSet rs = null; try { //加載驅動 Class.forName("com.mysql.cj.jdbc.Driver"); //建立連接 conn = DriverManager.getConnection(connectionUrl,userName,passWord); conn.setAutoCommit(false);//將自動提交設為false,即不進行自動提交 String sql = "select * from tadd where id = ?"; ps = conn.createStatement(); //批處理 for(int i = 0; i < 10000; i++){ ps.addBatch("INSERT INTO `mybatis`.`tadd`" + "(`id`, `tname`, `tpwd`, `tstudentnum`) " + "VALUES ('"+i+"', 'p1', 'p2', 'p3');"); } ps.executeBatch(); conn.commit();//手動提交 System.out.println("執行成功"); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ //依次關閉連接 try { rs.close(); } catch (Exception e) { // TODO: handle exception } try{ ps.close(); }catch(Exception e){ } try{ conn.close(); }catch(Exception e){ } } } }
運行結果:
執行成功
查看數據庫可以看到添加了10000條數據。
批處理就相當於將一個批次的語句打包一起執行,這樣比一條一條單獨執行效率要高。