当前位置: 首页 > news >正文

【源码】Spring Data JPA原理解析之事务执行原理

 Spring Data JPA系列

1、SpringBoot集成JPA及基本使用

2、Spring Data JPA Criteria查询、部分字段查询

3、Spring Data JPA数据批量插入、批量更新真的用对了吗

4、Spring Data JPA的一对一、LazyInitializationException异常、一对多、多对多操作

5、Spring Data JPA自定义Id生成策略、复合主键配置、Auditing使用

6、【源码】Spring Data JPA原理解析之Repository的自动注入(一)

7、【源码】Spring Data JPA原理解析之Repository的自动注入(二)

8、【源码】Spring Data JPA原理解析之Repository执行过程及SimpleJpaRepository源码

9、【源码】Spring Data JPA原理解析之Repository自定义方法命名规则执行原理(一)

10、【源码】Spring Data JPA原理解析之Repository自定义方法命名规则执行原理(二)

11、【源码】Spring Data JPA原理解析之Repository自定义方法添加@Query注解的执行原理

12、【源码】SpringBoot事务注册原理

13、【源码】Spring Data JPA原理解析之事务注册原理

14、【源码】Spring Data JPA原理解析之事务执行原理

前言

前两篇博文【源码】SpringBoot事务注册原理-CSDN博客和【源码】Spring Data JPA原理解析之事务注册原理-CSDN博客从源码的角度分别分析了Spring容器中的bean以及JPA中的Repository类的事务注册的原理。它们都是在方法中添加@Transactional注解,最后生成代理类,在代理类中添加TransactionInterceptor拦截器,从而实现了事务的管理。

限于篇幅,上面两篇博文只讲解了添加了TransactionInterceptor拦截器,本篇继续从源码的角度,分析一下TransactionInterceptor拦截器的执行过程。

ReflectiveMethodInvocation回顾

Spring中的动态代理对象方法调用的时候,会先执行ReflectiveMethodInvocation.proceed()方法,循环遍历所有的拦截器。执行完所有拦截器之后,再执行动态代理对象的target类的对应方法,即原方法。详见:

【源码】Spring Data JPA原理解析之Repository执行过程及SimpleJpaRepository源码-CSDN博客

博客中的动态代理方法拦截部分。

对于方法中添加@Transactional注解的代理类,在执行方法之前,会先执行拦截器的invoke()方法,也就包含了TransactionInterceptor拦截器。

TransactionInterceptor

TransactionInterceptor的invoke()方法代码如下:

