欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java事務(wù)失效八大場(chǎng)景詳細(xì)解析

 更新時(shí)間:2025年10月23日 08:47:41   作者:xiaoye3708  
在Java開(kāi)發(fā)中Spring框架提供了強(qiáng)大的事務(wù)管理功能,然而在實(shí)際開(kāi)發(fā)過(guò)程中,由于對(duì)框架特性的誤解或不當(dāng)使用,可能導(dǎo)致事務(wù)失效問(wèn)題,這篇文章主要介紹了Java事務(wù)失效八大場(chǎng)景的相關(guān)資料,需要的朋友可以參考下

前言

在 Java 開(kāi)發(fā)中,事務(wù)管理是保證數(shù)據(jù)一致性的核心機(jī)制,尤其是在 Spring 框架中,@Transactional注解的使用極大簡(jiǎn)化了事務(wù)配置。然而,在實(shí)際開(kāi)發(fā)中,事務(wù)常常會(huì)因?yàn)橐恍┘?xì)節(jié)問(wèn)題而失效,導(dǎo)致數(shù)據(jù)異常。本文將詳細(xì)解析 Java 事務(wù)失效的八大場(chǎng)景,每個(gè)場(chǎng)景都提供代碼示例與對(duì)應(yīng)的修復(fù)方案。

一、事務(wù)方法非 public 修飾

失效原理

Spring 的@Transactional注解默認(rèn)只對(duì)public方法生效。這是因?yàn)?Spring AOP 在實(shí)現(xiàn)事務(wù)管理時(shí),無(wú)論是 JDK 動(dòng)態(tài)代理還是 CGLIB 代理,都無(wú)法對(duì)非 public 方法(private、protected、默認(rèn)訪問(wèn)權(quán)限)進(jìn)行有效的事務(wù)增強(qiáng)。

失效代碼

java運(yùn)行

@Service
public class UserService {
    // 非public方法,事務(wù)注解失效
    @Transactional
    void updateUser(Long id) { 
        userMapper.updateStatus(id, 1);
    }
}

修復(fù)方案

將事務(wù)方法修改為public訪問(wèn)權(quán)限。

修復(fù)后代碼

java運(yùn)行

@Service
public class UserService {
    // 修改為public方法,事務(wù)生效
    @Transactional
    public void updateUser(Long id) { 
        userMapper.updateStatus(id, 1);
    }
}

二、異常被捕獲且未重新拋出

失效原理

Spring 事務(wù)默認(rèn)僅在遇到未捕獲的RuntimeExceptionError時(shí)觸發(fā)回滾。如果方法內(nèi)部使用try-catch捕獲了異常且未重新拋出,事務(wù)管理器會(huì)認(rèn)為沒(méi)有異常發(fā)生,從而不會(huì)執(zhí)行回滾操作。

失效代碼

java運(yùn)行

@Service
public class OrderService {
    @Transactional
    public void createOrder(Order order) {
        try {
            orderMapper.insert(order);
            // 模擬異常
            int i = 1 / 0;
        } catch (Exception e) {
            // 捕獲異常但未拋出,事務(wù)不會(huì)回滾
            log.error("創(chuàng)建訂單失敗", e);
        }
    }
}

修復(fù)方案

方案一:捕獲異常后重新拋出
方案二:使用TransactionAspectSupport手動(dòng)觸發(fā)回滾

修復(fù)后代碼(方案一)

java運(yùn)行

@Service
public class OrderService {
    @Transactional
    public void createOrder(Order order) {
        try {
            orderMapper.insert(order);
            // 模擬異常
            int i = 1 / 0;
        } catch (Exception e) {
            log.error("創(chuàng)建訂單失敗", e);
            // 重新拋出異常,觸發(fā)事務(wù)回滾
            throw new RuntimeException("創(chuàng)建訂單失敗", e);
        }
    }
}

修復(fù)后代碼(方案二)

java運(yùn)行

