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

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

JDBC

JDBC介紹

  • JDBC是什麼?
    • JDBC(Java Data Base Connectivity,java數據庫連接)
    • SUN公司爲了簡化、統一對數據庫的操作,定義了一套Java操作數據庫的規範,稱之爲JDBC。
    • 簡單說,就是可以直接通過java語言去操作數據庫。
    • jdbc是一套標準,它是由一些接口與類組成的。


組成JDBC的類和接口

  • java.sql
    • 類:DriverManger
    • 接口 Connection Statement ResultSet PreparedStatement CallableStatement(它是用於調用存儲過程)
  • javax.sql
    • 接口 DataSource(數據源)

  • 什麼是驅動?
    • 兩個設備要進行通信,滿足一定通信數據格式,數據格式由設備提供商規定,設備提供商爲設備提供驅動軟件,通過軟件可以與該設備進行通信。


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中取出數據,打印到命令行窗口
    • 六、斷開與數據庫的連接,並釋放相關資源

create table user(
  id int primary key auto_increment,
  username varchar(20) unique not null,
  password varchar(20) not null,
  email varchar(40) 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 {

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

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

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

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

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

    // 5.遍歷結果集
    // 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);
    }

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


JDBC操作詳解

1.註冊驅動

  • JDBC程序中的DriverManagerjava.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獲取操作SQLStatement對象
      • 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);
  • 常用數據類型轉換表


釋放資源

  • 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 != 0) {
                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 != 0) {
                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 != 0) {
                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)


使用dao模式完成登錄操作

  • 需求:

    • 1、把文件換成數據庫
    • 2、使用DAO模式
    • 3、防範sql注入攻擊
  • 1.web層

    • login.jsp LoginServlet User
  • 2.service層
    • UserService(實際應是接口)
  • 3.dao層
    • UserDao(實際應是接口)

  • 具體代碼見工程day17_2

  • 用戶註冊流程

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(1, fis, (int) (file.length())); //MySQL驅動只支持最後一個參數爲int類型的方法
        int row = pst.executeUpdate();

        if (row != 0) {
            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(1, 1);
        // 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 = -1;
            byte[] b = new byte[1024 * 100];

            while ((len = is.read(b)) != -1) {
                fos.write(b, 0, 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=64M
  • 對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(1, 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(1, 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(1, 1);
        // 3.得到結果集
        ResultSet rs = pst.executeQuery();

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

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

            int len = -1;
            char[] ch = new char[1024 * 100];

            while ((len = r.read(ch)) != -1) {
                fw.write(ch, 0, 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=3";
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=0;i<50000;i++){
st.setString(1, "aaa" + i);
st.setString(2, "123" + i);
st.setString(3, "aaa" + i + "@sina.com");
st.setDate(4,new Date(1980, 10, 10));

st.addBatch(); 
if(i%1000==0){
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操作

發佈了91 篇原創文章 · 獲贊 177 · 訪問量 43萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章