001/* 002 * Copyright (c) 2022-2025, Mybatis-Flex (fuhai999@gmail.com). 003 * <p> 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * <p> 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * <p> 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package com.mybatisflex.spring; 017 018import org.springframework.transaction.TransactionDefinition; 019import org.springframework.transaction.TransactionTimedOutException; 020 021import java.util.Date; 022 023/** 024 * 事务定义管理器 用于更完整的实现Spring事务 025 * 仅支持传统事务不支持R2DBC事务 026 * 027 * @author Aliothmoon 028 * @author Michael 029 * @since 2024/10/25 030 */ 031public final class TimeoutHolder { 032 private static final ThreadLocal<Long> TRANSACTION_DEADLINE = new ThreadLocal<>(); 033 034 public static void hold(TransactionDefinition definition) { 035 if (definition == null) { 036 return; 037 } 038 int timeout = definition.getTimeout(); 039 if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) { 040 Long deadline = System.currentTimeMillis() + timeout * 1000L; 041 TRANSACTION_DEADLINE.set(deadline); 042 } 043 } 044 045 046 /** 047 * 清除事务上下文 048 */ 049 public static void clear() { 050 TRANSACTION_DEADLINE.remove(); 051 } 052 053 /** 054 * 获取当前事务可用TTL 055 * 056 * @return int 057 */ 058 public static Integer getTimeToLiveInSeconds() { 059 Long deadline = TRANSACTION_DEADLINE.get(); 060 if (deadline == null) { 061 return null; 062 } 063 double diff = ((double) getTimeToLiveInMillis(deadline)) / 1000; 064 int secs = (int) Math.ceil(diff); 065 checkTransactionTimeout(secs <= 0, deadline); 066 return secs; 067 } 068 069 070 private static void checkTransactionTimeout(boolean deadlineReached, Long deadline) throws TransactionTimedOutException { 071 if (deadlineReached) { 072 throw new TransactionTimedOutException("Transaction timed out: deadline was " + new Date(deadline)); 073 } 074 } 075 076 077 private static long getTimeToLiveInMillis(Long deadline) throws TransactionTimedOutException { 078 if (deadline == null) { 079 throw new IllegalStateException("No timeout specified for this resource holder"); 080 } 081 long timeToLive = deadline - System.currentTimeMillis(); 082 checkTransactionTimeout(timeToLive <= 0, deadline); 083 return timeToLive; 084 } 085 086 087}