JDBC中事務、批量操作、大數據類型、獲取自動生成的主鍵、等用法

1 事務的用法

事務的ACID屬性:

通俗的說事務:指一組操作,要麼都成功執行,要麼都不執行-->原子性

在所有的操作沒有執行完畢之前,其他會話不能夠看到中間改變的過程-->隔離性

事務發生前,和發生後,數據的總額依然匹配-->一致性

當事務完成後,其影響應該保留下來,不能夠撤銷-->持久性

事務的操作:先定義開始一個事務,然後對數據做修改操作,這時如果提交(commit),這些修改就永遠保存下來,如果回退(rollback),數據庫管理系統將放棄您所做的所有修改而回到開始事務的狀態。

事務:指構成單個邏輯工作單元的操作集合

事務處理:保證所有事務都作爲一個工作單元來執行,即使出現了故障,都不能改變這種執行方式。當在一個事務中執行多個操作時,要麼所有的事務都被提交(commit),要麼整個事務回滾(rollback)到最初狀態。

處理事務的兩個動作:

    提交:commit:當整個事務中,所有的邏輯單元都正常執行成功,--->提交事務,--數據已經提交不能更改。

    回滾:rollback:當整個事務中,有一個邏輯單元執行失敗,--->回滾事務。
                      撤銷該事務中的所有操作--->恢復到最初狀態。

JDBC事務的細節:

1.在JDBC中事務是默認自動提交的,在執行DML語句的時候就已經提交事務了。

2.事務只對DML語句有效,對DQL(查詢)沒效果,查詢不會涉及到修改數據,但是以後再Spring設置的時候,往往把查詢也放到事務中,
  注意:以後做開發,方式代碼沒有錯,但是數據插入/修改/刪除不成功,首先判斷事務是否提交問題,

3.回滾事務在釋放資源,釋放鎖機制(InnoDB:行鎖)。

4.在MySQL中,MyISAM不支持外鍵,不支持事務,InnoDB都支持。

5.事務還有很多很多的細節操作,留給Hibernate再講講。

在Spring中有專門的事務管理器(TransactionManager):
1 銀行轉賬案例-不使用事務:
@Test
    public void testTX1() throws Exception {
        Connection conn = JdbcUtil.INSTANCE.getConn();
        //1.查找張無忌的賬戶餘額是否大於等於1000塊
        String sql = "SELECT * FROM account WHERE name = ? AND balance >= ?";
        PreparedStatement ps = conn.prepareStatement(sql);
        ps.setString(1, "張無忌");
        ps.setInt(2, 1000);
        ResultSet rs = ps.executeQuery();
        if (!rs.next()) {
            throw new RuntimeException("親,你的餘額不足!!");//有個特點結束代碼
        }
        //2.從張無忌的賬戶上減少1000塊錢。
        sql = "UPDATE account SET balance = balance - ? WHERE name = ?";
        ps = conn.prepareStatement(sql);
        ps.setInt(1, 1000);
        ps.setString(2, "張無忌");
        ps.executeUpdate();
        //模擬停電
        //System.out.print(3/0);  
        //3.從趙敏的賬戶上增加1000塊錢。
        sql = "UPDATE account SET balance = balance + ? WHERE name = ?";
        ps = conn.prepareStatement(sql);
        ps.setInt(1, 1000);
        ps.setString(2, "趙敏");
        ps.executeUpdate();
        System.out.println("轉賬成功");
        JdbcUtil.INSTANCE.close(conn, ps, rs);
    }
2 銀行轉賬案例-使用事務:
@Test
    public void testTX() {
        Connection conn = JdbcUtil.INSTANCE.getConn();
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            //1.查找張無忌的賬戶餘額是否大於等於1000塊
            String sql = "SELECT * FROM account WHERE name = ? AND balance >= ?";
            ps = conn.prepareStatement(sql);
            ps.setString(1, "張無忌");
            ps.setInt(2, 1000);
            rs = ps.executeQuery();
            if (!rs.next()) {
                throw new RuntimeException("親,你的餘額不足!!");//有個特點結束代碼
            }
            //取消事務的自動提交機制
            conn.setAutoCommit(false);
            //===================================
            //2.從張無忌的賬戶上減少1000塊錢。
            sql = "UPDATE account SET balance = balance - ? WHERE name = ?";
            ps = conn.prepareStatement(sql);
            ps.setInt(1, 1000);
            ps.setString(2, "張無忌");
            ps.executeUpdate();
            //模擬停電
            //System.out.print(3/0);  
            //3.從趙敏的賬戶上增加1000塊錢。
            sql = "UPDATE account SET balance = balance + ? WHERE name = ?";
            ps = conn.prepareStatement(sql);
            ps.setInt(1, 1000);
            ps.setString(2, "趙敏");
            ps.executeUpdate();
            //提交事務
            conn.commit();
            //====================================
        } catch (Exception e) {
            e.printStackTrace();
            try {
                conn.rollback();
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
        } finally {
            JdbcUtil.INSTANCE.close(conn, ps, rs);
        }
    }

