Spring Boot入門教程(四十五): 事務@Transactional
一:簡介
在Spring中事務可以通過兩種方式來管理,一種是程式設計式事務另一種是宣告式事務
- 宣告式事務:@Transactional 在方法的開頭開始事務,在方法的結束提交事務
- 程式設計式事務:TransactionTemplate或者PlatformTransactionManager
宣告式事務和程式設計式事務的區別:宣告式事務開始事務和提交事務都是固定的,不夠靈活,而程式設計式事務通過程式碼在想要的地方開始事務,在想要的地方提交事務,更加靈活。
二:宣告式事務
宣告式事務:通過AOP對目標方法進行攔截,在方法的開始出開始事務(或者加入事務),在方法結束時提交事務,當發生異常時回滾事務, 可以通過@Transactional來實現
- @Transactional註解在Service類上表示所有public方法都使用了事務
- @Transactional註解在方法上表示該方法使用註解,方法級別的註解會覆蓋類級別的註解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
// 當配置了多個事務管理器時,可以使用該屬性指定使用哪個事務管理器
@AliasFor("transactionManager" )
String value() default "";
// 事務傳播級別
Propagation propagation() default Propagation.REQUIRED;
// 設定是否只讀事務,true表示只讀事務(一般查詢設定為true),false表示讀寫事務
boolean readOnly() default false;
// 指定哪些異常型別需要回滾事務,例如@Transactional(rollbackFor={RuntimeException.class, Exception.class})
Class<? extends Throwable>[] rollbackFor() default {};
// 例如@Transactional(rollbackForClassName={“RuntimeException”,”Exception”})
String[] rollbackForClassName() default {};
// 指定當丟擲哪些異常時不需要回滾事務
Class<? extends Throwable>[] noRollbackFor() default {};
String[] noRollbackForClassName() default {};
// 該屬性用於設定底層資料庫的事務隔離級別,事務隔離級別用於處理多事務併發的情況,通常使用資料庫的預設隔離級別即可,基本不需要進行設定
Isolation isolation() default Isolation.DEFAULT;
// 設定事務的超時秒數,預設值為-1表示永不超時
int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;
}
事務傳播行為
所謂事務的傳播行為是指,如果在開始當前事務之前,一個事務上下文已經存在,此時有若干選項可以指定一個事務性方法的執行行為。事務的傳播行為,預設值為 Propagation.REQUIRED。
Propagation.REQUIRED: 如果當前存在事務,則加入該事務,如果當前不存在事務,則建立一個新的事務。
Propagation.SUPPORTS: 如果當前存在事務,則加入該事務;如果當前不存在事務,則以非事務的方式繼續執行。
Propagation.MANDATORY 如果當前存在事務,則加入該事務;如果當前不存在事務,則丟擲異常。
Propagation.REQUIRES_NEW 重新建立一個新的事務,如果當前存在事務,暫停當前的事務。
Propagation.NOT_SUPPORTED 以非事務的方式執行,如果當前存在事務,暫停當前的事務。
Propagation.NEVER 以非事務的方式執行,如果當前存在事務,則丟擲異常。
Propagation.NESTED 和 Propagation.REQUIRED 效果一樣。
事務超時
事務超時就是指一個事務所允許執行的最長時間,如果超過該時間限制但事務還沒有完成,則自動回滾事務。在 TransactionDefinition 中以 int 的值來表示超時時間,其單位是秒。
預設設定為底層事務系統的超時值,如果底層資料庫事務系統沒有設定超時值,那麼就是none,沒有超時限制。
事務隔離級別
隔離級別是指若干個併發的事務之間的隔離程度。TransactionDefinition 介面中定義了五個表示隔離級別的常量:
- Isolation.DEFAULT 使用底層資料庫預設的隔離級別。
- Isolation.READ_UNCOMMITTED
- Isolation.READ_COMMITTED
- Isolation.REPEATABLE_READ
- Isolation.SERIALIZABLE
三:宣告式事務示例
本示例基於 Spring Boot入門教程(八): MyBatis
@RestController
@RequestMapping("/orders")
public class OrderController {
@Autowired
private OrderService orderService;
/**
* {
* "orderId": 1,
* "items": [
* {
* "orderId": 1,
* "goodsId": 1,
* "quantity": 2
* }
* ]
* }
*/
@PostMapping("/")
public String addShoppingCart(@RequestBody OrderDTO orderDTO) {
orderService.addShoppingCart(orderDTO);
return "success";
}
}
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private OrderItemMapper orderItemMapper;
@Override
@Transactional(readOnly = true)
public List<Order> getOrders() {
return orderMapper.getOrders();
}
@Override
@Transactional(rollbackFor = Exception.class)
public void addShoppingCart(OrderDTO orderDTO) {
Long orderId = orderDTO.getOrderId();
orderMapper.updateOrderInfo(orderId);
orderItemMapper.insertOrderItems(orderId, orderDTO.getItems());
// 測試事務(當發生Exception時,回滾事務)
int error = 1/0;
}
}
public class OrderDTO {
private Long orderId;
private List<OrderItem> items;
// Getter & Setter
}
public class OrderItem {
private Long orderId;
private Long goodsId;
private Long quantity;
// Getter & Setter
}
public interface OrderMapper {
void updateOrderInfo(@Param("orderId") Long orderId);
}
OrderMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.example.mybatis.mapper.OrderMapper">
<update id="updateOrderInfo">
UPDATE tbl_order SET amount = amount + 2 WHERE id = #{orderId}
</update>
</mapper>
OrderItemMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.example.mybatis.mapper.OrderItemMapper">
<insert id="insertOrderItems">
INSERT INTO tbl_order_item(order_id, goods_id, quantity) VALUES
<foreach collection="items" item="item" separator=",">
(#{item.orderId}, #{item.goodsId}, #{item.quantity})
</foreach>
</insert>
</mapper>
注意 注意 注意 注意:
- @Transactional用來類上,不要用來介面上,宣告在介面上可能註解會無效
- @Transactional一般用在方法上,對於查詢方法不需要使用事務,如果用在類上,對查詢方法的效能有影響
- @Transactional 註解只能應用到 public 可見度的方法上。 如果你在 protected、private 或者 package-visible 的方法上使用 @Transactional 註解,它也不會報錯, 但是這個被註解的方法將不會展示已配置的事務設定
- 外部呼叫某個類的沒有使用@Transactional註解的方法,該方法內部調這個類的一個有事務註解的其它方法,則即使這個方法使用了事務也不會生效。比如OrderService,它的一個方法addShoppingCart,addShoppingCart再呼叫OrderService本類的方法doAddShoppingCart(不管doAddShoppingCart是否public還是private),但addShoppingCart沒有宣告註解事務,而B有。則外部呼叫addShoppingCart之後,doAddShoppingCart的事務是不會起作用的。
@Override
// @Transactional(rollbackFor = Exception.class)
public void addShoppingCart(OrderDTO orderDTO) {
test(orderDTO);
}
@Transactional(rollbackFor = Exception.class)
public void doAddShoppingCart(OrderDTO orderDTO){
Long orderId = orderDTO.getOrderId();
orderMapper.updateOrderInfo(orderId);
orderItemMapper.insertOrderItems(orderId, orderDTO.getItems());
int error = 1/0;
}
Propagation.REQUIRES_NEW :自己新起一個事務,不使用別的事務, 如果已經在事務中則掛起當前事務
@Service
public class OrderServiceHelperImpl implements OrderServiceHelper {
@Autowired
private OrderMapper orderMapper;
@Autowired
private OrderItemMapper orderItemMapper;
/**
* REQUIRES_NEW: 重新建立一個新的事務,如果當前存在事務,暫停當前的事務
* 自己獨立一個事務,不受其它事務干擾
* 注意:test方法要放到其它service中,不能放在OrderServiceImpl中
*/
@Override
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public void test(OrderDTO orderDTO){
Long orderId = orderDTO.getOrderId();
orderMapper.updateOrderInfo(orderId);
orderItemMapper.insertOrderItems(orderId, orderDTO.getItems());
}
}
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderServiceHelper orderServiceHelper;
@Override
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
public void addShoppingCart(OrderDTO orderDTO) {
// @Transactional(propagation = Propagation.REQUIRES_NEW)
// 只要該方法不拋異常就可以提交,不受addShoppingCart的影響
orderServiceHelper.test(orderDTO);
// 即使addShoppingCart跑出異常,orderServiceHelper.test(orderDTO)也不受影響
int error = 1/0;
}
}
注意:該示例要想成功test方法必須放到其它service類中,不能和OrderServiceImpl放在同一個類中,放在同一個類中會被回滾
四:程式設計式事務
public interface PlatformTransactionManager {
// 根據指定的傳播行為,返回當前活動的事務或建立新的事務。
TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;
// 提交事務
void commit(TransactionStatus status) throws TransactionException;
// 回滾事務
void rollback(TransactionStatus status) throws TransactionException;
}
public class DataSourceTransactionManager extends AbstractPlatformTransactionManager
implements ResourceTransactionManager, InitializingBean {
@Nullable
private DataSource dataSource;
public DataSourceTransactionManager(DataSource dataSource);
}
public interface TransactionDefinition {
int PROPAGATION_REQUIRED = 0;
int PROPAGATION_SUPPORTS = 1;
int PROPAGATION_MANDATORY = 2;
int PROPAGATION_REQUIRES_NEW = 3;
int PROPAGATION_NOT_SUPPORTED = 4;
int PROPAGATION_NEVER = 5;
int PROPAGATION_NESTED = 6;
int ISOLATION_DEFAULT = -1;
int ISOLATION_READ_UNCOMMITTED = Connection.TRANSACTION_READ_UNCOMMITTED;
int ISOLATION_READ_COMMITTED = Connection.TRANSACTION_READ_COMMITTED;
int ISOLATION_REPEATABLE_READ = Connection.TRANSACTION_REPEATABLE_READ;
int ISOLATION_SERIALIZABLE = Connection.TRANSACTION_SERIALIZABLE;
int TIMEOUT_DEFAULT = -1;
}
public class DefaultTransactionDefinition implements TransactionDefinition, Serializable {
private int propagationBehavior = PROPAGATION_REQUIRED;
private int isolationLevel = ISOLATION_DEFAULT;
private int timeout = TIMEOUT_DEFAULT;
private boolean readOnly = false;
@Nullable
private String name;
public DefaultTransactionDefinition();
public DefaultTransactionDefinition(TransactionDefinition other) {
this.propagationBehavior = other.getPropagationBehavior();
this.isolationLevel = other.getIsolationLevel();
this.timeout = other.getTimeout();
this.readOnly = other.isReadOnly();
this.name = other.getName();
}
}
public class TransactionTemplate extends DefaultTransactionDefinition implements TransactionOperations, InitializingBean {
@Nullable
private PlatformTransactionManager transactionManager;
public TransactionTemplate(PlatformTransactionManager transactionManager);
@Override
@Nullable
public <T> T execute(TransactionCallback<T> action) throws TransactionException;
}
@FunctionalInterface
public interface TransactionCallback<T> {
@Nullable
T doInTransaction(TransactionStatus status);
}
public abstract class TransactionCallbackWithoutResult implements TransactionCallback<Object> {
@Override
@Nullable
public final Object doInTransaction(TransactionStatus status) {
doInTransactionWithoutResult(status);
return null;
}
}
public interface TransactionStatus extends SavepointManager, Flushable {
boolean isNewTransaction();
boolean hasSavepoint();
void setRollbackOnly();
boolean isRollbackOnly();
@Override
void flush();
boolean isCompleted();
}
示例
public void test(OrderDTO orderDTO) {
TransactionTemplate transactionTemplate = getTransactionTemplate();
// TransactionTemplate 有返回值
Object result = transactionTemplate.execute(new TransactionCallback<Object>() {
@Override
public Object doInTransaction(TransactionStatus status) {
try {
Long orderId = orderDTO.getOrderId();
orderMapper.updateOrderInfo(orderId);
orderItemMapper.insertOrderItems(orderId, orderDTO.getItems());
return "success";
} catch (Exception e) {
status.setRollbackOnly();
}
return null;
}
});
System.out.println(result);
// 雖然這裡報錯,上面的事務扔不影響,已經提交過了
int a = 1/0;
}
public void test2(OrderDTO orderDTO) {
TransactionTemplate transactionTemplate = getTransactionTemplate();
// TransactionTemplate 沒有返回值
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
try {
Long orderId = orderDTO.getOrderId();
orderMapper.updateOrderInfo(orderId);
orderItemMapper.insertOrderItems(orderId, orderDTO.getItems());
} catch (Exception e) {
status.setRollbackOnly();
}
}
});
}
public void test3(OrderDTO orderDTO) {
// 直接使用transactionManager事務管理器來管理事務
DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
PlatformTransactionManager transactionManager = new DataSourceTransactionManager(dataSource);
TransactionStatus transactionStatus = transactionManager.getTransaction(definition);
try {
// coding
Long orderId = orderDTO.getOrderId();
orderMapper.updateOrderInfo(orderId);
orderItemMapper.insertOrderItems(orderId, orderDTO.getItems());
transactionManager.commit(transactionStatus);
} catch (Exception e) {
transactionManager.rollback(transactionStatus);
}
}