天天看點

Java進階學習第十七天——JDBC入門學習

文檔版本 開發工具 測試平台 工程名字 日期 作者 備注
V1.0 2016.05.11 lutianfei none

JDBC

JDBC介紹

  • JDBC是什麼?
    • JDBC(Java Data Base Connectivity,java資料庫連接配接)
    • SUN公司為了簡化、統一對資料庫的操作,定義了一套Java操作資料庫的規範,稱之為JDBC。
    • 簡單說,就是可以直接通過java語言去操作資料庫。
    • jdbc是一套标準,它是由一些接口與類組成的。
Java進階學習第十七天——JDBC入門學習

組成JDBC的類和接口

  • java.sql
    • 類:DriverManger
    • 接口

      Connection

      Statement

      ResultSet

      PreparedStatement

      CallableStatement

      (它是用于調用存儲過程)
  • javax.sql
    • 接口 DataSource(資料源)
Java進階學習第十七天——JDBC入門學習
  • 什麼是驅動?
    • 兩個裝置要進行通信,滿足一定通信資料格式,資料格式由裝置提供商規定,裝置提供商為裝置提供驅動軟體,通過軟體可以與該裝置進行通信。

JDBC入門

第一個JDBC程式

  • 程式設計從user表中讀取資料,并列印在指令行視窗中。
    • 一、搭建實驗環境 :
      • 1、在mysql中建立一個庫,并建立user表和插入表的資料。
      • 2、建立一個Java工程,并導入資料驅動。
    • 二、編寫程式,在程式中加載資料庫驅動
      • DriverManager. registerDriver(Driver driver)
    • 三、建立連接配接(Connection)
      • Connection conn = DriverManager.getConnection(url,user,pass);
    • 四、建立用于向資料庫發送SQL的Statement對象,并發送sql
      • Statement st = conn.createStatement();
      • ResultSet rs = st.executeQuery(sql);
    • 五、從代表結果集的ResultSet中取出資料,列印到指令行視窗
    • 六、斷開與資料庫的連接配接,并釋放相關資源
Java進階學習第十七天——JDBC入門學習
create table user(
  id int primary key auto_increment,
  username varchar() unique not null,
  password varchar() not null,
  email varchar() not null
);

INSERT INTO USER VALUES(NULL,'tom','123','[email protected]');
INSERT INTO USER VALUES(NULL,'fox','123','[email protected]');
           
  • 1.加載驅動
    • 将驅動jar包複制到lib下.
  • 2.建立一個JdbcDemo1類
public static void main(String[] args) throws SQLException {

    // 注冊驅動
    // DriverManager.registerDriver(new Driver()); //加載了兩個驅動
    Class.forName("com.mysql.jdbc.Driver"); // 加載mysql驅動

    // 擷取連接配接對象
    Connection con = DriverManager.getConnection(
            "jdbc:mysql://localhost:3306/day17", "root", "abc");

    // 通過連接配接對象擷取操作sql語句Statement
    Statement st = con.createStatement();

    // 操作sql語句
    String sql = "select * from user";

    // 操作sql語句(select語句),會得到一個ResultSet結果集
    ResultSet rs = st.executeQuery(sql);

    // 周遊結果集
    // boolean flag = rs.next(); // 向下移動,傳回值為true,代表有下一條記錄.
    // int id = rs.getInt("id");
    // String username=rs.getString("username");
    // System.out.println(id);
    // System.out.println(username);

    while(rs.next()){
        int id=rs.getInt("id");
        String username=rs.getString("username");
        String password=rs.getString("password");
        String email=rs.getString("email");

        System.out.println(id+"  "+username+"  "+password+"  "+email);
    }

    //釋放資源
    rs.close();
    st.close();
    con.close();
}
           

JDBC操作詳解