public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable {@Override@Nullablepublic Object invoke(MethodInvocation invocation) throws Throwable {// 获取代理类的代理目标对象Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);// 在事务内调用return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);}
}

在invoke()方法中,先获取了代理类的目标对象,然后调用父类的invokeWithinTransaction()方法,执行事务处理。

TransactionAspectSupport

TransactionAspectSupport的核心代码如下:

package org.springframework.transaction.interceptor;public abstract class TransactionAspectSupport implements BeanFactoryAware, InitializingBean {private static final Object DEFAULT_TRANSACTION_MANAGER_KEY = new Object();private static final ThreadLocal<TransactionInfo> transactionInfoHolder =new NamedThreadLocal<>("Current aspect-driven transaction");@Nullableprotected static TransactionInfo currentTransactionInfo() throws NoTransactionException {return transactionInfoHolder.get();}/*** 静态方法,在代码中,可以通过该方法,修改事务状态,实现手动回滚等*/public static TransactionStatus currentTransactionStatus() throws NoTransactionException {TransactionInfo info = currentTransactionInfo();if (info == null || info.transactionStatus == null) {throw new NoTransactionException("No transaction aspect-managed TransactionStatus in scope");}return info.transactionStatus;}protected final Log logger = LogFactory.getLog(getClass());@Nullableprivate String transactionManagerBeanName;@Nullableprivate PlatformTransactionManager transactionManager;// AnnotationTransactionAttributeSource对象@Nullableprivate TransactionAttributeSource transactionAttributeSource;@Nullableprivate BeanFactory beanFactory;// 省略其他/*** 执行事务*/@Nullableprotected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,final InvocationCallback invocation) throws Throwable {// If the transaction attribute is null, the method is non-transactional.// 如果事务属性资源为null,则方法没有事务TransactionAttributeSource tas = getTransactionAttributeSource();// 获取@Transactional注解的事务属性信息final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);// 获取事务管理器,如果是Jpa,则返回JpaTransactionManager;如果是普通jdbc,则返回JdbcTransactionManagerfinal PlatformTransactionManager tm = determineTransactionManager(txAttr);// 获取方法限定名。格式:类名.方法名final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {// Standard transaction demarcation with getTransaction and commit/rollback calls.TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);Object retVal;try {// 执行原方法retVal = invocation.proceedWithInvocation();}catch (Throwable ex) {// 抛异常后完成事务动作,只有满足回滚规则,才会回滚,否则即使抛异常,依然提交completeTransactionAfterThrowing(txInfo, ex);throw ex;}finally {// 清除本地线程中保存的当前事务的TransactionInfo信息cleanupTransactionInfo(txInfo);}// 执行事务提交commitTransactionAfterReturning(txInfo);return retVal;}else {Object result;final ThrowableHolder throwableHolder = new ThrowableHolder();// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.try {result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr, status -> {TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);try {return invocation.proceedWithInvocation();}catch (Throwable ex) {if (txAttr.rollbackOn(ex)) {// A RuntimeException: will lead to a rollback.if (ex instanceof RuntimeException) {throw (RuntimeException) ex;}else {throw new ThrowableHolderException(ex);}}else {// A normal return value: will lead to a commit.throwableHolder.throwable = ex;return null;}}finally {cleanupTransactionInfo(txInfo);}});}catch (ThrowableHolderException ex) {throw ex.getCause();}catch (TransactionSystemException ex2) {if (throwableHolder.throwable != null) {logger.error("Application exception overridden by commit exception", throwableHolder.throwable);ex2.initApplicationException(throwableHolder.throwable);}throw ex2;}catch (Throwable ex2) {if (throwableHolder.throwable != null) {logger.error("Application exception overridden by commit exception", throwableHolder.throwable);}throw ex2;}// Check result state: It might indicate a Throwable to rethrow.if (throwableHolder.throwable != null) {throw throwableHolder.throwable;}return result;}}protected void clearTransactionManagerCache() {this.transactionManagerCache.clear();this.beanFactory = null;}/*** 确定用于给定事务的特定事务管理器*/@Nullableprotected PlatformTransactionManager determineTransactionManager(@Nullable TransactionAttribute txAttr) {// Do not attempt to lookup tx manager if no tx attributes are setif (txAttr == null || this.beanFactory == null) {return getTransactionManager();}// 返回与此事务属性关联的限定符值String qualifier = txAttr.getQualifier();if (StringUtils.hasText(qualifier)) {return determineQualifiedTransactionManager(this.beanFactory, qualifier);}else if (StringUtils.hasText(this.transactionManagerBeanName)) {// transactionManagerBeanName默认为transactionManager// 如果是Jpa,则返回JpaTransactionManager;如果是普通jdbc,则返回JdbcTransactionManagerreturn determineQualifiedTransactionManager(this.beanFactory, this.transactionManagerBeanName);}else {PlatformTransactionManager defaultTransactionManager = getTransactionManager();if (defaultTransactionManager == null) {defaultTransactionManager = this.transactionManagerCache.get(DEFAULT_TRANSACTION_MANAGER_KEY);if (defaultTransactionManager == null) {defaultTransactionManager = this.beanFactory.getBean(PlatformTransactionManager.class);this.transactionManagerCache.putIfAbsent(DEFAULT_TRANSACTION_MANAGER_KEY, defaultTransactionManager);}}return defaultTransactionManager;}}/*** 从Spring IOC容器中查找qualifier或PlatformTransactionManager类型的bean。加入缓存*/private PlatformTransactionManager determineQualifiedTransactionManager(BeanFactory beanFactory, String qualifier) {PlatformTransactionManager txManager = this.transactionManagerCache.get(qualifier);if (txManager == null) {// 从Spring IOC容器中查找qualifier或PlatformTransactionManager类型的beantxManager = BeanFactoryAnnotationUtils.qualifiedBeanOfType(beanFactory, PlatformTransactionManager.class, qualifier);this.transactionManagerCache.putIfAbsent(qualifier, txManager);}return txManager;}/*** 获取方法限定名。格式:类名.方法名*/private String methodIdentification(Method method, @Nullable Class<?> targetClass,@Nullable TransactionAttribute txAttr) {String methodIdentification = methodIdentification(method, targetClass);if (methodIdentification == null) {if (txAttr instanceof DefaultTransactionAttribute) {methodIdentification = ((DefaultTransactionAttribute) txAttr).getDescriptor();}if (methodIdentification == null) {methodIdentification = ClassUtils.getQualifiedMethodName(method, targetClass);}}return methodIdentification;}@Nullableprotected String methodIdentification(Method method, @Nullable Class<?> targetClass) {return null;}/*** 如有必要,根据给定的TransactionAttribute创建事务*/@SuppressWarnings("serial")protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,@Nullable TransactionAttribute txAttr, final String joinpointIdentification) {// If no name specified, apply method identification as transaction name.// 如果未指定名称,则将方法标识应用为事务名称if (txAttr != null && txAttr.getName() == null) {txAttr = new DelegatingTransactionAttribute(txAttr) {@Overridepublic String getName() {return joinpointIdentification;}};}TransactionStatus status = null;if (txAttr != null) {if (tm != null) {status = tm.getTransaction(txAttr);}else {if (logger.isDebugEnabled()) {logger.debug("Skipping transactional joinpoint [" + joinpointIdentification +"] because no transaction manager has been configured");}}}// 准备TransactionInfo对象,并返回return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);}/*** 准备TransactionInfo对象*/protected TransactionInfo prepareTransactionInfo(@Nullable PlatformTransactionManager tm,@Nullable TransactionAttribute txAttr, String joinpointIdentification,@Nullable TransactionStatus status) {// 创建TransactionInfoTransactionInfo txInfo = new TransactionInfo(tm, txAttr, joinpointIdentification);if (txAttr != null) {if (logger.isTraceEnabled()) {logger.trace("Getting transaction for [" + txInfo.getJoinpointIdentification() + "]");}txInfo.newTransactionStatus(status);}else {if (logger.isTraceEnabled()) {logger.trace("No need to create transaction for [" + joinpointIdentification +"]: This method is not transactional.");}}// 将当前的TransactionAspectSupport绑定到线程本地变量txInfo.bindToThread();return txInfo;}/*** 执行事务提交*/protected void commitTransactionAfterReturning(@Nullable TransactionInfo txInfo) {if (txInfo != null && txInfo.getTransactionStatus() != null) {if (logger.isTraceEnabled()) {logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "]");}txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());}}/*** 执行回滚*/protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {if (txInfo != null && txInfo.getTransactionStatus() != null) {if (logger.isTraceEnabled()) {logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +"] after exception: " + ex);}// 如果满足回顾规则if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {try {// 进行事务回滚txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());}catch (TransactionSystemException ex2) {logger.error("Application exception overridden by rollback exception", ex);ex2.initApplicationException(ex);throw ex2;}catch (RuntimeException | Error ex2) {logger.error("Application exception overridden by rollback exception", ex);throw ex2;}}else {// We don't roll back on this exception.// Will still roll back if TransactionStatus.isRollbackOnly() is true.try {// 如果TransactionStatus.isRollbackOnly()为true,则仍将回滚txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());}catch (TransactionSystemException ex2) {logger.error("Application exception overridden by commit exception", ex);ex2.initApplicationException(ex);throw ex2;}catch (RuntimeException | Error ex2) {logger.error("Application exception overridden by commit exception", ex);throw ex2;}}}}protected void cleanupTransactionInfo(@Nullable TransactionInfo txInfo) {if (txInfo != null) {txInfo.restoreThreadLocalStatus();}}/*** 保存事务相关的信息类*/protected final class TransactionInfo {@Nullableprivate final PlatformTransactionManager transactionManager;@Nullableprivate final TransactionAttribute transactionAttribute;private final String joinpointIdentification;@Nullableprivate TransactionStatus transactionStatus;@Nullableprivate TransactionInfo oldTransactionInfo;public TransactionInfo(@Nullable PlatformTransactionManager transactionManager,@Nullable TransactionAttribute transactionAttribute, String joinpointIdentification) {this.transactionManager = transactionManager;this.transactionAttribute = transactionAttribute;this.joinpointIdentification = joinpointIdentification;}public PlatformTransactionManager getTransactionManager() {Assert.state(this.transactionManager != null, "No PlatformTransactionManager set");return this.transactionManager;}@Nullablepublic TransactionAttribute getTransactionAttribute() {return this.transactionAttribute;}public String getJoinpointIdentification() {return this.joinpointIdentification;}public void newTransactionStatus(@Nullable TransactionStatus status) {this.transactionStatus = status;}@Nullablepublic TransactionStatus getTransactionStatus() {return this.transactionStatus;}public boolean hasTransaction() {return (this.transactionStatus != null);}/*** 将当前的事务的TransactionInfo信息保存在本地线程变量中*/private void bindToThread() {this.oldTransactionInfo = transactionInfoHolder.get();transactionInfoHolder.set(this);}/*** 清除本地线程中保存的当前事务的TransactionInfo信息*/private void restoreThreadLocalStatus() {transactionInfoHolder.set(this.oldTransactionInfo);}@Overridepublic String toString() {return (this.transactionAttribute != null ? this.transactionAttribute.toString() : "No transaction");}}@FunctionalInterfaceprotected interface InvocationCallback {@NullableObject proceedWithInvocation() throws Throwable;}}

4.1 invokeWithinTransaction()

在invokeWithinTransaction()方法,主要执行如下:

4.1.1)执行getTransactionAttributeSource(),获取TransactionAttributeSource对象;

如果是Repository类,则TransactionAttributeSource为AnnotationTransactionAttributeSource的子类RepositoryAnnotationTransactionAttributeSource。Controller或Service等类中,为AnnotationTransactionAttributeSource对象。

4.1.2)执行TransactionAttributeSource的getTransactionAttribute()方法,获取TransactionAttribute对象;

