JDBC入門
1,什麼是JDBCjava
Java DataBase Connectivity就是Java數據庫鏈接,使用Java語言來操做數據庫。原來咱們操做數據庫是控制檯使用SQL語句來操做數據庫,JDBC是用Java語言向數據庫發送SQL語句。mysql
①導jar包:驅動;sql
②加載驅動類:class.forName("類名");數據庫
③給出url,username,password,其中url背下來;apache
④使用DriverManager類來獲得Connection對象。編程
import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class Demo1 { /** * @throws ClassNotFoundException * ①沒導入驅動包 * ②名稱、單詞等關鍵字不正確 * @throws SQLException * ①檢測3個參數:url、username、password是否正確 * ②是否開啓 */ public static void fun1() throws ClassNotFoundException, SQLException { /** * jdbc四大配置參數: * >driverClassName:com.mysql.jdbc.Driver * >url:jdbc:mysql://localhost:3306/mydb1 * >username:root * >password:無 */ Class.forName("com.mysql.jdbc.Driver");//驅動類加載【註冊驅動】 String url="jdbc:mysql://localhost:3306/mysql"; String username="root"; String password=null; Connection conn=DriverManager.getConnection(url,username,password); System.out.println(conn); } }緩存 |
2,JDBC原理服務器
早期SUN公司想編寫一套能夠鏈接全部數據庫的API,可是當他們剛剛開始的時候就發現這是個不可能完成的任務,由於各個廠商的數據庫服務器差別太大了。後來SUN開始與數據庫廠商們討論,最終得出的結論是,由SUN提供一套訪問數據庫的規範(就是一組接口),而且提供鏈接數據庫的協議標準,而後各個數據庫廠商會遵循SUN的規範提供一套訪問本身公司的數據庫服務器的API出現。SUN提供的規範命名爲JDBC,而各個廠商提供的-遵循JDBC規範的-能夠訪問本身數據庫的API被稱之爲驅動。dom
Class.forName("com.mysql.jdbc.Driver");//驅動類加載【註冊驅動】ide |
com.mysql.jdbc.Driver driver=new com.mysql.jdbc.Driver(); DriverManager.registerDriver(driver); |
加載類的時候會執行靜態代碼塊,全部的java.sql.Driver實現類,都提供了static塊,塊內的代碼就是把本身註冊到DriverManager中。
在jdbc4.0以後,每一個驅動jar包中,在META-INF/services目錄下提供有名爲java.sql.Driver的文件,文件內容就是該接口的實現類名稱!
static { try { DriverManager.registerDriver(new Driver()); } catch (SQLException var1) { throw new RuntimeException("Can't register driver!"); } } |
3,JDBC完成增刪改查
①數據庫鏈接準備
package cn.itcast.demo1; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; public class Demo2 { public static void fun1() throws ClassNotFoundException, SQLException { //①準備四大參數;②加載驅動類;③對數據庫進行增刪改查; String driverClassName="com.mysql.jdbc.Driver"; String url="jdbc:mysql://localhost:3306/mydb1"; //jdbc協議的格式!jdbc:廠商名稱:子協議(由廠商本身規定) //對於mysql而言,子協議結構://主機地址:端口號/數據庫名稱 String username="root"; String password=""; Class.forName(driverClassName); Connection conn=DriverManager.getConnection(url,username,password); //1,經過Connection對象建立Statement>Statement語句發送器,他的功能是想數據庫發送sql語句 Statement stmt=conn.createStatement(); //2,調用它的int executeUpdate(String sql),它能夠發生DML、DDL String sql="INSERT INTO emp VALUES(NULL,'王武',23,'男')"; int r=stmt.executeUpdate(sql); System.out.println(r); } } |
②數據庫DML操做
String sql="INSERT INTO emp VALUES(NULL,'王武',23,'男')"; int r=stmt.executeUpdate(sql); |
String sql="UPDATE emp"; int r=stmt.executeUpdate(sql); |
String sql="UPDATE emp SET ename='紅紅的夏天',age=18,gender='女' WHERE eid=4";//修改 int r=stmt.executeUpdate(sql); |
String sql="DELETE FROM emp";//刪除 int r=stmt.executeUpdate(sql); |
③數據庫DDL查詢操做
public ResultSet executeQuery(String sql) throws SQLException { /** * 解析ResultSet * ①把光標移動到第一行,能夠調用next()完成 */ this.resultSet=this.stmt.executeQuery(sql); while(this.resultSet.next()){ int eid=this.resultSet.getInt(1); String ename=this.resultSet.getString("ename"); int age=this.resultSet.getInt("age"); String gender=this.resultSet.getString("gender"); System.out.println(eid+","+ename+","+age+","+gender); } return this.resultSet; |
4,JDBC代碼規範化
package cn.itcast.demo1; import java.sql.*; public class Demo3 { public static void fun3() throws Exception { Connection conn=null;//定義引用對象 Statement stmt=null; ResultSet rs=null; try{ String driverClassName="com.mysql.jdbc.Driver"; // String url="jdbc:mysql://localhost:3306/mydb1"; String url="jdbc:mysql://localhost:3306/" + "mydb1?serverTimezone=GMT%2B8&useSSL=false"; //這裏須要修改一下 String username="root"; String password=""; Class.forName(driverClassName); conn=DriverManager.getConnection(url,username,password); //實例化Connection stmt=conn.createStatement();//實例化Statement String sql="SELECT * FROM emp"; rs=stmt.executeQuery(sql); while (rs.next()){ System.out.println(rs.getString(1)+","+ rs.getObject(2)+","+ rs.getString(3)+","+ rs.getObject(4)); } }catch(Exception e){//捕獲異常打印 e.printStackTrace(); } finally { if(rs!=null)rs.close();//先判斷在關閉 if(stmt!=null)stmt.close();//由子到父關閉 if(conn!=null)conn.close(); } } } |
Connection->Statement->ResultSet->
DriverManager
其實從此咱們只須要會用DriverManager的getConnection()方法便可:
①Class.forName(driverClassName);//註冊驅動
②String url="jdbc:mysql://localhost:3306/mydb1?serverTimezone=GMT%2B8&useSSL=false";
③String username="root";
④String password="";
⑤Connection conn=DriverManager.getConnection(url,username,password);
上面的代碼可能出現的兩種異常:
①ClassNotFoundException——這個異常在第一句出現,可能爲
·沒有導入mysql的jar包;
·把類名稱打錯,查看的類名不是com.mysql.jdbc.Driver
②SQLException——異常出如今第五句,出現這個異常的就是三個參數的問題
對於DriverManager.registerDriver()方法瞭解便可,由於從此註冊驅動只會使用Class.forName()而不是用前一個方法。
Connection
Connection最爲重要的方法就是獲取Statement:
·Statement stmt=conn.createStatement();
後面在學習ResultSet()方法時,還須要學習一下下面的方法【方法重載】:
·Statement stmt=conn.createStatement(int,int);//兩個參數決定可以生成什麼樣的結果集
Statement
·int executeUpdate(String sql)執行更新操做,即執行INSERT、UPDATE、DELETE語句,其實這個方法也能夠執行CREATE TABLE、ALTER TABLE,以及DROP TABLE等語句,但咱們不多會使用JDBC來執行這些語句。
·ResulSet executeQuery(String sql)執行查詢操做,返回ResultSet結果集數據。
·boolean execute()執行增、刪、改、查全部SQL語句,返回一個布爾類型,表示SQL語句是否有結果。若是execute()執行的是更新語句,那麼還要強調用int getUpdateCount()來獲取增刪改影響的行數;若是執行的是查詢語句,須要調用ResultSet getResultSet()來獲取查詢結果。
5,結果集光標與元數據
executeQuery()光標的位置:
①相對位移,下一行,不可滾動forward_only(只能rs.next());②絕對位移,第幾行。
void beforeFirst():把光標放在第一行的前面 void afterLast():把光標放在最後一行的後面 boolean first():把光標放在第一行,返回值表示光標調控是否成功 boolean last() boolean isBeforeFirst():當前光標是否在第一行的前面 boolean isAfterLast() boolean isFirst() boolean isLast() boolean previous():把光標往前挪動一行 boolean next() boolean relative(int row):相對位移,當row爲正數的時候,表示向下移動row行,爲負數時表示向上移動row行 boolean absolute(int row):絕對位移,把光標移動到指定的行上 int getRow():返回當前光標所在行 |
獲取結果集元數
rs.getMetaData()獲取元數據,返回值爲ResultSetMetaData; int getColumnCount()獲取結果數據集 String getColumnName(String colIndex)獲取指定列的列名 |
6,結果集的三大特性【滾動、敏感、可更新】
當使用Connection的createStatement時,已經肯定了Statement生成的結果集是什麼類型。
conn.createStatement();返回值->【不滾動-只有next()相對方法、不敏感、不可更新】
conn.createStatement(int,int);//方法重載 第一個參數: ResultSet.TYPE_FORWARD_ONLY:不滾動 ResultSet.TYPE_SCROLL_INSENSITIVE:滾動結果集,但結果集數據不會跟隨數據庫變化而變化 ResultSet.TYPE_SCROLL_SENSITIVE:滾動結果集,結果隨着數據庫變化而變化【實時性!理想!】 第二個參數: CONCUR_READ_ONLY:結果集是隻讀的,不能經過修改結果集而反向影響數據庫 CONCUR_UPDATE:結果集是讀寫的,能夠經過修改結果集而反向影響數據庫 |
注意:mysql數據庫默認全是可滾動的,沒有FORWARD_ONLY的
ResultSet方法:
getInt()、getString()、getDouble()、getBoolean()、getObject()
7,PreparedStatement的用法
PreparedStatement是Statement的子接口,優勢:①防SQL攻擊;②提升代碼的可讀性與可維護性;③提升效率。
·範例:一個簡單的SQL攻擊程序
package cn.itcast.demo4; import javax.swing.plaf.nimbus.State; import java.sql.*; /** * 演示:SQL攻擊 * @author Mufasa */ public class Demo4 { /** * 使用username和password查詢數據, * 查詢出結果返回true不然返回false * @param username * @param age * @return boolean */ public static boolean login(String username,String age) throws ClassNotFoundException, SQLException { String driverClassName="com.mysql.jdbc.Driver"; String url="jdbc:mysql://localhost:3306/mydb1"; String mysqlusername="root"; String mysqlpassword=""; Class.forName(driverClassName); Connection conn= DriverManager.getConnection(url,mysqlusername,mysqlpassword); Statement stmt=conn.createStatement(); String sql="SELECT * FROM emp WHERE ename='"+username+"'and age="+age; System.out.println(sql); ResultSet rs=stmt.executeQuery(sql); return rs.next(); } } |
import cn.itcast.demo4.Demo4; public class Main001 { public static void main(String[] args) throws Exception { // System.out.println(Demo4.login("紅紅的夏天",18)); System.out.println(Demo4.login("a' or 'a'='a","18 or 'a'='a'"));//SQL攻擊 } } |
SELECT * FROM emp WHERE ename='紅紅的夏天'and age=18//→正常SQL SELECT * FROM emp WHERE ename='a' or 'a'='a'and age=18 or 'a'='a' |
學習使用PreparedStatement的用法:
①給出SQL模板;
②調用Connection中的preparedStatement(String sql模板);
③調用pstmt的setXxx()系列方法爲sql模板中的?賦值;
④調用pstmt的executeUpdate()或executeQury()可是它的方法都沒有參數。
public static boolean login2(String username,String age) throws ClassNotFoundException, SQLException { String driverClassName="com.mysql.jdbc.Driver"; String url="jdbc:mysql://localhost:3306/mydb1"; String mysqlusername="root"; String mysqlpassword=""; Class.forName(driverClassName); Connection conn= DriverManager.getConnection(url,mysqlusername,mysqlpassword); String sql="SELECT * FROM emp WHERE ename=? and age=?"; //①給出SQL模板 PreparedStatement pstmt=conn.prepareStatement(sql); //②調用prepareStatement pstmt.setString(1,username);//給第一個問號賦值 pstmt.setString(2,age);//給第一個問號賦值 ResultSet rs=pstmt.executeQuery(); //調用查詢方法向數據庫發送SQL語句,返回ResultSet數據 return rs.next(); } |
8,預處理的原理
服務器的工做:
①校驗sql語句的語法【耗時】;
②編譯,與函數相似的東西;
③執行,調用函數;
PreparedStatement:
①前提,數據庫必須支持預處理【如今幾乎沒有不支持的】;
②每一個pstmt都與一個sql語句綁定在一塊兒,先把sql模板給數據庫,數據庫先進行校驗、再進行編譯、執行時只是把參數傳遞過去;
③二次執行時,無需再次校驗語法,直接執行。【相似懶漢-單例-模式】
9,mysql的預編譯功能
MySQL執行預編譯分爲三步【預處理MySQL4.0以後默認關閉】:
①執行預編譯語句,例如:prepare myfun from'SELECT * FROM emp WHERE eid=?'
②設置變量,例如:set @str='b1'
③執行語句,例如:execute myfun using @str
若是二次執行myfun,那麼就再也不須要第一步【預編譯語句】
使用Statement執行預編譯就是把上面的SQL語句執行一次。
Connection conn=JdbcUtils.getConnection(); Statement stmt=conn.createStatement(); stmt.executeUpdate("prepare myfun from'SELECT * FROM emp WHERE eid=?'"); stmt.executeUpdate("set @str='b1'"); ResulSet rs=stmt.executeQuery("execute myfun using @str"); while(rs.next()){ System.out.println(rs.getString(1)); } stmt.executeUpdate("set @str='b2'");//二次運行 ResulSet rs=stmt.executeQuery("execute myfun using @str"); while(rs.next()){ System.out.println(rs.getString(1)); } rs.close(); stmt.close(0; conn.close(); |
MySQL默認使用PreparedStatement是不能執行預處理編譯的,這須要在url中給出useServerPrepStmts=true參數;當使用不一樣的PreparedStatement對象來執行相同的SQL語句時,仍是會出現編譯兩次的現象,這是由於驅動沒有緩存編譯後的函數key,致使二次編譯,若是但願緩存編譯後函數的key,那麼就要設置cachePrepStmts參數爲true;打開批處理功能,rewriteBatchedStatements=true
例如:
①jdbc:mysql://localhost:3306/mydb1?useServerPrepStmts=true
②jdbc:mysql://localhost:3306/mydb1?useServerPrepStmts=true&cachePrepStmts=true
③jdbc:mysql://localhost:3306/mydb1?useServerPrepStmts=true&cachePrepStmts=true&rewriteBatchedStatements=true
url中的關鍵字及其值的做用
10,JdbcUtils1.0小工具
JdbcUtils-V1.0【單次初始化配置文件、數據庫】
package cn.itcast.demo1; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.util.Properties; public class JdbcUtils { private static Properties props=null; static {//靜態代碼段,對props進行初始化 try { InputStream in=JdbcUtils.class.getClassLoader().getResourceAsStream("configure//dbconfig.properties"); props=new Properties(); props.load(in); Class.forName(props.getProperty("driverClassName")); } catch (IOException e) { throw new RuntimeException(); } catch (ClassNotFoundException e) { throw new RuntimeException(); }finally { System.out.println("配置文件加載成功"); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(props.getProperty("url"), props.getProperty("mysqlusername"), props.getProperty("mysqlpassword")); } } |
11,面向接口編程
修改項目:
①把UserDao修改成接口,而後把原來的UserDao修改類名爲UserDaoImpl
②修改UserService中對UserDao的實例化:private UserDao userDao=DaoFactory.getUserDao()
③建立DaoFactory,提供getUserDao()方法
④使用配置文件來獲取實體類的名稱
DAO【Data Access Object】模式
DAO模式就是寫一個類,把訪問數據庫的代碼封裝起來。DAO在數據庫與業務【Service】邏輯之間。
·實體域,即操做的對象,例如咱們操做的表示user表,那麼就須要先寫一個User類;
·DAO模式須要先提供一個DAO接口;
·而後提供一個DAO接口的實現類;
·再編寫一個DAO工廠,Service經過工廠來獲取DAO實現。
12,修改day14的登陸案例
開閉原則:容許加,不容許改->須要從新測試
package cn.itcast.demo1; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; public class JdbcUserDaoImpl implements UserDao { Connection conn=null; PreparedStatement pstmt=null; @Override public void addUser() throws SQLException { try{ conn=JdbcUtils.getConnection(); String sql="INSERT INTO emp VALUES(?,?,?,?)"; pstmt=conn.prepareStatement(sql); pstmt.setString(1,from.getEid()); pstmt.setString(2,from.getEname()); pstmt.setString(3,from.getEage()); pstmt.setString(4,from.getEgender()); pstmt.executeUpdate(); }catch (Exception e){ throw new RuntimeException(e); }finally { if(pstmt!=null) pstmt.close(); if(conn!=null) conn.close(); } } } |
13,Util包下的Date與sql包下的時間類型轉換
java.sql的東西不能出如今Dao之外的地方,不然就干擾了Service或者Server類
數據庫類型與java中類型的對應關係:
DATE->java.sql.Date
TIME->java.sql.Time
TIMESTAMP->java.sql.Timestamp
領域對象【domain】中的全部屬性不能出現java.sql包下的東西!即不能使用java.sql.Date
·ResultSet#getDate()返回的是java.sql.Date()
·PreparedStatement#setDate(int,Date),其中第二個參數也是java.sql.Date類型
時間相似的轉換:【sql是util的子類】
·java.util.Date->java.sql.Date、Time、Timestamp
①先把util的Date轉換成毫秒值;
②使用毫秒值建立sql的Date、Time、Timestamp
java.util.Date date=new java.util.Date(); long l=date.getTime(); java.sql.Date sqlDate=new java.sql.Date(l); |
·java.sql.Date、Time、Timestamp->java.util.Date
java.util.Date date=java.sql.Date() |
14,大數據
目標:把MP3保存到數據庫之中!
USE mydb1; CREATE TABLE tab_bin( id INT PRIMARY KEY AUTO_INCREMENT, filename VARCHAR(100), DATA MEDIUMBLOB ); |
import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.util.Properties; public class JdbcUtils { private static Properties props=null; static {//靜態代碼段,對props進行初始化 try { InputStream in=JdbcUtils.class.getClassLoader().getResourceAsStream("configure/dbconfig.properties"); props=new Properties(); props.load(in); System.out.println(props.getProperty("driverClassName")); Class.forName(props.getProperty("driverClassName")); } catch (IOException e) { throw new RuntimeException(); } catch (ClassNotFoundException e) { throw new RuntimeException(); }finally { System.out.println("配置文件加載成功"); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(props.getProperty("url"), props.getProperty("mysqlusername"), props.getProperty("mysqlpassword")); } } |
import org.apache.commons.io.IOUtils; import javax.sql.rowset.serial.SerialBlob; import java.io.*; import java.sql.*; //import static com.sun.tools.doclint.Entity.copy;
public class Wrmp3 { public static void write_mp3() throws SQLException, IOException { Connection conn=JdbcUtils.getConnection();//①獲得Connection String sql="INSERT INTO tab_bin VALUES(?,?,?)";//②給出sql模板 PreparedStatement pstmt=conn.prepareStatement(sql);//建立pstmt pstmt.setInt(1,1); pstmt.setString(2,"RockDrums.mp3"); /** * 有文件須要獲得Blob * ①先把文件編程byte[]類型 * ②再使用byte[]建立Blob */ String mp3Path="E:\\RockDrums.mp3"; byte[] bytes=getMp3Content(mp3Path); Blob blob=new SerialBlob(bytes); pstmt.setBlob(3,blob); pstmt.executeUpdate(); } public static void read_mp3() throws SQLException, IOException { Connection conn=JdbcUtils.getConnection(); String sql="SELECT * FROM tab_bin"; PreparedStatement pstmt=conn.prepareStatement(sql); ResultSet rs=pstmt.executeQuery(); if(rs.next()){ Blob blob=rs.getBlob("data"); InputStream in = blob.getBinaryStream(); OutputStream out=new FileOutputStream("E:\\RockDrums_複寫版.mp3"); IOUtils.copy(in,out); } } public static byte[] getMp3Content(String filePath) throws IOException { File file = new File(filePath); long fileSize = file.length(); if (fileSize > Integer.MAX_VALUE) { System.out.println("file too big..."); return null; }else { System.out.println(fileSize); } FileInputStream fi = new FileInputStream(file); byte[] buffer = new byte[(int) fileSize]; int offset = 0; int numRead = 0; while (offset < buffer.length && (numRead = fi.read(buffer, offset, buffer.length - offset)) >= 0) { offset += numRead; } // 確保全部數據均被讀取 if (offset != buffer.length) { throw new IOException("Could not completely read file " + file.getName()); } fi.close(); return buffer; } } |
import java.io.IOException; import java.sql.Connection; import java.sql.SQLException; public class Main { public static void main(String[] args) throws SQLException, IOException { // Wrmp3.write_mp3(); Wrmp3.read_mp3(); } } |
15,批處理
批處理就是一批一批的處理,而不是一個一個的處理。
import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; public class Demo5 { public static void fun5() throws SQLException { Connection conn=JdbcUtils.getConnection(); String sql="INSERT INTO batch_emp VALUES(?,?,?,?)"; PreparedStatement pstmt=conn.prepareStatement(sql); /* 瘋狂添加數據*/ for(int i=0;i<10000;i++){ pstmt.setInt(1,i+1); pstmt.setString(2,"emp_"+i); pstmt.setInt(3,18); pstmt.setString(4,i%2==0?"男":"女"); pstmt.addBatch();//添加批 } long start = System.currentTimeMillis(); pstmt.executeBatch();//執行批 long end = System.currentTimeMillis(); System.out.println(end-start); } } |
driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/mydb1?useServerPrepStmts=true&cachePrepStmts=true&rewriteBatchedStatements=true mysqlusername=root mysqlpassword= |