@Service
public class OrderService {
    @Transactional
    public void createOrder(Order order) {
        try {
            orderMapper.insert(order);
            // 模擬異常
            int i = 1 / 0;
        } catch (Exception e) {
            log.error("創(chuàng)建訂單失敗", e);
            // 手動(dòng)觸發(fā)事務(wù)回滾
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
    }
}

三、錯(cuò)誤配置 rollbackFor 屬性

失效原理

@TransactionalrollbackFor屬性用于指定需要回滾的異常類(lèi)型,默認(rèn)值為{RuntimeException.class, Error.class}。如果業(yè)務(wù)中拋出的是受檢查異常(如IOException、SQLException),且未在rollbackFor中聲明,事務(wù)不會(huì)回滾。

失效代碼

java運(yùn)行

@Service
public class FileService {
    // 未指定rollbackFor,受檢查異常不會(huì)觸發(fā)回滾
    @Transactional
    public void importData(String filePath) throws IOException {
        // 讀取文件(可能拋出IOException)
        FileReader reader = new FileReader(filePath);
        // 數(shù)據(jù)入庫(kù)操作
        dataMapper.batchInsert(parseData(reader));
    }
}

修復(fù)方案

顯式指定rollbackFor屬性,包含需要回滾的異常類(lèi)型。

修復(fù)后代碼

java運(yùn)行

@Service
public class FileService {
    // 顯式指定rollbackFor包含IOException
    @Transactional(rollbackFor = {IOException.class, RuntimeException.class})
    public void importData(String filePath) throws IOException {
        FileReader reader = new FileReader(filePath);
        dataMapper.batchInsert(parseData(reader));
    }
}

// 更通用的方式:捕獲所有Exception
@Service
public class FileService {
    @Transactional(rollbackFor = Exception.class)
    public void importData(String filePath) throws IOException {
        // 業(yè)務(wù)邏輯不變
    }
}

四、事務(wù)傳播機(jī)制配置不當(dāng)

失效原理

Spring 事務(wù)的傳播機(jī)制決定了事務(wù)方法之間的嵌套行為。若傳播機(jī)制配置不合理(如使用NOT_SUPPORTED、SUPPORTS等),可能導(dǎo)致操作不在事務(wù)中執(zhí)行。

失效代碼

java運(yùn)行

@Service
public class OrderService {
    @Autowired
    private PaymentService paymentService;
    
    @Transactional
    public void createOrder(Order order) {
        orderMapper.insert(order);
        // 調(diào)用支付服務(wù)(非事務(wù)方式執(zhí)行)
        paymentService.processPayment(order.getId(), order.getAmount());
    }
}

@Service
public class PaymentService {
    // 配置為非事務(wù)方式執(zhí)行
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void processPayment(Long orderId, BigDecimal amount) {
        paymentMapper.insert(new Payment(orderId, amount));
        // 若此處發(fā)生異常,不會(huì)回滾
    }
}

修復(fù)方案

根據(jù)業(yè)務(wù)需求選擇合適的傳播機(jī)制,常用的是默認(rèn)的REQUIRED(如果當(dāng)前有事務(wù)則加入,否則創(chuàng)建新事務(wù))。

修復(fù)后代碼

java運(yùn)行

@Service
public class PaymentService {
    // 使用默認(rèn)傳播機(jī)制REQUIRED
    @Transactional
    public void processPayment(Long orderId, BigDecimal amount) {
        paymentMapper.insert(new Payment(orderId, amount));
    }
}

五、同類(lèi)方法內(nèi)部調(diào)用

失效原理

Spring 事務(wù)基于 AOP 代理實(shí)現(xiàn),事務(wù)增強(qiáng)邏輯在代理對(duì)象中執(zhí)行。若同一個(gè)類(lèi)中的方法 A 調(diào)用方法 B(B 有@Transactional注解),由于調(diào)用未經(jīng)過(guò)代理對(duì)象,方法 B 的事務(wù)注解會(huì)失效。

失效代碼

java運(yùn)行

@Service
public class UserService {
    // 方法A(無(wú)事務(wù))調(diào)用方法B(有事務(wù))
    public void updateUserInfo(Long id, String name, Integer age) {
        updateUserName(id, name);  // 內(nèi)部調(diào)用,事務(wù)失效
        updateUserAge(id, age);    // 內(nèi)部調(diào)用,事務(wù)失效
    }
    
    @Transactional
    public void updateUserName(Long id, String name) {
        userMapper.updateName(id, name);
    }
    