在TransactionAttributeSource的getTransactionAttribute()方法中,会通过事务注解解析器,解析方法添加的org.springframework.transaction.annotation包或javax.transaction包下的@Transactional注解的信息,返回TransactionAttribute对象。如果没有添加@Transactional注解,则返回null。详见:【源码】SpringBoot事务注册原理-CSDN博客

4.1.3)执行determineTransactionManager(),从Spring IOC容器中查找名称为qualifier或PlatformTransactionManager类型的bean对象。对于JPA,此处默认返回JpaTransactionManager;

qualifier为@Transactional注解中的value属性。当一个项目中有多个数据源的时候,需要手动指定一个数据源。

4.1.4)执行methodIdentification(),获取方法限定名。默认格式:类名.方法名;

4.1.5)代码会执行第一个if分支,执行createTransactionIfNecessary()方法,根据给定的TransactionAttribute创建事务信息;

4.1.6)调用invocation.proceedWithInvocation(),执行ReflectiveMethodInvocation.proceed()方法,直动执行原方法,获取返回值;

4.1.7)如果4.1.6)抛异常,则执行completeTransactionAfterThrowing(),抛异常后完成事务动作,只有满足回滚规则,才会回滚,否则即使抛异常,依然提交。抛出异常,结束方法。

4.1.8)执行cleanupTransactionInfo(),清除本地线程中保存的当前事务的TransactionInfo信息。即使抛4.1.6)抛异常了,也会执行;

4.1.9)如果4.1.6)正常执行,执行commitTransactionAfterReturning(),提交事务;

4.2 createTransactionIfNecessary()

createTransactionIfNecessary()方法执行如下:

4.2.1)如果未指定名称,则将方法标识应用为事务名称;

4.2.2)执行PlatformTransactionManager.getTransaction()方法,根据事务的传播行为,返回一个TransactionStatus对象;

4.2.3)执行prepareTransactionInfo(),创建一个TransactionInfo对象,并将当前的TransactionAspectSupport绑定到线程本地变量;

4.3 completeTransactionAfterThrowing()

completeTransactionAfterThrowing()方法执行如下:

4.3.1)调用TransactionInfo.transactionAttribute.rollbackOn(ex),判断异常是否满足回滚规则。只有满足规则的,才会执行rollback()进行回滚;

默认的回滚规则实现在DefaultTransactionAttribute类中,异常必须是RuntimeException或Error才能回滚。

也可以通过@Transactional注解的rollbackFor、rollbackForClassName、noRollbackFor和noRollbackForClassName来设置回滚规则。

4.3.2)如果不满足回滚规则,则仍然执行commit(),进行事务提交;

AbstractPlatformTransactionManager

AbstractPlatformTransactionManager的核心代码如下:

