SQL學習筆記之資料庫專題(四):淺談JDBC用法,學習筆記jdbc
資料庫廠商提供的用來操作資料庫用的jar包就是資料庫驅動。各個廠商如果提供各自的資料庫驅動的話會導致開發人員學習成本太高,所以sun公司提供了一套資料庫驅動應該遵循的介面規範,這套規範就叫做JDBC,本質上是很多的介面。簡而言之,JDBC就是一套操作資料庫的介面規範,由於所有的資料庫驅動都遵循JDBC規範,我們在學習和使用資料庫時只要學習JDBC中的介面就可以了。
組成JDBC的2個包:java.sql,javax.sql,開發JDBC應用需要以上2個包的支援外,還需要匯入相應JDBC的資料庫實現(即資料庫驅動)。
我們先看看JDCB的使用步驟:
*在資料庫中建立好表
*在程式中匯入資料庫驅動包
1.註冊資料庫驅動
DriverManager.registerDriver(new Driver());//缺點一:觀察mysqlDriver源碼發現此方法導致了資料庫驅動被註冊了兩次。缺點二:整個程式域mysql資料庫驅動綁定增加了耦合性
Class.forName(“com.mysql.jdbc.Driver”);
2.擷取串連
DriverManager.getConnection(url, user, password);
~url的寫法:
Oracle寫法:jdbc:oracle:thin:@localhost:1521:sid
SqlServer—jdbc:microsoft:sqlserver://localhost:1433; DatabaseName=sid
MySql—jdbc:mysql://localhost:3306/sid
~url可以接的參數
user、password
useUnicode=true&characterEncoding=UTF-8
3.擷取傳輸器
createStatement():建立向資料庫發送sql的statement對象。
prepareStatement(sql) :建立向資料庫發送先行編譯sql的PrepareSatement對象。
4.利用傳輸器執行sql語句擷取結果集
executeQuery(String sql) :用於向資料發送查詢語句。
executeUpdate(String sql):用於向資料庫發送insert、update或delete語句
execute(String sql):用於向資料庫發送任意sql語句
5.遍曆結果集取出結構
ResultSet以表的樣式在記憶體中儲存了查詢結果,其中還維護了一個遊標,最開始的時候遊標在第一行之前,每調用一次next()方法就試圖下移一行,如果移動成功返回true;
ResultSet還提供了很多個Get方法,用來擷取查詢結果中的不同類型的資料
除了next方法,還有以下方法可以用來遍曆結果集:
next():移動到下一行
Previous():移動到前一行
absolute(int row):移動到指定行
beforeFirst():移動resultSet的最前面。
afterLast() :移動到resultSet的最後面。
6.釋放資源
conn是一個有限的資源,用完立即要釋放表
stat佔用記憶體,所以使用完後也要釋放
rs佔用記憶體,所以使用完後也要釋放
釋放時後建立的先釋放
if(rs != null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
} finally{
rs = null;
}
}
if(stat != null){
try {
stat.close();
} catch (SQLException e) {
e.printStackTrace();
} finally{
stat = null;
}
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
} finally{
conn = null;
}
}
再看一個簡單的例子:
import java.sql.Connection;import java.sql.DriverManager;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;public class FreedomJDBCDemo1 {public static void main(String[] args){Connection conn = null;Statement stat = null;ResultSet rs = null;try{//1.註冊資料庫驅動//--由於mysql在Driver類的實現中自己註冊了一次,而我們又註冊了一次,於是會導致MySql驅動被註冊兩次//--建立MySql的Driver對象時,導致了程式和具體的Mysql驅動綁死在了一起,在切換資料庫時需要改動java代碼//DriverManager.registerDriver(new Driver());Class.forName("com.mysql.jdbc.Driver");//2.擷取資料庫連接,這裡使用簡潔寫法,可以省略掉localhost和連接埠conn = DriverManager.getConnection("jdbc:mysql:///freedom?user=root&password=root");//3.擷取傳輸器對象stat = conn.createStatement();//4.利用傳輸器傳輸sql語句到資料庫中執行,擷取結果集對象rs = stat.executeQuery("select * from user");//5.遍曆結果集擷取查詢結果while(rs.next()){String name = rs.getString("name");System.out.println(name);}}catch (Exception e) {e.printStackTrace();}finally{//6.關閉資源if(rs!=null){try {rs.close();} catch (SQLException e) {e.printStackTrace();}finally{rs = null;}}if(stat!=null){try {stat.close();} catch (SQLException e) {e.printStackTrace();}finally{stat = null;}}if(conn!=null){try {conn.close();} catch (SQLException e) {e.printStackTrace();}finally{conn = null;}}}}}
我們可以將上述例子封裝一下,將公用部分比如連結資料庫,關閉資源等操作,封裝起來作為一個工具類。我們可以將路徑、使用者民和密碼用適配好。以後只需要修改設定檔即可。
設定檔內容如下:
driver=com.mysql.jdbc.Driver
url=jdbc:mysql:///freedom
user=root
password=root
看工具類:
import java.io.FileReader;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 JDBCUtils {private static Properties prop = null;private JDBCUtils() {}static{try{prop = new Properties();prop.load(new FileReader(JDBCUtils.class.getClassLoader().getResource("config.properties").getPath()));}catch (Exception e) {e.printStackTrace();throw new RuntimeException(e);}}/** * 擷取串連 * @throws ClassNotFoundException * @throws SQLException */public static Connection getConn() throws ClassNotFoundException, SQLException{// 1.註冊資料庫驅動Class.forName(prop.getProperty("driver"));// 2.擷取串連return DriverManager.getConnection(prop.getProperty("url"), prop.getProperty("user"), prop.getProperty("password"));}/** * 關閉串連 */public static void close(ResultSet rs, Statement stat,Connection conn){if(rs!=null){try {rs.close();} catch (SQLException e) {e.printStackTrace();}finally{rs = null;}}if(stat!=null){try {stat.close();} catch (SQLException e) {e.printStackTrace();}finally{stat = null;}}if(conn!=null){try {conn.close();} catch (SQLException e) {e.printStackTrace();}finally{conn = null;}}}}
我們再看使用工具類簡化後的操作例子:
import java.sql.Connection;import java.sql.ResultSet;import java.sql.Statement;import org.junit.Test;import com.itheima.util.JDBCUtils;public class FreedomJDBC {/** * @Title: add * @Description: 增加 * @throws */@Testpublic void add() {Connection conn = null;Statement stat = null;try {// 1.註冊資料庫驅動// 2.擷取串連conn = JDBCUtils.getConn();// 3.擷取傳輸器對象stat = conn.createStatement();// 4.執行sql語句int count = stat.executeUpdate("insert into user values (null,'freedom','123456','freedom@qq.com','2012-01-01')");// 5.處理結果if (count > 0) {System.out.println("執行成功!影響到的行數為" + count);} else {System.out.println("執行失敗!!");}} catch (Exception e) {e.printStackTrace();} finally {// 6.關閉資源JDBCUtils.close(null, stat, conn);}}/** * @Title: delete * @Description:刪除 * @throws */@Testpublic void delete() {Connection conn = null;Statement stat = null;ResultSet rs = null;try {conn = JDBCUtils.getConn();stat = conn.createStatement();stat.executeUpdate("delete from user where name='freedom'");} catch (Exception e) {e.printStackTrace();} finally {JDBCUtils.close(rs, stat, conn);}}/** * @Title: update * @Description: 更新 * @throws */@Testpublic void update() {Connection conn = null;Statement stat = null;try {conn = JDBCUtils.getConn();stat = conn.createStatement();stat.executeUpdate("update user set password=666 where name='freedom'");} catch (Exception e) {e.printStackTrace();} finally {JDBCUtils.close(null, stat, conn);}}/** * @Title: find * @Description: 查詢 * @throws */@Testpublic void find() {Connection conn = null;Statement stat = null;ResultSet rs = null;try {conn = JDBCUtils.getConn();stat = conn.createStatement();rs = stat.executeQuery("select * from user where name='freedom'");while (rs.next()) {String name = rs.getString("name");String password = rs.getString("password");System.out.println(name + ":" + password);}} catch (Exception e) {e.printStackTrace();} finally {JDBCUtils.close(rs, stat, conn);}}}
SQL注入攻擊:
由於dao中執行的SQL語句是拼接出來的,其中有一部分內容是由使用者從用戶端傳入,所以當使用者傳入的資料中包含sql關鍵字時,就有可能通過這些關鍵字改變sql語句的語義,從而執行一些特殊的操作,這樣的攻擊方式就叫做sql注入攻擊
PreparedStatement利用先行編譯的機制將sql語句的主乾和參數分別傳輸給資料庫伺服器,從而使資料庫分辨的出哪些是sql語句的主幹哪些是參數,這樣一來即使參數中帶了sql的關鍵字,資料庫伺服器也僅僅將他當作參數值使用,關鍵字不會起作用,從而從原理上防止了sql注入的問題
PreparedStatement主要有如下的三個優點:
~1.可以防止sql注入
~2.由於使用了先行編譯機制,執行的效率要高於Statement
~3.sql語句使用?形式替代參數,然後再用方法設定?的值,比起拼接字串,代碼更加優雅.
看使用代碼:
import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.ResultSet;import com.itheima.util.JDBCUtils;public class JDBCDemo3 {public static void main(String[] args) {Connection conn = null;PreparedStatement ps = null;ResultSet rs = null;try {conn = JDBCUtils.getConn();ps = conn.prepareStatement("select * from user where name=? and password=?");ps.setString(1, "freedom");ps.setString(2, "666");rs = ps.executeQuery();while (rs.next()) {System.out.println(rs.getString("email"));}} catch (Exception e) {e.printStackTrace();} finally {JDBCUtils.close(rs, ps, conn);}}}
JDBC其實還支援大文本和大二進位的處理,但是實際開發中我們很少使用到,一般情況下,處理這些大資料可能還需要修改虛擬機器的啟動記憶體大小。這裡就講講大位元據組的儲存和讀取吧。
import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.InputStream;import java.io.OutputStream;import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.ResultSet;import org.junit.Test;import com.itheima.util.JDBCUtils;/* * 資料庫中建立一個表 create table blobdemo( id int primary key auto_increment, name varchar(100), content MEDIUMBLOB ); */public class BlobDemo1 {@Testpublic void addBlob() {Connection conn = null;PreparedStatement ps = null;ResultSet rs = null;try {conn = JDBCUtils.getConn();ps = conn.prepareStatement("insert into blobdemo values (null,?,?)");ps.setString(1, "高達.mp3");File file = new File("1.mp3");ps.setBinaryStream(2, new FileInputStream(file), (int) file.length());ps.executeUpdate();} catch (Exception e) {e.printStackTrace();} finally {JDBCUtils.close(rs, ps, conn);}}@Testpublic void findBlob() {Connection conn = null;PreparedStatement ps = null;ResultSet rs = null;try {conn = JDBCUtils.getConn();ps = conn.prepareStatement("select * from blobdemo");rs = ps.executeQuery();while (rs.next()) {String name = rs.getString("name");InputStream in = rs.getBinaryStream("content");OutputStream out = new FileOutputStream(name);byte[] bs = new byte[1024];int i = 0;while ((i = in.read(bs)) != -1) {out.write(bs, 0, i);}in.close();out.close();}} catch (Exception e) {e.printStackTrace();} finally {JDBCUtils.close(rs, ps, conn);}}}
有時候,當需要向資料庫發送一批SQL語句執行時,應避免向資料庫一條條的發送執行,而應採用JDBC的批處理機制,以提升執行效率。JDBC的批處理有兩種方式,各有各的優缺點。我們先看第一種方式:
import java.sql.Connection;import java.sql.Statement;import com.itheima.util.JDBCUtils;/*create database day10batch;use day10batch;create table batchDemo(id int primary key auto_increment,name varchar(20));insert into batchDemo values(null,'aaaa');insert into batchDemo values(null,'bbb');insert into batchDemo values(null,'cc');insert into batchDemo values(null,'d'); *//*Statement方式執行批處理:優點:可以執行多條不同結構的sql語句缺點:沒有使用先行編譯機制,效率低下,如果要執行多條結構相同僅僅參數不同的sql時,仍然需要寫多次sql語句的主幹 */public class StatementBatch {public static void main(String[] args) {Connection conn = null;Statement stat = null;try{conn = JDBCUtils.getConn();stat = conn.createStatement();stat.addBatch("create database day10batch");stat.addBatch("use day10batch");stat.addBatch("create table batchDemo("+"id int primary key auto_increment,"+"name varchar(20)"+")");stat.addBatch("insert into batchDemo values(null,'aaaa')");stat.addBatch("insert into batchDemo values(null,'bbb')");stat.addBatch("insert into batchDemo values(null,'cc')");stat.addBatch("insert into batchDemo values(null,'d')");stat.executeBatch();}catch (Exception e) {e.printStackTrace();}finally{JDBCUtils.close(null, stat, conn);}}}
再看第二種方式:
import java.sql.Connection;import java.sql.PreparedStatement;import com.itheima.util.JDBCUtils;/* create table psbatch( id int primary key auto_increment, name varchar(30) ); *//*prparedStatement 方式實現的批處理:優點:有先行編譯機制,效率比較高.執行多條結構相同,參數不同的sql時,不需要重複寫sql的主幹缺點:只能執行主幹相同參數不同的sql,沒有辦法在一個批中加入結構不同的sql */public class PSBatch {public static void main(String[] args) {Connection conn = null;PreparedStatement ps = null;try{conn = JDBCUtils.getConn();ps = conn.prepareStatement("insert into psbatch values(null,?)");for(int i=1;i<=100000;i++){ps.setString(1, "name"+i);ps.addBatch();if(i%1000==0){ps.executeBatch();ps.clearBatch();}}ps.executeBatch();}catch (Exception e) {e.printStackTrace();}finally{JDBCUtils.close(null, ps, conn);}}}
好了,JDBC的基礎用法到此講解完畢,希望能夠協助到看到此文的人。