    @Transactional
    public void updateUserAge(Long id, Integer age) {
        userMapper.updateAge(id, age);
    }
}

修復(fù)方案

方案一:將方法拆分到不同的類(lèi)中
方案二:通過(guò)AopContext獲取代理對(duì)象調(diào)用

修復(fù)后代碼(方案二)

java運(yùn)行

// 1. 首先在啟動(dòng)類(lèi)開(kāi)啟暴露代理
@SpringBootApplication
@EnableAspectJAutoProxy(exposeProxy = true)  // 關(guān)鍵配置
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

// 2. 在Service中通過(guò)代理對(duì)象調(diào)用
@Service
public class UserService {
    public void updateUserInfo(Long id, String name, Integer age) {
        // 通過(guò)AopContext獲取代理對(duì)象
        UserService proxy = (UserService) AopContext.currentProxy();
        proxy.updateUserName(id, name);  // 代理對(duì)象調(diào)用,事務(wù)生效
        proxy.updateUserAge(id, age);    // 代理對(duì)象調(diào)用,事務(wù)生效
    }
    
    @Transactional
    public void updateUserName(Long id, String name) {
        userMapper.updateName(id, name);
    }
    
    @Transactional
    public void updateUserAge(Long id, Integer age) {
        userMapper.updateAge(id, age);
    }
}

六、數(shù)據(jù)庫(kù)不支持事務(wù)

失效原理

事務(wù)最終依賴數(shù)據(jù)庫(kù)支持。若使用的數(shù)據(jù)庫(kù)引擎不支持事務(wù)(如 MySQL 的MyISAM引擎),即使代碼中配置了事務(wù),也無(wú)法生效。

失效場(chǎng)景

sql

-- 使用MyISAM引擎創(chuàng)建表,不支持事務(wù)
CREATE TABLE `user` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;

此時(shí)即使 Service 層配置了@Transactional,數(shù)據(jù)庫(kù)操作也不會(huì)有事務(wù)保障。

修復(fù)方案

使用支持事務(wù)的數(shù)據(jù)庫(kù)引擎(如 MySQL 的InnoDB)。

修復(fù)后代碼

sql

-- 使用InnoDB引擎創(chuàng)建表,支持事務(wù)
CREATE TABLE `user` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

七、未被 Spring 容器管理

失效原理

若事務(wù)所在的類(lèi)未被 Spring 容器掃描并實(shí)例化(如未加@Service、@Component等注解),@Transactional注解會(huì)因沒(méi)有代理對(duì)象而失效。

失效代碼

java運(yùn)行

// 未加@Service注解,未被Spring管理
public class ProductService {
    @Autowired
    private ProductMapper productMapper;
    
    @Transactional
    public void updateStock(Long productId, Integer quantity) {
        productMapper.decreaseStock(productId, quantity);
    }
}

修復(fù)方案

為類(lèi)添加 Spring 注解(如@Service),確保其被 Spring 容器管理。

修復(fù)后代碼

java運(yùn)行

// 添加@Service注解,被Spring容器管理
@Service
public class ProductService {
    @Autowired
    private ProductMapper productMapper;
    
    @Transactional
    public void updateStock(Long productId, Integer quantity) {
        productMapper.decreaseStock(productId, quantity);
    }
}

八、多線程場(chǎng)景下的事務(wù)隔離

失效原理

在事務(wù)方法中啟動(dòng)新線程執(zhí)行數(shù)據(jù)庫(kù)操作時(shí),新線程的操作不會(huì)納入當(dāng)前事務(wù)管理(線程間事務(wù)上下文獨(dú)立)。即使主線程事務(wù)回滾,新線程的操作也可能已提交。

失效代碼

java運(yùn)行

@Service
public class BatchService {
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private LogMapper logMapper;
    
    @Transactional
    public void batchProcess(List<Long> userIds) {
        // 主線程操作
        userMapper.batchUpdateStatus(userIds, 1);
        
        // 新線程執(zhí)行日志記錄(不在當(dāng)前事務(wù)中)
        new Thread(() -> {
            logMapper.insert(new Log("批量處理用戶: " + userIds));
        }).start();
    }
}

修復(fù)方案

避免在事務(wù)方法中使用多線程執(zhí)行數(shù)據(jù)庫(kù)操作,或使用分布式事務(wù)協(xié)調(diào)機(jī)制。

修復(fù)后代碼

java運(yùn)行

@Service
public class BatchService {
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private LogMapper logMapper;
    
    @Transactional
    public void batchProcess(List<Long> userIds) {
        // 主線程操作
        userMapper.batchUpdateStatus(userIds, 1);
        
        // 同一事務(wù)中執(zhí)行日志記錄
        logMapper.insert(new Log("批量處理用戶: " + userIds));
    }
}

總結(jié)

事務(wù)失效的核心原因通常與以下幾點(diǎn)相關(guān):