2 批量處理操作

批量操作:當需要成批插入或者更新記錄時。可以採用Java的批量跟新機制,這一機制允許多條語句一次性提交給數據庫批量處理。通常情況下比單獨提交更有效率。

JDBC批量處理語句包括下面倆個方法:

addBatch(String),添加需要批量處理的SQL語句或是參數。

executeBatch():執行批量處理語句。

通常情況下我們會遇到2種批量執行SQL語句的情況:

多條SQL語句的批量處理:Statment

一個SQL語句的批量傳參:PreparedStatement

需求:往account表中插入1000條數據。

結論:MySQL不支持PreparedStatment的性能優化,也不支持批量操作。

Statement:

    Statement 批處理:一次性可以執行多條SQL語句,需要編譯多次。

    應用場景:系統初始化(創建表,創建數據庫等)

    添加sql語句:st.addBatch(sql)  添加sql語句

    批量處理sql語句,int[] st.esecuteBatch()

    清除緩存: st.clearBatch();

PreparedStatement:

    PreparedStatement 批處理:一次性可以執行多條SQL語句,需要編譯多次。

    應用場景:表數據初始化

    添加批量參數:psmt.addBatch(sql)  添加實際參數,執行之前,需要執行psmt.setXxx()設置實際參數

    執行批處理,int[] psmt.esecuteBatch()

    清除緩存: psmt.clearBatch();

    清除參數:psmt.clearParameter();
    //不使用批量操作
    //MyISAM:441ms
    //InnoDB:4125ms
    @Test
    public void testStatement() throws Exception {
        long begin = System.currentTimeMillis();
        Connection conn = JdbcUtil.INSTANCE.getConn();
        Statement st = conn.createStatement();
        for (int i = 0; i < 1001; i++) {
            String sql = "insert into account (name,balance) values ('韋小寶',28)";
            st.executeUpdate(sql);
        }
        JdbcUtil.INSTANCE.close(conn, st, null);
        System.out.println(System.currentTimeMillis() - begin);
    }

    //使用批量操作
    //MyISAM:492ms
    //Innodb:4151ms
    @Test
    public void testStatementByBatch() throws Exception {
        Long begin = System.currentTimeMillis();
        Connection conn = JdbcUtil.INSTANCE.getConn();
        Statement st = conn.createStatement();
        for (int i = 0; i < 1001; i++) {
            String sql = "insert into account (name,balance) values ('韋小寶',28)";
            st.addBatch(sql);//放入批量中
            if (i % 200 == 0) {
                st.executeBatch();//執行批量操作
                st.clearBatch();//清除緩存
            }
        }
        JdbcUtil.INSTANCE.close(conn, st, null);
        System.out.println(System.currentTimeMillis() - begin);
    }

    //不使用批量操作
    //MyISAM:473ms
    //InnoDB:4087ms
    @Test
    public void testPreparedStatement() throws Exception {
        Long begin = System.currentTimeMillis();
        Connection conn = JdbcUtil.INSTANCE.getConn();
        String sql = "insert into account (name,balance) values (?,?)";
        PreparedStatement ps = conn.prepareStatement(sql);
        for (int i = 0; i < 1001; i++) {
            ps.setString(1, "喬峯");
            ps.setInt(2, 36);
            ps.executeUpdate();
        }
        JdbcUtil.INSTANCE.close(conn, ps, null);
        System.out.println(System.currentTimeMillis() - begin);
    }

    //使用批量操作
    //MyISAM:470sm
    //InnoDB:4140sm
    @Test
    public void testPreparedStatementByBatch() throws Exception {
        Long begin = System.currentTimeMillis();
        Connection conn = JdbcUtil.INSTANCE.getConn();
        String sql = "insert into account (name,balance) values (?,?)";
        PreparedStatement ps = conn.prepareStatement(sql);
        for (int i = 0; i < 1001; i++) {
            ps.setString(1, "喬峯");
            ps.setInt(2, 36);
            ps.addBatch();//添加批量參數
            if (i % 200 == 0) {
                ps.executeBatch();//執行批量操作
                ps.clearBatch();//清除緩存
                ps.clearParameters();//清除參數
            }
        }
        JdbcUtil.INSTANCE.close(conn, ps, null);
        System.out.println(System.currentTimeMillis() - begin);
    }

3 大數據類型

TinyBlob, Blob, MediumBlob, LongBlob 都是二進制類型。