package org.springframework.transaction.support;public abstract class AbstractPlatformTransactionManager implements PlatformTransactionManager, Serializable {/*** 获取一个TransactionStatus对象,此实现处理传播行为。*/@Overridepublic final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException {// 抽象方法。如JpaTransactionManager.doGetTransaction(),创建一个JpaTransactionObject对象Object transaction = doGetTransaction();// Cache debug flag to avoid repeated checks.boolean debugEnabled = logger.isDebugEnabled();if (definition == null) {// Use defaults if no transaction definition given.definition = new DefaultTransactionDefinition();}// 如果事务存在,则检测传播行为并返回if (isExistingTransaction(transaction)) {// Existing transaction found -> check propagation behavior to find out how to behave.// 找到现有事务->检查传播行为以了解行为方式return handleExistingTransaction(definition, transaction, debugEnabled);}// Check definition settings for new transaction.// 检查事务属性中的超时属性,默认为-1if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());}// No existing transaction found -> check propagation behavior to find out how to proceed.// 如果事务的传播型为为PROPAGATION_MANDATORY,则抛异常if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {throw new IllegalTransactionStateException("No existing transaction found for transaction marked with propagation 'mandatory'");}else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {// 挂起事务,此处返回的suspendedResources为nullSuspendedResourcesHolder suspendedResources = suspend(null);if (debugEnabled) {logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);}try {// 初始getTransactionSynchronization()为0,需要激活事务同步// SYNCHRONIZATION_NEVER为2,表示不激活事务同步boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);// 创建一个DefaultTransactionStatus对象DefaultTransactionStatus status = newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);// 开始事务doBegin(transaction, definition);// 准备同步,将事务相关信息保存到本地线程变量prepareSynchronization(status, definition);return status;}catch (RuntimeException | Error ex) {resume(null, suspendedResources);throw ex;}}else {// 传播行为为PROPAGATION_SUPPORTS PROPAGATION_NOT_SUPPORTED PROPAGATION_NEVER// Create "empty" transaction: no actual transaction, but potentially synchronization.if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {logger.warn("Custom isolation level specified but no actual transaction initiated; " +"isolation level will effectively be ignored: " + definition);}boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);}}protected final DefaultTransactionStatus prepareTransactionStatus(TransactionDefinition definition, @Nullable Object transaction, boolean newTransaction,boolean newSynchronization, boolean debug, @Nullable Object suspendedResources) {DefaultTransactionStatus status = newTransactionStatus(definition, transaction, newTransaction, newSynchronization, debug, suspendedResources);prepareSynchronization(status, definition);return status;}/*** 创建一个DefaultTransactionStatus对象*/protected DefaultTransactionStatus newTransactionStatus(TransactionDefinition definition, @Nullable Object transaction, boolean newTransaction,boolean newSynchronization, boolean debug, @Nullable Object suspendedResources) {// 初始actualNewSynchronization为trueboolean actualNewSynchronization = newSynchronization &&!TransactionSynchronizationManager.isSynchronizationActive();return new DefaultTransactionStatus(transaction, newTransaction, actualNewSynchronization,definition.isReadOnly(), debug, suspendedResources);}/*** 将事务相关信息保存到本地线程变量*/protected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) {if (status.isNewSynchronization()) {TransactionSynchronizationManager.setActualTransactionActive(status.hasTransaction());TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT ?definition.getIsolationLevel() : null);TransactionSynchronizationManager.setCurrentTransactionReadOnly(definition.isReadOnly());// 事务名称,默认为类名.方法名TransactionSynchronizationManager.setCurrentTransactionName(definition.getName());TransactionSynchronizationManager.initSynchronization();}}@Overridepublic final void commit(TransactionStatus status) throws TransactionException {if (status.isCompleted()) {throw new IllegalTransactionStateException("Transaction is already completed - do not call commit or rollback more than once per transaction");}DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;// 如果本地代码设置了回滚if (defStatus.isLocalRollbackOnly()) {if (defStatus.isDebug()) {logger.debug("Transactional code has requested rollback");}processRollback(defStatus, false);return;}// 全局事务被标记为仅回滚,但事务代码请求提交if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {if (defStatus.isDebug()) {logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");}processRollback(defStatus, true);return;}// 提交处理processCommit(defStatus);}private void processCommit(DefaultTransactionStatus status) throws TransactionException {try {boolean beforeCompletionInvoked = false;try {boolean unexpectedRollback = false;// 为提交做准备,在beforeCommit同步回调发生之前执行。空方法,可以扩展。在该方法中,如果抛异常,依然会导致回滚prepareForCommit(status);// 执行beforeCommit()和beforeCompletion()触发器triggerBeforeCommit(status);// 主要用于资源的回收,如本地线程变量回收等triggerBeforeCompletion(status);beforeCompletionInvoked = true;// 有保存点,释放保存点信息if (status.hasSavepoint()) {if (status.isDebug()) {logger.debug("Releasing transaction savepoint");}unexpectedRollback = status.isGlobalRollbackOnly();status.releaseHeldSavepoint();}// 提交事务else if (status.isNewTransaction()) {if (status.isDebug()) {logger.debug("Initiating transaction commit");}unexpectedRollback = status.isGlobalRollbackOnly();doCommit(status);}else if (isFailEarlyOnGlobalRollbackOnly()) {unexpectedRollback = status.isGlobalRollbackOnly();}// Throw UnexpectedRollbackException if we have a global rollback-only// marker but still didn't get a corresponding exception from commit.// 事务以静默方式回滚,因为它已标记为仅回滚if (unexpectedRollback) {throw new UnexpectedRollbackException("Transaction silently rolled back because it has been marked as rollback-only");}}catch (UnexpectedRollbackException ex) {// can only be caused by doCommittriggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);throw ex;}catch (TransactionException ex) {// can only be caused by doCommitif (isRollbackOnCommitFailure()) {doRollbackOnCommitException(status, ex);}else {triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);}throw ex;}catch (RuntimeException | Error ex) {if (!beforeCompletionInvoked) {triggerBeforeCompletion(status);}doRollbackOnCommitException(status, ex);throw ex;}// Trigger afterCommit callbacks, with an exception thrown there// propagated to callers but the transaction still considered as committed.try {triggerAfterCommit(status);}finally {triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);}}finally {cleanupAfterCompletion(status);}}@Overridepublic final void rollback(TransactionStatus status) throws TransactionException {if (status.isCompleted()) {throw new IllegalTransactionStateException("Transaction is already completed - do not call commit or rollback more than once per transaction");}DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;// 回滚处理processRollback(defStatus, false);}/*** 处理实际回滚*/private void processRollback(DefaultTransactionStatus status, boolean unexpected) {try {boolean unexpectedRollback = unexpected;try {// 执行beforeCompletion()触发器,回收资源等triggerBeforeCompletion(status);// 如果有保存点,则回滚到保存点。通过调用Connection.rollback(savepoint),回滚到保存点// 【JdbcTransactionObjectSupport.rollbackToSavepoint()】if (status.hasSavepoint()) {if (status.isDebug()) {logger.debug("Rolling back transaction to savepoint");}status.rollbackToHeldSavepoint();}else if (status.isNewTransaction()) {if (status.isDebug()) {logger.debug("Initiating transaction rollback");}// 执行回滚doRollback(status);}else {// Participating in larger transactionif (status.hasTransaction()) {if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {if (status.isDebug()) {logger.debug("Participating transaction failed - marking existing transaction as rollback-only");}doSetRollbackOnly(status);}else {if (status.isDebug()) {logger.debug("Participating transaction failed - letting transaction originator decide on rollback");}}}else {logger.debug("Should roll back transaction but cannot - no transaction available");}// Unexpected rollback only matters here if we're asked to fail earlyif (!isFailEarlyOnGlobalRollbackOnly()) {unexpectedRollback = false;}}}catch (RuntimeException | Error ex) {triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);throw ex;}triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);// Raise UnexpectedRollbackException if we had a global rollback-only markerif (unexpectedRollback) {throw new UnexpectedRollbackException("Transaction rolled back because it has been marked as rollback-only");}}finally {cleanupAfterCompletion(status);}}private void doRollbackOnCommitException(DefaultTransactionStatus status, Throwable ex) throws TransactionException {try {if (status.isNewTransaction()) {if (status.isDebug()) {logger.debug("Initiating transaction rollback after commit exception", ex);}doRollback(status);}else if (status.hasTransaction() && isGlobalRollbackOnParticipationFailure()) {if (status.isDebug()) {logger.debug("Marking existing transaction as rollback-only after commit exception", ex);}doSetRollbackOnly(status);}}catch (RuntimeException | Error rbex) {logger.error("Commit exception overridden by rollback exception", ex);triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);throw rbex;}triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);}protected abstract void doBegin(Object transaction, TransactionDefinition definition)throws TransactionException;// 省略其他,其中的触发器大部分是空方法,用于扩展}

该类的核心方法为getTransaction(),根据事务的传播行为,获取TransactionStatus对象。传播行为可以通过@Transactional的propagation属性进行设置。其中doBegin()在JPA中的实现为JpaTransactionManager。

JpaTransactionManager

JpaTransactionManager的核心源码如下:

package org.springframework.orm.jpa;public class JpaTransactionManager extends AbstractPlatformTransactionManagerimplements ResourceTransactionManager, BeanFactoryAware, InitializingBean {@Nullableprivate EntityManagerFactory entityManagerFactory;@Nullableprivate String persistenceUnitName;private final Map<String, Object> jpaPropertyMap = new HashMap<>();@Nullableprivate DataSource dataSource;private JpaDialect jpaDialect = new DefaultJpaDialect();// 省略其他/*** 获取datasource,如果使用第三方的数据源,则返回对应的数据源,如DruidDataSource* @return*/@Nullablepublic DataSource getDataSource() {return this.dataSource;}@Overridepublic void afterPropertiesSet() {if (getEntityManagerFactory() == null) {throw new IllegalArgumentException("'entityManagerFactory' or 'persistenceUnitName' is required");}if (getEntityManagerFactory() instanceof EntityManagerFactoryInfo) {EntityManagerFactoryInfo emfInfo = (EntityManagerFactoryInfo) getEntityManagerFactory();DataSource dataSource = emfInfo.getDataSource();if (dataSource != null) {setDataSource(dataSource);}JpaDialect jpaDialect = emfInfo.getJpaDialect();if (jpaDialect != null) {setJpaDialect(jpaDialect);}}}@Overridepublic Object getResourceFactory() {return obtainEntityManagerFactory();}/*** 创建一个JpaTransactionObject对象,并对象的EntityManagerHolder和ConnectionHolder*/@Overrideprotected Object doGetTransaction() {// JPA事务对象,表示EntityManagerHolder。被JpaTransactionManager用作事务对象JpaTransactionObject txObject = new JpaTransactionObject();// 设置是否允许保存点txObject.setSavepointAllowed(isNestedTransactionAllowed());// 从线程本地变量获取EntityManagerHolder。EntityManagerFactory和EntityManagerHolder绑定EntityManagerHolder emHolder = (EntityManagerHolder)TransactionSynchronizationManager.getResource(obtainEntityManagerFactory());if (emHolder != null) {if (logger.isDebugEnabled()) {logger.debug("Found thread-bound EntityManager [" + emHolder.getEntityManager() +"] for JPA transaction");}txObject.setEntityManagerHolder(emHolder, false);}// 获取数据库资源if (getDataSource() != null) {// 从线程本地变量获取EntityManagerHolderConnectionHolder conHolder = (ConnectionHolder)TransactionSynchronizationManager.getResource(getDataSource());txObject.setConnectionHolder(conHolder);}return txObject;}@Overrideprotected boolean isExistingTransaction(Object transaction) {return ((JpaTransactionObject) transaction).hasTransaction();}/**** @param transaction 通过doGetTransaction()创建的JpaTransactionObject对象* @param definition TransactionAspectSupport对象*/@Overrideprotected void doBegin(Object transaction, TransactionDefinition definition) {JpaTransactionObject txObject = (JpaTransactionObject) transaction;if (txObject.hasConnectionHolder() && !txObject.getConnectionHolder().isSynchronizedWithTransaction()) {throw new IllegalTransactionStateException("Pre-bound JDBC Connection found! JpaTransactionManager does not support " +"running within DataSourceTransactionManager if told to manage the DataSource itself. " +"It is recommended to use a single JpaTransactionManager for all transactions " +"on a single DataSource, no matter whether JPA or JDBC access.");}try {if (!txObject.hasEntityManagerHolder() ||txObject.getEntityManagerHolder().isSynchronizedWithTransaction()) {// 新创建一个EntityManager,返回SessionImpl对象EntityManager newEm = createEntityManagerForTransaction();if (logger.isDebugEnabled()) {logger.debug("Opened new EntityManager [" + newEm + "] for JPA transaction");}txObject.setEntityManagerHolder(new EntityManagerHolder(newEm), true);}// 获取SessionImpl对象EntityManager em = txObject.getEntityManagerHolder().getEntityManager();// Delegate to JpaDialect for actual transaction begin.final int timeoutToUse = determineTimeout(definition);// 在HibernateJpaDialect中,返回一个SessionTransactionData对象Object transactionData = getJpaDialect().beginTransaction(em,new DelegatingTransactionDefinition(definition) {@Overridepublic int getTimeout() {return timeoutToUse;}});txObject.setTransactionData(transactionData);// Register transaction timeout.if (timeoutToUse != TransactionDefinition.TIMEOUT_DEFAULT) {txObject.getEntityManagerHolder().setTimeoutInSeconds(timeoutToUse);}// Register the JPA EntityManager's JDBC Connection for the DataSource, if set.if (getDataSource() != null) {// 在HibernateJpaDialect中,返回HibernateConnectionHandle对象ConnectionHandle conHandle = getJpaDialect().getJdbcConnection(em, definition.isReadOnly());if (conHandle != null) {// 创建一个连接持有者ConnectionHolder conHolder = new ConnectionHolder(conHandle);if (timeoutToUse != TransactionDefinition.TIMEOUT_DEFAULT) {conHolder.setTimeoutInSeconds(timeoutToUse);}if (logger.isDebugEnabled()) {logger.debug("Exposing JPA transaction as JDBC [" + conHandle + "]");}// 资源添加到本地线程遍变量TransactionSynchronizationManager.bindResource(getDataSource(), conHolder);txObject.setConnectionHolder(conHolder);}else {if (logger.isDebugEnabled()) {logger.debug("Not exposing JPA transaction [" + em + "] as JDBC transaction because " +"JpaDialect [" + getJpaDialect() + "] does not support JDBC Connection retrieval");}}}// Bind the entity manager holder to the thread.// 将EntityManagerHolder保存到线程本地变量if (txObject.isNewEntityManagerHolder()) {TransactionSynchronizationManager.bindResource(obtainEntityManagerFactory(), txObject.getEntityManagerHolder());}// 设置事务同步标记txObject.getEntityManagerHolder().setSynchronizedWithTransaction(true);}catch (TransactionException ex) {closeEntityManagerAfterFailedBegin(txObject);throw ex;}catch (Throwable ex) {closeEntityManagerAfterFailedBegin(txObject);throw new CannotCreateTransactionException("Could not open JPA EntityManager for transaction", ex);}}/*** 返回SessionImpl对象*/protected EntityManager createEntityManagerForTransaction() {EntityManagerFactory emf = obtainEntityManagerFactory();if (emf instanceof EntityManagerFactoryInfo) {// emf为代理对象,执行如下方法时,会先执行// AbstractEntityManagerFactoryBean.ManagedEntityManagerFactoryInvocationHandler.invoke()方法,// 然后执行AbstractEntityManagerFactoryBean.getNativeEntityManagerFactory()方法emf = ((EntityManagerFactoryInfo) emf).getNativeEntityManagerFactory();}Map<String, Object> properties = getJpaPropertyMap();// EntityManagerFactory.createEntityManager() -> SessionFactoryImpl.createEntityManager()。返回一个SessionImplreturn (!CollectionUtils.isEmpty(properties) ?emf.createEntityManager(properties) : emf.createEntityManager());}@Overrideprotected void doCommit(DefaultTransactionStatus status) {JpaTransactionObject txObject = (JpaTransactionObject) status.getTransaction();if (status.isDebug()) {logger.debug("Committing JPA transaction on EntityManager [" +txObject.getEntityManagerHolder().getEntityManager() + "]");}try {// 获取当前事务的EntityTransaction对象EntityTransaction tx = txObject.getEntityManagerHolder().getEntityManager().getTransaction();// 执行提交tx.commit();}catch (RollbackException ex) {if (ex.getCause() instanceof RuntimeException) {DataAccessException dae = getJpaDialect().translateExceptionIfPossible((RuntimeException) ex.getCause());if (dae != null) {throw dae;}}throw new TransactionSystemException("Could not commit JPA transaction", ex);}catch (RuntimeException ex) {// Assumably failed to flush changes to database.throw DataAccessUtils.translateIfNecessary(ex, getJpaDialect());}}/*** 执行回滚*/@Overrideprotected void doRollback(DefaultTransactionStatus status) {JpaTransactionObject txObject = (JpaTransactionObject) status.getTransaction();if (status.isDebug()) {logger.debug("Rolling back JPA transaction on EntityManager [" +txObject.getEntityManagerHolder().getEntityManager() + "]");}try {// 获取事务对象EntityTransaction tx = txObject.getEntityManagerHolder().getEntityManager().getTransaction();// 如果当前事务是激活的if (tx.isActive()) {// 回滚当前事务,放弃此事务中发生的任何更改。如事务中打开的查询将在提交或回滚时关闭(如果尚未关闭)。tx.rollback();}}catch (PersistenceException ex) {throw new TransactionSystemException("Could not roll back JPA transaction", ex);}finally {if (!txObject.isNewEntityManagerHolder()) {// 清除EntityManager中所有挂起的操作txObject.getEntityManagerHolder().getEntityManager().clear();}}}/*** JPA事务对象,表示EntityManagerHolder。被JpaTransactionManager用作事务对象。*/private class JpaTransactionObject extends JdbcTransactionObjectSupport {@Nullableprivate EntityManagerHolder entityManagerHolder;private boolean newEntityManagerHolder;@Nullableprivate Object transactionData;public void setEntityManagerHolder(@Nullable EntityManagerHolder entityManagerHolder, boolean newEntityManagerHolder) {this.entityManagerHolder = entityManagerHolder;this.newEntityManagerHolder = newEntityManagerHolder;}public EntityManagerHolder getEntityManagerHolder() {Assert.state(this.entityManagerHolder != null, "No EntityManagerHolder available");return this.entityManagerHolder;}public boolean hasEntityManagerHolder() {return (this.entityManagerHolder != null);}public boolean isNewEntityManagerHolder() {return this.newEntityManagerHolder;}public boolean hasTransaction() {return (this.entityManagerHolder != null && this.entityManagerHolder.isTransactionActive());}public void setTransactionData(@Nullable Object transactionData) {this.transactionData = transactionData;getEntityManagerHolder().setTransactionActive(true);if (transactionData instanceof SavepointManager) {getEntityManagerHolder().setSavepointManager((SavepointManager) transactionData);}}@Nullablepublic Object getTransactionData() {return this.transactionData;}public void setRollbackOnly() {EntityTransaction tx = getEntityManagerHolder().getEntityManager().getTransaction();if (tx.isActive()) {tx.setRollbackOnly();}if (hasConnectionHolder()) {getConnectionHolder().setRollbackOnly();}}@Overridepublic boolean isRollbackOnly() {EntityTransaction tx = getEntityManagerHolder().getEntityManager().getTransaction();return tx.getRollbackOnly();}@Overridepublic void flush() {try {getEntityManagerHolder().getEntityManager().flush();}catch (RuntimeException ex) {throw DataAccessUtils.translateIfNecessary(ex, getJpaDialect());}}@Overridepublic Object createSavepoint() throws TransactionException {if (getEntityManagerHolder().isRollbackOnly()) {throw new CannotCreateTransactionException("Cannot create savepoint for transaction which is already marked as rollback-only");}return getSavepointManager().createSavepoint();}@Overridepublic void rollbackToSavepoint(Object savepoint) throws TransactionException {getSavepointManager().rollbackToSavepoint(savepoint);getEntityManagerHolder().resetRollbackOnly();}@Overridepublic void releaseSavepoint(Object savepoint) throws TransactionException {getSavepointManager().releaseSavepoint(savepoint);}private SavepointManager getSavepointManager() {if (!isSavepointAllowed()) {throw new NestedTransactionNotSupportedException("Transaction manager does not allow nested transactions");}SavepointManager savepointManager = getEntityManagerHolder().getSavepointManager();if (savepointManager == null) {throw new NestedTransactionNotSupportedException("JpaDialect does not support savepoints - check your JPA provider's capabilities");}return savepointManager;}}}

在JpaTransactionManager类中,通过Connection执行真正的数据库相关操作,实现事务的开启、事务提交以及事务回滚。

小结

限于篇幅,本篇先分享到这里。以下做一个小结:

1)bean中的方法添加@Transactional注解,会生成一个代理类,且添加TransactionInterceptor拦截器;

2)当方法调用时,执行TransactionInterceptor.invoke()方法,该方法调用父类TransactionAspectSupport.invokeWithinTransaction()方法;

2.1)解析原方法的@Transactional注解信息,封装为TransactionAttribute对象;