1.注冊驅動

  • JDBC程式中的

    DriverManager

    java.sql

    包下的一個驅動管理的工具類,可以了解成是一個容器(Vector),可以裝入很多資料庫驅動,并建立與資料庫的連結,這個API的常用方法:
    • DriverManager.registerDriver(new Driver())
    • DriverManager.getConnection(url, user, password),
  • registDriver方法

    分析
    • public static synchronized void registerDriver(java.sql.Driver driver)
      • 參數:java.sql.Driver
      • 我們傳遞的是 : com.mysql.jdbc.Driver;
    • 在com.mysql.jdbc.Driver類中有一段靜态代碼塊:
static {
    try {
        java.sql.DriverManager.registerDriver(new Driver());
        } catch (SQLException E) {
        throw new RuntimeException("Can't register driver!");
    }
}
           
  • 上述代碼的問題:
    • 1.在驅動管理器中會裝入兩個mysql驅動.
      • 解決方案:使用反射

        Class.forName("com.mysql.jdbc.Driver");

      • 分析:使用反射的方式來加載驅動有什麼好處?
        • 一、檢視Driver的源代碼可以看到,如果采用此種方式,會導緻驅動程式注冊兩次,也就是在記憶體中會有兩個Driver對象。
        • 二、程式依賴mysql的api,脫離mysql的jar包,程式将無法編譯,将來程式切換底層資料庫将會非常麻煩。
    • 2.可以通過DriverManager來擷取連接配接對象
      • Connection con=DriverManager.getConection(String url,String user,String password);

      • url作用:就是用于确定使用哪一個驅動.
        • mysql url:

          jdbc: mysql ://localhsot:3306/資料庫名

        • oralce url:

          jdbc : oracle :thin :@ localhost :1521 :sid

  • DriverManager作用總結:
    • 1.注冊驅動
    • 2.擷取連接配接Connection
資料庫URL
  • URL用于辨別資料庫的位置,程式員通過URL位址告訴JDBC程式連接配接哪個資料庫,URL的寫法為:
    • jdbc : mysql : // localhost :3306/test ?key=value

  • url格式
    • 主協定 子協定 主機 端口 資料庫
    • jdbc : mysql ://localhost:3306/day17

  • mysql的url可以簡寫
    • 前提:主機是:localhost 端口是 :3306
    • jdbc : mysql : ///day17

  • 在url後面可以帶參數
    • eg: useUnicode=true&characterEncoding=UTF-8

2.Connection詳解

  • java.sql.Connection

    ,它代表的是一個連接配接對象。簡單說,就是我們程式與資料庫連接配接。
  • Connection作用:
    • 1.可以通過Connection擷取操作SQL的Statement對象。
      • Statement createStatement() throws SQLException
      • 示例:
        • Statement st=con.createStatement();
    • 2.操作事務
      • setAutoCommit(boolean flag);開啟事務,設定事務是否自動送出。
      • rollback();事務復原,在此連結上復原事務。
      • commit();事務送出,在連結上送出事務。 —與事務相關!!
    • 了解:
      • 1.可以擷取執行預處理的

        PreparedStatement對象

        .建立向資料庫發送預編譯sql的PrepareSatement對象
        • PreparedStatement prepareStatement(String sql) throws SQLException
      • 2.可以擷取執行存儲過程的

        CallableStatement

        ,建立執行存儲過程的callableStatement對象。
        • CallableStatement prepareCall(String sql) throws SQLException

3.Statement詳解

  • java.sql.Statement

    用于向資料庫發送SQL語句,執行sql語句。
Statement作用
  • 1.執行SQL
    • DML :

      insert

      update

      delete

      • int executeUpdate(String sql) :用于向資料庫發送insert、update或delete語句
        • 利用傳回值判斷非0來确定sql語句是否執行成功。
    • DQL :

      select

      • ResultSet executeQuery(String sql) : 用于向資料發送查詢語句。
    • execute(String sql):用于向資料庫發送任意SQL語句
  • 2.批處理操作
    • addBatch(String sql); 将SQL語句添加到批處理
    • executeBatch(); 向資料庫發送一批SQl語句執行。
    • clearBatch(); 清空批處理。