唯一不同的就是容量不同,可以把二進制的數據保存到數據庫,比如把一個音頻,視頻,圖片存到數據庫中。

注意:開發中我們往往把二進制文件的保存路徑存儲到數據庫中,而不是把數據存儲到數據庫。

比如:上傳一張圖片:保存到當前項目下的:/upload/12345.png


id name email headImg

1 喬峯 qiao /upload/qiao.png

2 阿朱 ah /upload/ah.png

TEXT系列:保存文字比較多(博客/小說)

TinyTEXT, TEXT, MediumTEXT, LongTEXT

對應着java中的String 在Java代碼中沒有變化。

    //把圖片寫入到數據中
    @Test
    public void testWrite() throws Exception {
        Connection conn = JdbcUtil.INSTANCE.getConn();
        String sql = "INSERT INTO image (img) values (?)";
        PreparedStatement ps = conn.prepareStatement(sql);
        InputStream in = new FileInputStream("C:/three.jpg");
        ps.setBlob(1, in);
        ps.executeUpdate();
        JdbcUtil.INSTANCE.close(conn, ps, null);
    }

    //把圖片從數據庫中讀取出來
    @Test
    public void testRead() throws Exception {
        Connection conn = JdbcUtil.INSTANCE.getConn();
        String sql = "SELECT * FROM image WHERE id = ?";
        PreparedStatement ps = conn.prepareStatement(sql);
        ps.setInt(1, 1);
        ResultSet rs = ps.executeQuery();
        if (rs.next()) {
            Blob blob = rs.getBlob("img");
            //從數據庫中讀出來
            InputStream in = blob.getBinaryStream();
            //保存到磁盤的文件中:文件拷貝操作
            Files.copy(in, Paths.get("D:/123.png"));
        }
        JdbcUtil.INSTANCE.close(conn, ps, rs);
    }
    //插入文檔
    @Test
    public void testWrite1() throws Exception {
        Connection conn = JdbcUtil.INSTANCE.getConn();
        String sql = "INSERT INTO image (img) values (?)";
        PreparedStatement ps = conn.prepareStatement(sql);
        InputStream in = new FileInputStream("C:/story.txt");
        ps.setBlob(1, in);
        ps.executeUpdate();
        JdbcUtil.INSTANCE.close(conn, ps, null);
    }


    //把文檔從數據庫中拿出來
    @Test
    public void testRead1() throws Exception {
        Connection conn = JdbcUtil.INSTANCE.getConn();
        String sql = "SELECT * FROM image WHERE id = ?";
        PreparedStatement ps = conn.prepareStatement(sql);
        ps.setInt(1, 4);
        ResultSet rs = ps.executeQuery();
        if (rs.next()) {
            Blob blob = rs.getBlob("img");
            //從數據庫中讀出來
            InputStream in = blob.getBinaryStream();
            //保存到磁盤的文件中:文件拷貝操作
            Files.copy(in, Paths.get("D:/123.txt"));
        }
        JdbcUtil.INSTANCE.close(conn, ps, rs);
    }

4 獲取自動生成的主鍵

Statement:

int executeUpdate(String sql):執行DML/DDL語句。

int executeUpdate(String sql, int autoGeneratedKeys):

參數:autoGeneratedKeys:是否需要返回自動生成的主鍵

    Statement.RETURN_GENERATED_KEYS:要返回

    Statement.NO_GENERATED_KTY:不返回

ResultSet getGeneratedKeys():獲取自動生成的主鍵

PreparedStatement:

PreparedStatement prepareStatement(String sql) :執行DML/DDL語句。

PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) :

參數:autoGeneratedKeys:是否需要返回自動生成的主鍵

    Statement.RETURN_GENERATED_KEYS:要返回

    Statement.NO_GENERATED_KTY:不返回
    @Test
    public void testStatement() throws Exception {
        Connection conn = JdbcUtil.INSTANCE.getConn();
        Statement st = conn.createStatement();
        String sql = "insert into t_student (name) values ('A')";
        st.executeUpdate(sql,Statement.RETURN_GENERATED_KEYS);
        ResultSet rs = st.getGeneratedKeys();
        if(rs.next()){
            long id = rs.getLong(1);
            System.out.println(id);
        }
        JdbcUtil.INSTANCE.close(conn, st, rs);
    }

    @Test
    public void testPreparedStatement() throws Exception {
        Connection conn = JdbcUtil.INSTANCE.getConn();
        String sql = "insert into t_student (name) values (?)";
        PreparedStatement ps = conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
        ps.setString(1, "B");
        ps.executeUpdate();
        ResultSet rs = ps.getGeneratedKeys();
        if(rs.next()){
            Long id = rs.getLong(1);
            System.out.println(id);
        }
        JdbcUtil.INSTANCE.close(conn, ps, rs);
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章