2.2)从Spring容器中获取JpaTransactionManager对象;

2.3)开启事务;

2.4)在try中执行ReflectiveMethodInvocation.proceed()方法,直动执行原方法,获取返回值;

2.5)在catch中捕获异常,如果出现异常,执行completeTransactionAfterThrowing(),对满足回滚规则的,执行回滚;如果不满足回滚规则,依然提交事务,并抛出异常,结束方法;

2.6)如果没有异常,提交事务;

关于本篇内容你有什么自己的想法或独到见解,欢迎在评论区一起交流探讨下吧。

相关文章:

【源码】Spring Data JPA原理解析之事务执行原理

Spring Data JPA系列 1、SpringBoot集成JPA及基本使用 2、Spring Data JPA Criteria查询、部分字段查询 3、Spring Data JPA数据批量插入、批量更新真的用对了吗 4、Spring Data JPA的一对一、LazyInitializationException异常、一对多、多对多操作 5、Spring Data JPA自定…...

第十一篇——信息增量:信息压缩中的保守主义原则

目录 一、背景介绍二、思路&方案三、过程1.思维导图2.文章中经典的句子理解3.学习之后对于投资市场的理解4.通过这篇文章结合我知道的东西我能想到什么&#xff1f; 四、总结五、升华 一、背景介绍 通过信息中的保守主义&#xff0c;我想到了现实中人的保守主义一样&#…...