4.ResultSet詳解

  • java.sql.ResultSet

    它是用于封裝select語句執行後查詢的結果。Resultset封裝執行結果時,采用的類似于表格的方式。ResultSet 對象維護了一個指向表格資料行的

    遊标cursor

    ,初始的時候,遊标在第一行之前,調用

    ResultSet.next() 方法

    ,可以使遊标指向具體的資料行,進而調用方法擷取該行的資料。
ResultSet常用API
  • 1.next()方法
    • public boolean next();
    • 用于判斷是否有下一條記錄。如果有傳回true,并且讓遊标向下移動一行。
    • 如果沒有傳回false.
  • 2.可以通過ResultSet提供的getXxx()方法來擷取目前遊标指向的這條記錄中的列資料。
    • 常用:
      • getInt()
      • getString(int index)
      • getString(String columnName):也可以獲得int,Data等類型
      • getDate()
      • getDouble()
    • 參數有兩種
      • 1.getInt(int columnIndex);
      • 2.getInt(String columnName);
  • 如果列的類型不知道,可以通過下面的方法來操作
    • getObject(int columnIndex);
    • getObject(String columnName);
  • 常用資料類型轉換表
    Java進階學習第十七天——JDBC入門學習
Java進階學習第十七天——JDBC入門學習

釋放資源

  • JDBC程式運作完後,切記要釋放程式在運作過程中,建立的那些與資料庫進行互動的對象,這些對象通常是ResultSet, Statement和Connection對象。
  • 特别是

    Connection對象

    ,它是非常稀有的資源,用完後必須馬上釋放,如果Connection不能及時、正确的關閉,極易導緻系統當機。
  • Connection的使用原則是盡量晚建立,盡量早的釋放。
  • 為確定資源釋放代碼能運作,資源釋放代碼也一定要放在

    finally

    語句中。
  • 完整版JDBC示例代碼:
