【源码】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)如果没有异常,提交事务;

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/851274.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python 虚拟环境 + 嵌入式 部署方案

Python 虚拟环境 嵌入式 部署方案 开发阶段1. 在虚拟环境下开发 Python 项目 部署阶段1. 创建项目文件夹2. 准备嵌入器 Python 解释器3. 处理第三方库4. 修改 ._pth 文件添加 Python 运行环境 5. 添加启动 bat 脚本最终目录结构参考资料 开发阶段 1. 在虚拟环境下开发 Python…

PHP 寿光蔬菜大棚宣传平台-计算机毕业设计源码88288

摘 要 随着科学技术的飞速发展&#xff0c;各行各业都在努力与现代先进技术接轨&#xff0c;通过科技手段提高自身的优势&#xff1b;对于寿光蔬菜大棚宣传平台当然也不能排除在外&#xff0c;随着网络技术的不断成熟&#xff0c;带动了寿光蔬菜大棚宣传平台&#xff0c;它彻底…

ISO 19115-2:2019 附录C XML 模式实现

C.1 XML 模式 本文件中定义的 UML 模型的 XML 模式在 ISO/TS 19115-3 中定义的适当 XML 命名空间中提供。新增内容包括: 命名空间前缀模式文件名Metadata for ACquisition (mac)acquisitionInformationImagery.xsdMetadata for Resource Content (mrc)contentInformationImag…

BPF:BCC(BPF Compiler Collection)工具集认知

写在前面 博文内容为 《BPF Performance Tools》 读书笔记整理内容涉及 BCC 工具整体介绍理解不足小伙伴帮忙指正 &#x1f603;,生活加油 不必太纠结于当下&#xff0c;也不必太忧虑未来&#xff0c;当你经历过一些事情的时候&#xff0c;眼前的风景已经和从前不一样了。——村…

内存EDAC在AMD异构场景的应用

在异构系统中&#xff0c;AMD通过将CPU与GPU的数据织物网络通过定制的xGMI链接连接&#xff0c;实现了统一访问&#xff0c;使得GPU节点的数据织物网络如同CPU节点一样可访问。这样的设计使得系统能够高效地整合CPU与GPU资源&#xff0c;提升数据处理速度和效率&#xff0c;特别…

从河流到空气,BL340工控机助力全面环保监测网络构建

在环保监测领域&#xff0c;智能化、高效率的监测手段正逐步成为守护绿水青山的新常态。其中&#xff0c;ARMxy工业计算机BL340凭借其强大的处理能力、高度的灵活性以及广泛的兼容性&#xff0c;在水质监测站、空气质量检测、噪音污染监控等多个环保应用场景中脱颖而出&#xf…

MySQL深分页优化

MySQL中的深分页问题通常是指当我们通过LIMIT语句查询数据&#xff0c;尤其是在翻到较后面的页码时&#xff0c;性能会急剧下降。例如&#xff0c;查询第1000页的数据&#xff0c;每页10条&#xff0c;系统需要跳过前9990条数据&#xff0c;然后才能获取到所需的记录&#xff0…

Vue基础面试题(一)

1.Vue的基本原理 Vue.js的核心原理在于其响应式的数据绑定机制&#xff0c;当创建一个Vue实例时&#xff0c;Vue会遍历每个属性&#xff0c;用Object.defineProperty转化为getter和setter。这样使得Vue可以追踪属性的变化&#xff0c;在属性被修改和访问时通知变化。每个组件也…

PostgreSQL的视图pg_tables

PostgreSQL的视图pg_tables pg_tables 是 PostgreSQL 中的一个系统视图&#xff0c;用于显示当前数据库中所有用户定义的表的信息。这个视图提供了关于表的名称、所属模式&#xff08;schema&#xff09;、所有者以及表类型等详细信息。 pg_tables 视图的主要列 列名类型描述…

如何提高个人和企业的网络安全意识?