中国飞行器设计创新大赛多旋翼无人机任务飞行

源码&#xff1a;后续补充 1、启动launch文件 roslaunch robot_bringup mission.launch <launch> <!--启动mavros节点 --><include file"$(find mavros)/launch/px4.launch" /><!--启动USB摄像头节点 --><include file"$(find…...

WPF-UI布局

WPF布局元素有如下几个&#xff1a; Grid&#xff1a;网格。可以自定义行和列并通过行列的数量、行高和列宽来调整控件的布局。StackPanel&#xff1a;栈式面板。可将包含的元素在竖直或水平方向上排成一条直线&#xff0c;当移除一个元素后&#xff0c;后面的元素会自动向前移…...

武忠祥17堂课没必要全听,这几个才是精华!

作者&#xff1a;Captain 链接&#xff1a;https://www.zhihu.com/question/381665751/answer/3197724055 来源&#xff1a;知乎 著作权归作者所有。商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处。 17堂课类似于习题课&#xff0c;是专题训练 17堂课省略了…...

Android 蓝牙profile的配置

在做BQB认证之前&#xff0c;我们需要确认那些profile之前&#xff0c;我们需要查看profile的配置情况 Android13版本前蓝牙profile的配置 MTK的项目代码如下 vendor\mediatek\proprietary\packages\modules\Bluetooth\android\app\res\values\config.xml 高通的项目代码如…...