public static void main(String[] args) {
        Connection con = null;
        Statement st = null;
        ResultSet rs = null;
        try {
            // 1.注冊驅動

            Class.forName("com.mysql.jdbc.Driver");

            // 2.擷取連接配接
            con = DriverManager.getConnection("jdbc:mysql:///day17", "root",
                    "abc");

            // 3.擷取操作sql語句對象Statement
            st = con.createStatement();

            // 4.執行sql
            rs = st.executeQuery("select * from user");

            // 5.周遊結果集
            while (rs.next()) {
                int id = rs.getInt("id");
                String username = rs.getString("username");
                String password = rs.getString("password");
                String email = rs.getString("email");

                System.out.println(id + "  " + username + "  " + password
                        + "  " + email);
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 6.釋放資源

            try {
                if (rs != null) {
                    rs.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }

            try {
                if (st != null)
                    st.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            try {
                if (con != null)
                    con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}
           

使用JDBC對資料庫進行CRUD

  • Jdbc中的statement對象用于向資料庫發送SQL語句,想完成對資料庫的增删改查,隻需要通過這個對象向資料庫發送增删改查語句即可。
  • Statement對象的

    executeUpdate方法

    ,用于向資料庫發送增、删、改的sql語句,executeUpdate執行完後,将會傳回一個整數(即增删改語句導緻了資料庫幾行資料發生了變化)。
  • Statement.executeQuery方法

    用于向資料庫發送查詢語句,executeQuery方法傳回代表查詢結果的ResultSet對象。
  • 1.查詢
    • 1.查詢全部
    • 2.條件查詢—根據id
  • 2.修改
  • 3.删除
  • 4.添加
  • 練習:編寫程式對User表進行增删改查操作。
  • 練習:編寫工具類簡化CRUD操作。(異常暫不處理)

JdbcUtils工具類

  • 隻抽取到Connection
//jdbc.properties
driverClass=com.mysql.jdbc.Driver
url=jdbc:mysql:///day17
username=root
password=abc




public class JdbcUtils {


    private static final String DRIVERCLASS;
    private static final String URL;
    private static final String USERNAME;
    private static final String PASSWORD;

    static {
        DRIVERCLASS = ResourceBundle.getBundle("jdbc").getString("driverClass");
        URL = ResourceBundle.getBundle("jdbc").getString("url");
        USERNAME = ResourceBundle.getBundle("jdbc").getString("username");
        PASSWORD = ResourceBundle.getBundle("jdbc").getString("password");
    }

    static {
        try {
            // 将加載驅動操作,放置在靜态代碼塊中.這樣就保證了隻加載一次.
            Class.forName(DRIVERCLASS);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static Connection getConnection() throws SQLException {

        // 2.擷取連接配接
        Connection con = DriverManager.getConnection(URL, USERNAME, PASSWORD);

        return con;
    }

    //關閉操作
    public static void closeConnection(Connection con) throws SQLException{
        if(con!=null){
            con.close();
        }
    }
    public static void closeStatement(Statement st) throws SQLException{
        if(st!=null){
            st.close();
        }
    }
    public static void closeResultSet(ResultSet rs) throws SQLException{
        if(rs!=null){
            rs.close();
        }
    }
}
           
  • JDBC CURD
//jdbc的crud操作
public class JdbcDemo6 {

    @Test
    public void findByIdTest() {
        // 1.定義sql
        String sql = "select * from user where id=1";

        Connection con = null;
        Statement st = null;
        ResultSet rs = null;
        try {
            // 1.注冊驅動

            Class.forName("com.mysql.jdbc.Driver");

            // 2.擷取連接配接
            con = DriverManager.getConnection("jdbc:mysql:///day17", "root",
                    "abc");

            // 3.擷取操作sql語句對象Statement
            st = con.createStatement();

            // 4.執行sql
            rs = st.executeQuery(sql);

            // 5.周遊結果集
            while (rs.next()) {
                int id = rs.getInt("id");
                String username = rs.getString("username");
                String password = rs.getString("password");
                String email = rs.getString("email");

                System.out.println(id + "  " + username + "  " + password
                        + "  " + email);
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 6.釋放資源

            try {
                if (rs != null) {
                    rs.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }

            try {
                if (st != null)
                    st.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            try {
                if (con != null)
                    con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    // 添加操作
    @Test
    public void addTest() {
        // 定義sql
        String sql = "insert into user values(null,'張三','123','[email protected]')";
        Connection con = null;
        Statement st = null;
        ResultSet rs = null;
        try {
            // 1.注冊驅動

            Class.forName("com.mysql.jdbc.Driver");

            // 2.擷取連接配接
            con = DriverManager.getConnection("jdbc:mysql:///day17", "root",
                    "abc");

            // 3.擷取操作sql語句對象Statement
            st = con.createStatement();

            // 4.執行sql
            int row = st.executeUpdate(sql);

            if (row != ) {
                System.out.println("添加成功");
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 6.釋放資源

            try {
                if (rs != null) {
                    rs.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }

            try {
                if (st != null)
                    st.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            try {
                if (con != null)
                    con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    // update操作
    @Test
    public void updateTest() {
        // 将id=3的人的password修改為456
        String password = "456";
        String sql = "update user set password='" + password + "' where id=3";

        // 1.得到Connection
        Connection con = null;
        Statement st = null;
        try {
            con = JdbcUtils1.getConnection();

            // 3.擷取操作sql語句對象Statement
            st = con.createStatement();

            // 4.執行sql
            int row = st.executeUpdate(sql);

            if (row != ) {
                System.out.println("修改成功");
            }

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 關閉資源
            try {
                if (st != null)
                    st.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            try {
                if (con != null)
                    con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

    }

    // delete測試
    @Test
    public void deleteTest() {
        // 将id=3的人删除

        String sql = "delete from user where id=2";

        // 1.得到Connection
        Connection con = null;
        Statement st = null;
        try {
            con = JdbcUtils.getConnection();

            // 3.擷取操作sql語句對象Statement
            st = con.createStatement();

            // 4.執行sql
            int row = st.executeUpdate(sql);

            if (row != ) {
                System.out.println("删除成功");
            }

        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 關閉資源

            try {
                JdbcUtils.closeStatement(st);
                JdbcUtils.closeConnection(con);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

    }
}
           

ResultSet 滾動結果集

  • 預設得到的ResultSet它隻能向下周遊(next()),對于ResultSet它可以設定成是滾動的,可以向上周遊,或者直接定位到一個指定的實體行号。
  • 設定滾動結果集的方法
    • 在建立Statement對象時,不使用

      createStatement()

      ;而使用帶參數的

      createStatement(int,int)

Statement createStatement(int resultSetType,
                          int resultSetConcurrency)
                          throws SQLException
、
Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);
           
  • 第一個參數值,

    resultSetType

    • ResultSet.TYPE_FORWARD_ONLY 該常量訓示光标隻能向前移動的 ResultSet 對象的類型。
    • ResultSet.TYPE_SCROLL_INSENSITIVE 該常量訓示可滾動但通常不受 ResultSet 底層資料更改影響的 ResultSet 對象的類型。
    • ResultSet.TYPE_SCROLL_SENSITIVE 該常量訓示可滾動并且通常受 ResultSet 底層資料更改影響的 ResultSet 對象的類型。
  • 第二個參數值,

    resultSetConcurrency

    • ResultSet.CONCUR_READ_ONLY 該常量訓示不可以更新的 ResultSet 對象的并發模式。
    • ResultSet.CONCUR_UPDATABLE 該常量訓示可以更新的 ResultSet 對象的并發模式。
  • 以上五個值,可以有三種搭配方式
    • ResultSet.TYPE_FORWARD_ONLY ResultSet.CONCUR_READ_ONLY 預設,不受底層影響
    • ResultSet.TYPE_SCROLL_INSENSITIVE ResultSet.CONCUR_READ_ONLY
    • ResultSet.TYPE_SCROLL_SENSITIVE ResultSet.CONCUR_UPDATABLE 滾動的,可以并發更新的
  • 常用API
    • next():移動到下一行
    • previous():移動到前一行
    • absolute(int row):移動到指定行
    • beforeFirst():移動resultSet的最前面
    • afterLast() :移動到resultSet的最後面
    • updateRow() :更新行資料

DAO模式——JavaEE模式

  • DAO模式

    (Data Access Object 資料通路對象):在持久層通過DAO将資料源操作完全封裝起來,業務層通過操作Java對象,完成對資料源操作
  • 業務層無需知道資料源底層實作 ,通過java對象操作資料源

DAO模式結構

  • 1、DataSource資料源(MySQL資料庫)
  • 2、Business Object 業務層代碼,調用DAO完成 對資料源操作 (代表資料的使用者)
  • 3、DataAccessObject 資料通路對象,持久層DAO程式,封裝對資料源增删改查,提供方法參數都是Java對象
  • 4、TransferObject 傳輸對象(值對象) 業務層通過向資料層傳遞 TO對象,完成對資料源的增删改查(表示資料的Java Bean)
Java進階學習第十七天——JDBC入門學習

使用dao模式完成登入操作

  • 需求:
    • 1、把檔案換成資料庫
    • 2、使用DAO模式
    • 3、防範sql注入攻擊
  • 1.web層
    • login.jsp LoginServlet User
  • 2.service層
    • UserService(實際應是接口)
  • 3.dao層
    • UserDao(實際應是接口)
Java進階學習第十七天——JDBC入門學習
  • 具體代碼見工程

    day17_2

  • 使用者注冊流程
    Java進階學習第十七天——JDBC入門學習

sql注入

  • 由于沒有對使用者輸入進行充分檢查,而SQL又是拼接而成,在使用者輸入參數時,在參數中添加一些SQL 關鍵字,達到改變SQL運作結果的目的,也可以完成惡意攻擊。
  • 1、statement存在sql注入攻擊問題
    • 例如登陸使用者名采用

      xxx’ or ‘1’=‘1

    • 使用mysql注釋
  • 2、對于防範 SQL 注入,可以采用

    PreparedStatement

    取代

    Statement

    • 它是一個預處理的Statement,它是java.sql.Statement接口的一個子接口。
    • PreparedStatement是Statement的子接口,它的執行個體對象可以通過調

      Connection.preparedStatement(sql)

      方法獲得
    • Statement會使資料庫頻繁編譯SQL,可能造成資料庫緩沖區溢出。PreparedStatement 可對SQL進行預編譯,進而提高資料庫的執行效率。
    • PreperedStatement對于sql中的參數,允許使用占位符的形式進行替換,簡化sql語句的編寫。
  • PreparedStatement使用總結
    • 1.在sql語句中,使用

      ?

      占位
      • String sql=”select * from user where username=? and password=?”;
    • 2.得到PreparedStatement對象
      • PreparedStatement pst=con.prepareStatement(String sql);
    • 3.對占位符指派
      • pst.setXxx(int index,Xxx obj);
      • 例如:
        • setInt()
        • setString();
        • 參數index,代表的是”

          ?

          “的序号.注意:從1開始。
    • 4.執行sql
      • DML: pst.executeUpdate();
      • DQL: pst.executeQuery();
      • 注意:這兩方法無參數
  • 關于PreparedStatement優點:
    • 1.解決sql注入(具有預處理功能)
    • 2.不需要再拼sql語句。

jdbc處理大資料

  • 在實際開發中,程式需要把大文本

    Text

    或二進制資料

    Blob

    儲存到資料庫。
    • Text

      是mysql叫法,Oracle中叫

      Clob

  • 大資料

    也稱之為

    LOB

    (Large Objects),LOB又分為:
    • clob用于存儲大文本。Text
    • blob用于存儲二進制資料,例如圖像、聲音、二進制文等。
  • Text和blob分别又分為:
    • Text(clob)
      • TINYTEXT(255B)、TEXT(64kb)、MEDIUMTEXT(16M)和LONGTEXT(4G)
    • blob
      • TINYBLOB(255B)、BLOB(64kb)、MEDIUMBLOB(16M)和LONGBLOB(4G)
  • 對于大資料操作,我們一般隻有兩種: insert select
  • 示範1: 大二進制操作
create table myblob(
id int primary key auto_increment,
content longblob
)
           
public class MyBlobTest {

    // 添加
    @Test
    public void save() throws SQLException, IOException {
        String sql = "insert into myblob values(null,?)";

        // 1.擷取Connection
        Connection con = JdbcUtils.getConnection();
        // 2.擷取PreparedStatement
        PreparedStatement pst = con.prepareStatement(sql);
        // 3.插入值
        File file = new File("D:\\java1110\\day17-jdbc\\視訊\\3.jdbc快速入門.avi");
        FileInputStream fis = new FileInputStream(file);
        pst.setBinaryStream(, fis, (int) (file.length())); //MySQL驅動隻支援最後一個參數為int類型的方法
        int row = pst.executeUpdate();

        if (row != ) {
            System.out.println("插入成功");
        }

        // 4.釋放資源
        fis.close();
        pst.close();
        con.close();

    }

    // 擷取
    @Test
    public void get() throws SQLException, IOException {
        String sql = "select * from myblob where id=?";

        // 1.擷取Connection
        Connection con = JdbcUtils.getConnection();
        // 2.擷取PreparedStatement
        PreparedStatement pst = con.prepareStatement(sql);
        pst.setInt(, );
        // 3.得到結果集
        ResultSet rs = pst.executeQuery();

        // 4.周遊結果集
        if (rs.next()) {
            // System.out.println(rs.getInt("id"));

            InputStream is = rs.getBinaryStream("content");// 得到的這個輸入流它的源可以了解成就是資料庫中的大二進制資訊

            FileOutputStream fos = new FileOutputStream("d:/a.avi");

            int len = -;
            byte[] b = new byte[ * ];

            while ((len = is.read(b)) != -) {
                fos.write(b, , len);
                fos.flush();
            }
            fos.close();
            is.close();

        }

        // 5.關閉
        rs.close();
        pst.close();
        con.close();
    }
}
           
  • 向表中插入資料可能出現的問題
  • 問題1:java.lang.AbstractMethodError: com.mysql.jdbc.PreparedStatement.setBinaryStream(ILjava/io/InputStream;)
  • 原因:mysql驅動不支援setBinaryStream(int,InputStream);
  • 修改成 pst.setBinaryStream(1, fis,file.length());
  • 原因:因為mysql驅動不支 setBinaryStream(int,InputStream,long);
  • 解決: mysql驅動支援setBinaryStream(int,InputStream,int);
  • 注意:如果檔案比較大,那麼需要在my.ini檔案中配置
    • max_allowed_packet=64M
  • 總結:
    • 存:pst.setBinaryStream(1, fis, (int) (file.length()));
    • 取:InputStream is = rs.getBinaryStream(“content”);

使用JDBC處理大文本

  • 對于MySQL中的Text類型,可調用如下方法

    設定

PreparedStatement.setCharacterStream(index, reader, length);
//注意length長度須設定,并且設定為int型
//當包過大時修改配置:[mysqld] max_allowed_packet=M
           
  • 對MySQL中的Text類型,可調用如下方法

    擷取

reader = resultSet. getCharacterStream(i);
等價于
reader = resultSet.getClob(i).getCharacterStream();
           
  • 示範:存儲大文本
create table mytext(
id int primary key auto_increment,
content longtext
)

//存儲
File file = new File("D:\\java1110\\workspace\\day17_3\\a.txt");
FileReader fr = new FileReader(file);
pst.setCharacterStream(, fr, (int) (file.length()));

//擷取:
Reader r = rs.getCharacterStream("content");
           

使用JDBC處理二進制資料

  • 對于MySQL中的BLOB類型,可調用如下方法

    設定

    • PreparedStatement. setBinaryStream(i, inputStream, length);

  • 對MySQL中的BLOB類型,可調用如下方法

    擷取

    • InputStream in = resultSet.getBinaryStream(i);

    • InputStream in = resultSet.getBlob(i).getBinaryStream();

public class MyTextTest {

    // 存儲
    @Test
    public void save() throws SQLException, FileNotFoundException {
        String sql = "insert into mytext values(null,?)";

        // 1.擷取Connection
        Connection con = JdbcUtils.getConnection();
        // 2.擷取PreparedStatement
        PreparedStatement pst = con.prepareStatement(sql);
        // 3.插入值
        File file = new File("D:\\java1110\\workspace\\day17_3\\a.txt");
        FileReader fr = new FileReader(file);
        pst.setCharacterStream(, fr, (int) (file.length()));

        pst.executeUpdate();

        // 4.釋放資源
        pst.close();
        con.close();
    }

    // 擷取
    @Test
    public void get() throws SQLException, IOException {
        String sql = "select * from mytext where id=?";

        // 1.擷取Connection
        Connection con = JdbcUtils.getConnection();
        // 2.擷取PreparedStatement
        PreparedStatement pst = con.prepareStatement(sql);
        pst.setInt(, );
        // 3.得到結果集
        ResultSet rs = pst.executeQuery();

        // 4.周遊結果集
        if (rs.next()) {
            Reader r = rs.getCharacterStream("content");

            FileWriter fw = new FileWriter("d:/筆記.txt");

            int len = -;
            char[] ch = new char[ * ];

            while ((len = r.read(ch)) != -) {
                fw.write(ch, , len);
                fw.flush();
            }
            fw.close();
            r.close();

        }

        pst.close();
        con.close();
    }
}
           

JDBC批處理

  • 業務場景:當需要向資料庫發送一批SQL語句執行時,應避免向資料庫一條條的發送執行,而應采用JDBC的批處理機制,以提升執行效率。
  • 實作批處理有兩種方式,第一種方式:
    • Statement.addBatch(sql)
    • executeBatch()方法:執行批處理指令
    • clearBatch()方法:清除批處理指令
  • 采用Statement.addBatch(sql)方式實作批處理:
    • 優點:可以向資料庫發送多條不同的SQL語句。
    • 缺點:
      • SQL語句沒有預編譯。
      • 當向資料庫發送多條語句相同,但僅參數不同的SQL語句時,需重複寫上很多條SQL語句。例如:
        • Insert into user(name,password) values(‘aa’,’111’);
        • Insert into user(name,password) values(‘bb’,’222’);
        • Insert into user(name,password) values(‘cc’,’333’);
        • Insert into user(name,password) values(‘dd’,’444’);
  • 實作批處理的第二種方式
    • PreparedStatement.addBatch()
      • addBatch();
      • executeBatch();
      • clearBatch();
  • 采用PreparedStatement.addBatch()實作批處理
    • 優點:發送的是預編譯後的SQL語句,執行效率高。
    • 缺點:隻能應用在SQL語句相同,但參數不同的批進行中。是以此種形式的批處理經常用于在同一個表中批量插入資料,或批量更新表的資料。
  • 兩個對象執行批處理差別?
    • 1.Statement它更适合執行不同sql的批處理。它沒有提供預處理功能,性能比較低。
    • 2.PreparedStatement它适合執行相同sql的批處理,它提供了預處理功能,性能比較高。
  • eg:第一種方式
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
conn = JdbcUtil.getConnection();
String sql1 = "insert into person(name,password,email,birthday) 
    values('kkk','123','[email protected]','1978-08-08')";
String sql2 = "update user set password='123456' where id=";
st = conn.createStatement();
st.addBatch(sql1);  //把SQL語句加入到批指令中
st.addBatch(sql2);  //把SQL語句加入到批指令中
st.executeBatch();
} finally{
    JdbcUtil.free(conn, st, rs);
}
           
  • eg:第二種方式
conn = JdbcUtil.getConnection();
String sql = "insert into person(name,password,email,birthday) values(?,?,?,?)";
st = conn.prepareStatement(sql);
for(int i=;i<50000;i++){
st.setString(, "aaa" + i);
st.setString(, "123" + i);
st.setString(, "aaa" + i + "@sina.com");
st.setDate(,new Date(, , ));

st.addBatch(); 
if(i%==){
st.executeBatch();
st.clearBatch();
}
}
st.executeBatch();
           
  • 注意;mysql預設情況下,批進行中的預處理功能沒有開啟,需要開啟
    • 1.在 url下添加參數
      • url=jdbc :mysql :///day17?

        useServerPrepStmts=true&cachePrepStmts=true&rewriteBatchedStatements=true

    • 2.注意驅動版本
      • Mysql驅動要使用mysql-connector-java-5.1.13以上
  • 作業:
  • 1.注冊+登入案例
  • 采用dao模式 使用PreparedStatement操作
  • 2.使用PreparedStatement完成CRUD
  • 客戶資訊表DAO編寫
    • 創立如下資料庫表customer,并編寫DAO進行crud操作
Java進階學習第十七天——JDBC入門學習