數據庫及MYSQL基礎(3)-JDBC

教學視頻連接:https://edu.aliyun.com/course/1694?spm=5176.11400004.0.0.29254768sg2H5P
程序文件連接:https://pan.baidu.com/s/1PyJEgHl8y8BaP-VPcJpb8g 提取碼:oe97

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

wps4

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;

wps5

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

wps6

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=

相關文章
相關標籤/搜索