Selenium时间等待_显示等待

特点&#xff1a; 针对具体元素进行时间等待 可以自定义等待时长和间隔时间 按照设定的时间&#xff0c;不断定位元素&#xff0c;定位到了直接执行下一步操作 如在设定时间内没定位到元素&#xff0c;则报错&#xff08;TimeOutException&#xff09; 显示等待概念&#x…...

41 mysql subquery 的实现

前言 sub query 是一个我们经常会使用到的一个 用法 我们这里 看一看各个场景下面的 sub query 的相关处理 查看 本文, 需要 先看一下 join 的相关处理 测试数据表如下, 两张测试表, tz_test, tz_test03, 表结构 一致 CREATE TABLE tz_test (id int(11) unsigned NOT NUL…...

钉钉二次开发-企业内部系统集成官方OA审批流程(三)

书接上回&#xff0c;本文主要分享 企业内部系统集成钉钉官方OA审批流程的步骤 的第二部分。 前端代码集成钉钉免登JSAPI: 前端通过corpid 获得钉钉临时访问码code&#xff0c;再通过临时访问码code调用此接口返回当前用户的姓名、userid、 钉钉用户id、 系统工号、 钉钉部门…...

代码随想录算法训练营第五十四 | ● 392.判断子序列 ● 115.不同的子序列

392.判断子序列 https://programmercarl.com/0392.%E5%88%A4%E6%96%AD%E5%AD%90%E5%BA%8F%E5%88%97.html class Solution { public:bool isSubsequence(string s, string t) {if(s.size()0 )return true;if(t.size()0)return false;vector<vector<int>> dp(s.size(…...

C++设计模式-外观模式,游戏引擎管理多个子系统,反汇编

运行在VS2022&#xff0c;x86&#xff0c;Debug下。 30. 外观模式 为子系统定义一组统一的接口&#xff0c;这个高级接口会让子系统更容易被使用。应用&#xff1a;如在游戏开发中&#xff0c;游戏引擎包含多个子系统&#xff0c;如物理、渲染、粒子、UI、音频等。可以使用外观…...

嵌入式软件测试相关分析

嵌入式软件测试相关分析 1. 引言 在软件发展之初&#xff0c;上个世纪五六十年代&#xff0c;软件被视为数学领域&#xff0c;编程是为了进行数学计算&#xff0c;由数学公式推导&#xff0c;来写函数。因此&#xff0c;在那个时候所编写的程序是被视为数学问题&#xff0c;数…...

vue+jave实现文件报表增加文件下载功能

需求背景:系统有文件交互功能。但没有做页面展示。为了测试方便&#xff0c;写了报表展示并可下载文件做检查。(所以下载是依赖表数据的) 使用语言和框架: 前端:vue-cli 后端:springBoot 前端实现 1、在报表vue文件&#xff0c;显示下载按钮并实现下载接口请求和处理。 //报…...

网站安全性评估方法

评估一个网站的安全性是一个多方面的过程&#xff0c;涉及到对网站的技术架构、代码质量、数据处理、用户交互等多个维度的考察。以下是一些常用的评估方法&#xff1a; 1.了解常见的安全风险&#xff1a;包括恶意软件、钓鱼攻击、跨站脚本攻击等&#xff0c;这些都是网站可能…...

【小程序】WXML模板语法

目录 数据绑定 数据绑定的基本原则 在data中定义页面的数据 Mustache语法的格式 Mustache语法的应用场景 事件绑定 什么是事件 小程序中常用的事件 事件对象的属性列表 target和currentTarget的区别 bindtap的语法格式 在事件处理函数中为data中的数据赋值 事件…...

[数据集][目标检测]厨房积水检测数据集VOC+YOLO格式88张2类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;88 标注数量(xml文件个数)&#xff1a;88 标注数量(txt文件个数)&#xff1a;88 标注类别数…...

QSlider样式示例

参考代码&#xff1a; /********************QSlider横向滑动条样式**********************/ QSlider {background-color: rgba(170, 255, 255, 100); /* 设置滑动条主体*/ }QSlider::groove:horizontal {border: 1px solid #999999;height: 8px; /* 默认…...

【Linux】进程3——PID/PPID,父进程,子进程

在讲父子进程之前&#xff0c;我们接着上面那篇继续讲 1.查看进程 mycode.c makefile 我们在zs_108直接编译mycode.c&#xff0c;直接运行&#xff0c;然后我们转换另一个账号来查看这个进程 我们可以通过ps指令来查看进程 我们就会好奇了&#xff0c;第二行是什么&#xff…...

开发常用的组件库

框架&#xff1a; Vue.js - 渐进式 JavaScript 框架 | Vue.js (vuejs.org) React 官方中文文档 (docschina.org) Svelte 中文文档 | Svelte 中文网 SolidJS 反应式 JavaScript 库 页面样式&#xff1a; 网页端&#xff1a; 指南 |元素 (eleme.cn) Mint UI (mint-ui.github.io…...

深度解析地铁票务系统的技术架构与创新应用

在城市交通体系中&#xff0c;地铁作为一种快速、便捷的公共交通方式&#xff0c;已经成为现代都市生活的重要组成部分。而地铁票务系统的技术架构&#xff0c;则是支撑地铁运营的核心之一。本文将深度解析地铁票务系统的技术架构与创新应用&#xff0c;从系统设计、数据管理、…...

Python集合的基本概念和使用方法

目录 集合&#xff08;Set&#xff09; 基本概念 基本特性 基本操作 集合运算 成员测试 高级操作 集合推导式 总结 集合&#xff08;Set&#xff09; Python集合&#xff08;Set&#xff09;是Python语言中一个非常实用且强大的数据结构&#xff0c;它用于存储多个不…...

谷歌浏览器124版本Webdriver驱动下载

查看谷歌浏览器版本 在浏览器的地址栏输入&#xff1a; chrome://version/回车后即可查看到对应版本(不要点击帮助-关于Google chrome&#xff0c;因为点击后会自动更新谷歌版本) 114之前版本&#xff1a;下载链接 ​​​​​​123以后版本&#xff1a;下载链接&#xff0…...

十大排序

本文将以「 通俗易懂」的方式来描述排序的基本实现。 &#x1f9d1;‍&#x1f4bb;阅读本文前&#xff0c;需要一点点编程基础和一点点数据结构知识 本文的所有代码以cpp实现 文章目录 排序的定义 插入排序 ⭐ &#x1f9d0;算法描述 &#x1f496;具体实现 &#x1f…...

微信小程序学习笔记(1)

文章目录 一、文件作用app.json&#xff1a;project.config.json:sitemap.json页面中.json 二、项目首页三、语法**WXML**和**HTML**WXSS 和CSS的区别小程序中.js文件的分类 一、文件作用 app.json&#xff1a; 当前小程序的全局配置&#xff0c;包括所有页面路径、窗口外观、…...

OpenGauss数据库-6.表空间管理

第1关&#xff1a;创建表空间 gsql -d postgres -U gaussdb -W passwd123123 CREATE TABLESPACE fastspace OWNER omm relative location tablespace/tablespace_1; 第2关&#xff1a;修改表空间 gsql -d postgres -U gaussdb -W passwd123123 ALTER TABLESPACE fastspace R…...

相约乌镇 续写网络空间命运与共的新篇章(二)

从乌镇峰会升级为世界互联网大会&#xff0c;既是展示互联网发展成果的技术盛会&#xff0c;也是尖端科技综合运用的宏大场景。从枕水江南散发出的“互联网之光”&#xff0c;到前沿技术的创新突破和场景应用&#xff0c;澎湃的是数字经济浪潮&#xff0c;激荡的是科技创新能量…...

【全网最简单的解决办法】vscode中点击运行出现仅当从 VS 开发人员命令提示符处运行 VS Code 时,cl.exe 生成和调试才可用

首先确保你是否下载好了gcc编译器&#xff01;&#xff01;&#xff01; 检测方法&#xff1a; winR 打开cmd命令窗 输入where gcc(如果出现路径则说明gcc配置好啦&#xff01;) where gcc 然后打开我们的vscode 把这个文件删除掉 再次点击运行代码&#xff0c;第一个出现…...

NFS共享存储服务

NFS共享存储服务 NFS&#xff1a;network file system ,在计算机网络中共享文件系统的协议。 计算机之间可以通过网络共享目录和文件&#xff0c;分为两个部分&#xff1a; 1、rpcbind&#xff1a;远程共享调用 2、nfs&#xff1a;共享服务&#xff0c;端口号&#xff1a;2…...

Docker面试整理-Docker 常用命令

Docker 提供了一套丰富的命令行工具,使得用户能够管理容器、镜像、网络和卷等资源。这里列出了一些常用的 Docker 命令: 镜像相关命令:docker pull [OPTIONS] NAME[:TAG|@DIGEST]: 从仓库拉取一个镜像或仓库。docker push NAME[:TAG]: 推送一个镜像或仓库到远程仓库。docker …...

Cinema 4D 2024 软件安装教程、附安装包下载

Cinema 4D 2024 Cinema 4D&#xff08;C4D&#xff09;是一款由Maxon开发的三维建模、动画和渲染软件&#xff0c;广泛用于电影制作、广告、游戏开发、视觉效果等领域。Cinema 4D允许用户创建复杂的三维模型&#xff0c;包括角色、场景、物体等。它提供了多种建模工具&#x…...