  • 代理機(jī)制限制(非 public 方法、同類(lèi)內(nèi)部調(diào)用)
  • 異常處理不當(dāng)(捕獲未拋出、未配置 rollbackFor)
  • 事務(wù)屬性配置錯(cuò)誤(傳播機(jī)制不合理)
  • 基礎(chǔ)環(huán)境問(wèn)題(數(shù)據(jù)庫(kù)不支持、未被 Spring 管理)
  • 并發(fā)場(chǎng)景下的事務(wù)隔離問(wèn)題

在實(shí)際開(kāi)發(fā)中,需結(jié)合業(yè)務(wù)場(chǎng)景合理配置事務(wù)屬性,同時(shí)注意編碼規(guī)范,避免上述陷阱,才能確保事務(wù)機(jī)制有效運(yùn)行,保障數(shù)據(jù)一致性。

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

相關(guān)文章

  • 在mybatis中使用mapper進(jìn)行if條件判斷

    在mybatis中使用mapper進(jìn)行if條件判斷

    這篇文章主要介紹了在mybatis中使用mapper進(jìn)行if條件判斷,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-01-01
  • 深入解析Java編程中方法的參數(shù)傳遞

    深入解析Java編程中方法的參數(shù)傳遞

    這篇文章主要介紹了Java編程中方法的參數(shù)傳遞,是Java入門(mén)學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下
    2015-10-10
  • Java中如何判斷中文字符串長(zhǎng)度

    Java中如何判斷中文字符串長(zhǎng)度

    這篇文章主要介紹了Java中如何判斷中文字符串長(zhǎng)度問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-05-05
  • Java 添加、更新和移除PDF超鏈接的實(shí)現(xiàn)方法

    Java 添加、更新和移除PDF超鏈接的實(shí)現(xiàn)方法

    PDF超鏈接用一個(gè)簡(jiǎn)單的鏈接包含了大量的信息,滿足了人們?cè)诓徽加锰嗫臻g的情況下渲染外部信息的需求。這篇文章主要介紹了Java 添加、更新和移除PDF超鏈接的實(shí)現(xiàn)方法,需要的朋友可以參考下
    2019-05-05
  • Hadoop1.2中配置偽分布式的實(shí)例

    Hadoop1.2中配置偽分布式的實(shí)例

    這篇文章主要介紹了Hadoop1.2中配置偽分布式的實(shí)例,使用的系統(tǒng)是linux mint 15 64bit,hadoop使用的是1.2.1版本,需要的朋友可以參考下
    2014-04-04
  • IDEA中創(chuàng)建maven項(xiàng)目webapp目錄無(wú)法識(shí)別即未被標(biāo)識(shí)的解決辦法

    IDEA中創(chuàng)建maven項(xiàng)目webapp目錄無(wú)法識(shí)別即未被標(biāo)識(shí)的解決辦法

    在學(xué)習(xí)SpringMVC課程中,基于IDEA新建maven項(xiàng)目模塊后,webapp目錄未被標(biāo)識(shí),即沒(méi)有小藍(lán)點(diǎn)的圖標(biāo)顯示,所以本文給大家介紹了IDEA中創(chuàng)建maven項(xiàng)目webapp目錄無(wú)法識(shí)別即未被標(biāo)識(shí)的解決辦法,需要的朋友可以參考下
    2024-03-03
  • SpringBoot項(xiàng)目中處理返回json的null值(springboot項(xiàng)目為例)

    SpringBoot項(xiàng)目中處理返回json的null值(springboot項(xiàng)目為例)

    本文以spring boot項(xiàng)目為例給大家介紹SpringBoot項(xiàng)目中處理返回json的null值問(wèn)題,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友參考下
    2019-10-10
  • Java代碼審計(jì)的一些基礎(chǔ)知識(shí)你知道嗎

    Java代碼審計(jì)的一些基礎(chǔ)知識(shí)你知道嗎

    這篇文章主要介紹了基于Java的代碼審計(jì)功能的基礎(chǔ)知識(shí),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2021-09-09
  • 教你如何更改IDEA項(xiàng)目的路徑

    教你如何更改IDEA項(xiàng)目的路徑

    這篇文章主要介紹了教你如何更改IDEA項(xiàng)目的路徑,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-01-01
  • JAVA maven項(xiàng)目使用釘釘SDK獲取token、用戶

    JAVA maven項(xiàng)目使用釘釘SDK獲取token、用戶

    這篇文章主要介紹了JAVA maven項(xiàng)目使用釘釘SDK獲取token、用戶,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-06-06

最新評(píng)論