提高个人网络安全意识 个人可以通过以下方式提高网络安全意识&#xff1a; 1. 加强教育和培训&#xff1a;参加网络安全培训课程&#xff0c;学习识别网络攻击的常见迹象、安全密码的设置、不点击可疑链接等知识。 2. 建立网络安全政策&#xff1a;制定个人网络安全规则&…

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

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

基于JavaScript 实现近邻算法以及优化方案

前言 近邻算法&#xff08;K-Nearest Neighbors&#xff0c;简称 KNN&#xff09;是一种简单的、广泛使用的分类和回归算法。它的基本思想是&#xff1a;给定一个待分类的样本&#xff0c;找到这个样本在特征空间中距离最近的 k 个样本&#xff0c;这 k 个样本的多数类别作为待…

【C++】<知识点> C++11新特性

文章目录 一、auto关键字 二、decltype关键字 三、nullptr关键字 四、智能指针 五、 无序容器&#xff08;哈希表&#xff09; 六、统一的初始化方法 七、成员变量默认初始值 八、范围for循环 九、右值引用与移动语义 十、lambda表达式 一、auto关键字 1. 作用&#…

Linux shell编程学习笔记58:cat /proc/mem 获取系统内存信息

0 前言 在开展系统安全检查的过程中&#xff0c;除了收集cpu信息&#xff0c;我们还需要收集内存信息。在Linux中&#xff0c;获取内存信息的命令很多&#xff0c;这里我们着重研究 cat /proc/mem命令。 1 cat /proc/mem命令 /proc/meminfo 文件提供了有关系统内存的使用情况…

280 基于matlab的摇号系统GUI界面仿真MATLAB程序

基于matlab的摇号系统GUI界面仿真MATLAB程序&#xff0c;输入总数量及摇号需求&#xff0c;进行随机性摇号&#xff0c;并对摇取的号码进行双重随机性数据检测&#xff0c;确定是否符合要求。程序已调通&#xff0c;可直接运行。 280 GUI人机交互 摇号系统GUI界面仿真 - 小红书…

技术前沿 |【大模型InstructBLIP进行指令微调】

大模型InstructBLIP进行指令微调 一、引言二、InstructBLIP模型介绍三、指令微调训练通用视觉语言模型的应用潜力四、InstructBLIP的指令微调训练步骤五、实验结果与讨论六、结论与展望 一、引言 随着人工智能技术的快速发展&#xff0c;视觉语言模型&#xff08;Vision-Langu…

使用SourceTree切换不同的托管平台

背景&#xff1a;sourcetree一开始绑定了gitee&#xff0c;想拉取github的项目时拉取不了 原因&#xff1a;git绑定的账号&#xff08;邮箱&#xff09;、密码不一致 解决办法&#xff1a; 重新设置账号密码 在windows种可找到下面的文件夹&#xff0c;进行删除 C:\Users\US…

5.1 实体完整性

一个表只能有一个主键约束&#xff0c;且主键约束不能取空值。 通过unique约束定义唯一性&#xff0c;为了保证一个表非主键列不输入重复值&#xff0c;可在该列定义unique约束。 primary key约束与unique约束主要区别如下。 (1)一个表只能创建一个primary key约束&#xff0…

让GNSSRTK不再难【第一天】

第1讲 GNSS系统组成以及应用 北斗导航科普动画_哔哩哔哩_bilibili 1.1 GNSS系统 1.1.1 基本概念 全球卫星导航系统&#xff08;Global Navigation Satellite System, GNSS&#xff09;&#xff0c;是能在地球表面或近地空间的任何地点为用户提供全天候的三维坐标、速度以及…

STM32-电灯,仿真

目录 前言: 一. 配置vscode 二. 新创建软件工程 三. 仿真 1.新建工程想到,选择名称和路径 2.从选中的模板创建原理图 3.不创建PCB布版设计 4.选择没有固件项目 5.完成 四.源码 五. 运行效果 六. 总结 前言: 这篇主要是配置vscode和创建仿真,和点灯的完整代码,欢迎大…