最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解SpringBoot中JdbcTemplate的事務(wù)控制

 更新時(shí)間:2021年09月13日 15:53:53   作者:DayDayUp丶  
JdbcTemplate是spring-jdbc提供的數(shù)據(jù)庫核心操作類,那對(duì)JdbcTemplate進(jìn)行事務(wù)控制呢,本文就詳細(xì)的介紹一下

前言

JdbcTemplate是spring-jdbc提供的數(shù)據(jù)庫核心操作類,那對(duì)JdbcTemplate進(jìn)行事務(wù)控制呢?

我的環(huán)境:spring-boot-2.1.3,druid-1.1.3。

原生Jdbc的事務(wù)控制

即,批處理+自動(dòng)提交的控制方式,

public static void demo(String[] args) throws SQLException, ClassNotFoundException {
    String url = "jdbc:mysql://10.1.4.16:3306/szhtest";
    String username = "ababab";
    String password = "123456";
    String sql1 = "insert xx";
    String sql2 = "insert xx";
    Class.forName("com.mysql.jdbc.Driver");
    Connection conn = DriverManager.getConnection(url, username, password);
    Statement statement = conn.createStatement();
    // 獲取到原本的自動(dòng)提交狀態(tài)
    boolean ac = conn.getAutoCommit();
    // 批處理多條sql操作
    statement.addBatch(sql1);
    statement.addBatch(sql2);
    // 關(guān)閉自動(dòng)提交
    conn.setAutoCommit(false);
    try {
        // 提交批處理
        statement.executeBatch();
        // 若批處理無異常,則準(zhǔn)備手動(dòng)commit
        conn.commit();
    } catch (Exception e) {
        e.printStackTrace();
        // 批處理拋異常,則rollback
        try {
            conn.rollback();
        } catch (SQLException ex) {
            ex.printStackTrace();
        }
    } finally {
        // 恢復(fù)到原本的自動(dòng)提交狀態(tài)
        conn.setAutoCommit(ac);
        if (statement != null) {
            try {
                statement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

Spring的聲明式事務(wù)控制

Bean的類或方法上加@Transactional,事務(wù)控制粒度較大,只能控制在方法級(jí)別,不能控制到代碼粒度級(jí)別。

嘗試JdbcTemplate的事務(wù)控制

采取跟原生jdbc事務(wù)控制一樣的方法試試,在批處理前關(guān)閉自動(dòng)提交,若批處理失敗則回滾的思路。

@RequestMapping("/druidData1")
public String druidData1() throws SQLException {
    String sql1 = "INSERT INTO user_tmp(`id`, `username`) VALUES(22, 222)";
    // id=1的主鍵沖突插入失敗
    String sql2 = "INSERT INTO user_tmp(`id`, `username`) VALUES(1, 111)";
    Connection conn = jdbcTemplate.getDataSource().getConnection();
    LOG.info("1:{}", conn);
    boolean ac = conn.getAutoCommit();
    conn.setAutoCommit(false);
    try {
        int[] rs2 = jdbcTemplate.batchUpdate(new String[]{sql1, sql2});
        conn.commit();
    } catch (Throwable e) {
        LOG.error("Error occured, cause by: {}", e.getMessage());
        conn.rollback();
    } finally {
        conn.setAutoCommit(ac);
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                LOG.error("Error occurred while closing connectin, cause by: {}", e.getMessage());
            }
        }
    }
    return "test";
}

期望結(jié)果:id=1的因?yàn)橹麈I沖突,所以id=22的也要回滾。

實(shí)際結(jié)果:id=1的插入失敗,id=22的插入成功,未回滾。

原因分析:自始至終都是同一個(gè)connection連接對(duì)象,按道理不應(yīng)該無法控制自動(dòng)提交,唯一的解釋是jdbcTemplate.batchUpdate()中真正使用的連接對(duì)象并非代碼中的conn,于是一方面把conn打印出來,另一方面準(zhǔn)備調(diào)試jdbcTemplate.batchUpdate()源碼內(nèi)部,看看是否使用了另外獲取到的connection。

調(diào)試流程:jdbcTemplate.batchUpdate()

execute(new BatchUpdateStatementCallback())

DataSourceUtils.getConnection(obtainDataSource())

對(duì)比兩個(gè)connection,確非同一對(duì)象,因此對(duì)我們的conn進(jìn)行事務(wù)的控制不會(huì)影響jdbcTemplate內(nèi)部真正使用的con,

緊接著進(jìn)入源碼376行,回調(diào)函數(shù)action.doInStatement(stmt)

在回調(diào)函數(shù)中,真正進(jìn)行數(shù)據(jù)庫操作。至此,便明白了這樣的方法為何不能成功控制jdbcTemplate事務(wù)的原因,即我們控制的conn和jdbcTemplate真正使用的con不是同一個(gè)對(duì)象。那如果Druid數(shù)據(jù)庫連接池里只有1個(gè)conn呢,這樣的方法會(huì)不會(huì)成功控制?

于是修改druid配置,將initial-size、max-active、min-idle都設(shè)置為1,這樣,你jdbcTemplate里獲取到的跟我的conn總該是同一對(duì)象了吧?然而,方法運(yùn)行約1min后,拋出異常:

Failed to obtain JDBC Connection; nested exception is com.alibaba.druid.pool.GetConnectionTimeoutException: wait millis 60001, active 1, maxActive 1, creating 0

繼續(xù)跟了一下源碼,原來是池子里最大只有一個(gè)連接conn,而它又未被釋放,導(dǎo)致jdbcTemplate內(nèi)部再去從池子里獲取con時(shí),一直在等待已有連接conn的釋放,一直等不到釋放,所以等待了max-wait=60000ms的時(shí)間,最后報(bào)錯(cuò)。

所以這樣的控制也是不合理的,那究竟如何控制JdbcTemplate的事務(wù)呢?答案就是TransactionTemplate

TransactionTemplate的編程式事務(wù)控制

注冊(cè)事務(wù)相關(guān)bean:TransactionTemplate,如下:

package com.boot.druid.config;
 
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.support.TransactionTemplate;
 
/**
 * Druid數(shù)據(jù)庫連接池配置文件
 */
@Configuration
public class DruidConfig {
    private static final Logger logger = LoggerFactory.getLogger(DruidConfig.class);
 
    @Value("${spring.datasource.druid.url}")
    private String dbUrl;
 
    @Value("${spring.datasource.druid.username}")
    private String username;
 
    @Value("${spring.datasource.druid.password}")
    private String password;
 
    @Value("${spring.datasource.druid.driverClassName}")
    private String driverClassName;
 
    @Value("${spring.datasource.druid.initial-size}")
    private int initialSize;
 
    @Value("${spring.datasource.druid.max-active}")
    private int maxActive;
 
    @Value("${spring.datasource.druid.min-idle}")
    private int minIdle;
 
    @Value("${spring.datasource.druid.max-wait}")
    private int maxWait;
 
    /**
     * Druid 連接池配置
     */
    @Bean     //聲明其為Bean實(shí)例
    public DruidDataSource dataSource() {
        DruidDataSource datasource = new DruidDataSource();
        datasource.setUrl(dbUrl);
        datasource.setUsername(username);
        datasource.setPassword(password);
        datasource.setDriverClassName(driverClassName);
        datasource.setInitialSize(initialSize);
        datasource.setMinIdle(minIdle);
        datasource.setMaxActive(maxActive);
        datasource.setMaxWait(maxWait);
        datasource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);
        try {
            datasource.setFilters(filters);
        } catch (Exception e) {
            logger.error("druid configuration initialization filter", e);
        }
        datasource.setConnectionProperties(connectionProperties);
        return datasource;
    }
    /**
     * JDBC操作配置
     */
    @Bean(name = "dataOneTemplate")
    public JdbcTemplate jdbcTemplate (@Autowired DruidDataSource dataSource){
        return new JdbcTemplate(dataSource) ;
    }
    /**
     * 裝配事務(wù)管理器
     */
    @Bean(name="transactionManager")
    public DataSourceTransactionManager transactionManager(@Autowired DruidDataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
 
    /**
     * JDBC事務(wù)操作配置
     */
    @Bean(name = "txTemplate")
    public TransactionTemplate transactionTemplate (@Autowired DataSourceTransactionManager transactionManager){
        return new TransactionTemplate(transactionManager);
    }
 
    /**
     * 配置 Druid 監(jiān)控界面
     */
    @Bean
    public ServletRegistrationBean statViewServlet(){
        ServletRegistrationBean srb =
                new ServletRegistrationBean(new StatViewServlet(),"/druid/*");
        //設(shè)置控制臺(tái)管理用戶
        srb.addInitParameter("loginUsername","root");
        srb.addInitParameter("loginPassword","root");
        //是否可以重置數(shù)據(jù)
        srb.addInitParameter("resetEnable","false");
        return srb;
    }
    @Bean
    public FilterRegistrationBean statFilter(){
        //創(chuàng)建過濾器
        FilterRegistrationBean frb =
                new FilterRegistrationBean(new WebStatFilter());
        //設(shè)置過濾器過濾路徑
        frb.addUrlPatterns("/*");
        //忽略過濾的形式
        frb.addInitParameter("exclusions",
                "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
        return frb;
    }
}

然后注入TransactionTemplate,使用transactionTemplate.execute(new TransactionCallback<> action)或者transactionTemplate.execute(new TransactionCallbackWithoutResult<> action)執(zhí)行多條sql,最后可以通過transactionStatussetRollbackOnly()或rollbackToSavepoint(savepoint) 控制事務(wù),如下:

@RequestMapping("/druidData2")
public String runTransactionSamples() {
    String sql1 = "INSERT INTO user_tmp(`id`, `username`) VALUES(22, 222)";
    String sql2 = "INSERT INTO user_tmp(`id`, `username`) VALUES(1, 111)";
    txTemplate.execute(new TransactionCallback<Object>() {
        @Override
        public Object doInTransaction(TransactionStatus transactionStatus) {
            Object savepoint = transactionStatus.createSavepoint();
            // DML執(zhí)行
            try {
                int[] rs2 = jdbcTemplate.batchUpdate(new String[]{sql1, sql2});
            } catch (Throwable e) {
                LOG.error("Error occured, cause by: {}", e.getMessage());
                transactionStatus.setRollbackOnly();
                // transactionStatus.rollbackToSavepoint(savepoint);
            }
            return null;
        }
    });
    return "test2";
}

上面是不帶參數(shù)的多條sql的事務(wù)執(zhí)行,若是帶參數(shù)的多條sql,可以實(shí)現(xiàn)如下:

@RequestMapping("/druidData3")
public String runTransactionSamples2() {
    String sql1 = "INSERT INTO user_tmp(`id`, `username`) VALUES(?, ?)";
    Object[] args1 = new Object[] {22, 222};
    String sql2 = "INSERT INTO user_tmp(`id`, `username`) VALUES(?, ?)";
    Object[] args2 = new Object[] {1, 111};
    txTemplate.execute(new TransactionCallback<Object>() {
        @Override
        public Object doInTransaction(TransactionStatus transactionStatus) {
            Object savepoint = transactionStatus.createSavepoint();
            // DML執(zhí)行
            try {
                int rs1 = jdbcTemplate.update(sql1, args1);
                int rs2 = jdbcTemplate.update(sql2, args2);
            } catch (Throwable e) {
                LOG.error("Error occured, cause by: {}", e.getMessage());
                transactionStatus.setRollbackOnly();
                // transactionStatus.rollbackToSavepoint(savepoint);
            }
            return null;
        }
    });
    return "test2";
}

到此這篇關(guān)于SpringBoot中JdbcTemplate的事務(wù)控制的文章就介紹到這了,更多相關(guān)SpringBoot JdbcTemplate事務(wù)控制內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 深入解析Spring中的@Bean注解

    深入解析Spring中的@Bean注解

    這篇文章主要介紹了深入解析Spring中的@Bean注解,Spring的@Bean注解用于告訴方法,產(chǎn)生一個(gè)Bean對(duì)象,然后這個(gè)Bean對(duì)象交給Spring管理,產(chǎn)生這個(gè)Bean對(duì)象的方法Spring只會(huì)調(diào)用一次,隨后這個(gè)Spring將會(huì)將這個(gè)Bean對(duì)象放在自己的IOC容器中,需要的朋友可以參考下
    2023-07-07
  • 詳解Java中Comparable和Comparator接口的區(qū)別

    詳解Java中Comparable和Comparator接口的區(qū)別

    這篇文章主要介紹了詳解Java中Comparable和Comparator接口的區(qū)別的相關(guān)資料,希望通過本文大家能徹底掌握這部分內(nèi)容,需要的朋友可以參考下
    2017-09-09
  • Java實(shí)現(xiàn)學(xué)生成績(jī)輸出到磁盤文件的方法詳解

    Java實(shí)現(xiàn)學(xué)生成績(jī)輸出到磁盤文件的方法詳解

    這篇文章主要為大家詳細(xì)介紹了如何利用Java實(shí)現(xiàn)將學(xué)生成績(jī)輸出到磁盤文件的功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下
    2022-11-11
  • 解決SpringBoot @value注解取不到值的問題

    解決SpringBoot @value注解取不到值的問題

    這篇文章主要介紹了解決SpringBoot @value注解取不到值的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • 淺談JVM中的JOL

    淺談JVM中的JOL

    我們天天都在使用java來new對(duì)象,但估計(jì)很少有人知道new出來的對(duì)象到底長(zhǎng)的什么樣子?對(duì)于普通的java程序員來說,可能從來沒有考慮過java中對(duì)象的問題,不懂這些也可以寫好代碼。今天,給大家介紹一款工具JOL,可以滿足大家對(duì)java對(duì)象的所有想象。
    2021-06-06
  • 徹底搞懂java并發(fā)ThreadPoolExecutor使用

    徹底搞懂java并發(fā)ThreadPoolExecutor使用

    這篇文章主要為大家介紹了徹底搞懂java并發(fā)ThreadPoolExecutor使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-02-02
  • Swagger實(shí)現(xiàn)動(dòng)態(tài)條件注入與全局?jǐn)r截功能詳細(xì)流程

    Swagger實(shí)現(xiàn)動(dòng)態(tài)條件注入與全局?jǐn)r截功能詳細(xì)流程

    這篇文章主要介紹了Swagger實(shí)現(xiàn)動(dòng)態(tài)條件注入與全局?jǐn)r截功能詳細(xì)流程,Swagger 可以提供 API 操作的測(cè)試文檔,本文記錄 Swagger 使用過程中遇到的小問題
    2023-01-01
  • springboot?整合?dubbo?的實(shí)現(xiàn)組聚合詳情

    springboot?整合?dubbo?的實(shí)現(xiàn)組聚合詳情

    這篇文章主要介紹了springboot整合dubbo的實(shí)現(xiàn)組聚合詳情,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-07-07
  • Springboot輕量級(jí)的監(jiān)控組件SpringbootAdmin

    Springboot輕量級(jí)的監(jiān)控組件SpringbootAdmin

    這篇文章主要為大家介紹了Springboot輕量級(jí)的監(jiān)控組件SpringbootAdmin使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-02-02
  • java多線程中的異常處理機(jī)制簡(jiǎn)析

    java多線程中的異常處理機(jī)制簡(jiǎn)析

    在java多線程程序中,所有線程都不允許拋出未捕獲的checked exception,也就是說各個(gè)線程需要自己把自己的checked exception處理掉,需要了解的朋友可以參考下
    2012-11-11

最新評(píng)論

高陵县| 游戏| 肇源县| 南阳市| 微山县| 鲁甸县| 临湘市| 芒康县| 江陵县| 同江市| 南川市| 平阳县| 宜城市| 时尚| 通山县| 静安区| 越西县| 县级市| 大田县| 板桥市| 威海市| 久治县| 余姚市| 桑日县| 安图县| 洛宁县| 洛扎县| 晋宁县| 柏乡县| 龙南县| 临武县| 铜川市| 乡城县| 玛纳斯县| 崇信县| 岢岚县| 新昌县| 济源市| 琼海市| 定襄县| 谢通门县|