纯静态做企业网站,php网站做代理服务器,wordpress文章重复,宁国网络推广原创#xff1a;小姐姐味道(微信公众号ID#xff1a;xjjdog)#xff0c;欢迎分享#xff0c;转载请保留出处。Spring有5种隔离级别#xff0c;7种传播行为。这是面试常问的内容#xff0c;也是代码中经常碰到的知识点。这些知识枯燥而且乏味#xff0c;其中有些非常的绕… 原创小姐姐味道(微信公众号IDxjjdog)欢迎分享转载请保留出处。Spring有5种隔离级别7种传播行为。这是面试常问的内容也是代码中经常碰到的知识点。这些知识枯燥而且乏味其中有些非常的绕。如果栽在这上面就实在是太可惜了。xjjdog在一些事务的基础上再探讨几个容易淡忘的概念从源码层面找原因加深我们的理解问题大概包括Spring的事务和数据库的事务隔离是一个概念么Spring是如何实现事务的事务隔离机制都有哪些事务传播机制都有哪些查询语句需要开事务么private方法加事务注解有用么1、Spring的事务和数据库的事务隔离是一个概念么先来第一个问题Spring的事务隔离级别和数据的事务隔离级别是一回事么其实数据库一般只有4种隔离机制Spring抽象出一种default根据数据设置来变动。read uncommitted(未提交读)read committed(提交读、不可重复读)repeatable read(可重复读)serializable(可串行化)default (PlatformTransactionManager默认的隔离级别使用的就是数据库默认的)这是因为Spring只提供统一事务管理接口具体实现都是由各数据库自己实现(如MySQL)。Spring会在事务开始时根据当前环境中设置的隔离级别调整数据库隔离级别由此保持一致。在DataSourceUtils文件中代码详细的输出了这个过程。// Apply specific isolation level, if any.Integer previousIsolationLevel null;if (definition ! null definition.getIsolationLevel() ! TransactionDefinition.ISOLATION_DEFAULT) {if (logger.isDebugEnabled()) { logger.debug(Changing isolation level of JDBC Connection [ con ] to definition.getIsolationLevel()); }int currentIsolation con.getTransactionIsolation();if (currentIsolation ! definition.getIsolationLevel()) { previousIsolationLevel currentIsolation; con.setTransactionIsolation(definition.getIsolationLevel()); }}结论三种情况如果Spring没有指定事务隔离级别则会采用数据库默认的事务隔离级别当Spring指定了事务隔离级别则会在代码里将事务隔离级别修改为指定值当数据库不支持这种隔离级别效果则以数据库的为准(比如采用了MyISAM引擎)。我们会使用如下的方式进行声明。如果不是有性能和需求问题就不要瞎改。事务处理弄不好是会锁表的而锁表在大并发的情况下是会死人的。Transactional(isolation Isolation.READ_UNCOMMITTED)2、Spring事务的7种传播机制只要写代码代码总会存在嵌套或者循环造成了事务的嵌套或者循环。那么事务在这些情况下根据配置会有不同的反应。REQUIRED 这是默认的。表示当前方法必须在一个具有事务的上下文中运行如有客户端有事务在进行那么被调用端将在该事务中运行否则的话重新开启一个事务。(如果被调用端发生异常那么调用端和被调用端事务都将回滚)REQUIRE_NEW 表示当前方法必须运行在它自己的事务中。如果存在当前事务在该方法执行期间当前事务会被挂起NESTED 如果当前方法正有一个事务在运行中则该方法应该运行在一个嵌套事务中被嵌套的事务可以独立于被封装的事务中进行提交或者回滚。如果封装事务存在并且外层事务抛出异常回滚那么内层事务必须回滚反之内层事务并不影响外层事务。如果封装事务不存在则同required的一样SUPPORTS 表示当前方法不必需要具有一个事务上下文但是如果有一个事务的话它也可以在这个事务中运行NOT_SUPPORTED 表示该方法不应该在一个事务中运行。如果有一个事务正在运行他将在运行期被挂起直到这个事务提交或者回滚才恢复执行MANDATORY 表示当前方法必须在一个事务中运行如果没有事务将抛出异常NEVER 表示当方法务不应该在一个事务中运行如果存在一个事务则抛出异常一般用得比较多的是REQUIRED REQUIRES_NEW用到其他的你就要小心了搞懂再用。最怕如果这俩字了它会将事情搞的很复杂尤其是代码量大的时候你永远不知道你写的service会被谁用到。这就很尴尬了。我们会采用下面的方式进行声明。鉴于Spring的事务传播非常的绕如果功能满足需求那么就用默认的就好否则会引起不必要的麻烦。Transactional(propagationPropagation.REQUIRED)3、事务传播机制是怎么实现的事务传播机制看似神奇实际上是使用简单的ThreadLocal的机制实现的。所以如果调用的方法是在新线程调用的事务传播实际上是会失效的。这不同于我们以前讲到的透传Spring并没有做这样的处理。参考你的也是我的。3例ko多线程局部变量透传所以事务传播机制只有翻一遍源代码才能印象深刻。仅靠文字去传播很多东西会变得不可描述。如图PlatformTransactionManager只有简单的三个抽象接口定义了包含JDBC在内的Spring所有的事务操作。我们平常说的JDBC只是占到其中一部分。实现的方式依然是使用AOP来实现的具体的实现类是由TransactionAspectSupport来实现的。可以看到代码定义了一个叫做transactionInfoHolder的ThreadLocal变量当用到它的时候就能够确保在同一个线程下获取的变量是一致的。/** * Holder to support the {code currentTransactionStatus()} method, * and to support communication between different cooperating advices * (e.g. before and after advice) if the aspect involves more than a * single method (as will be the case for around advice).*/private static final ThreadLocal transactionInfoHolder new NamedThreadLocal(Current aspect-driven transaction);具体的业务逻辑是在invokeWithinTransaction中实现的。如果你继续向下跟踪的话会找到AbstractPlatformTransactionManager类中的getTransaction方法。Overridepublic final TransactionStatus getTransaction(Nullable TransactionDefinition definition)throws TransactionException {// Use defaults if no transaction definition given. TransactionDefinition def (definition ! null ? definition : TransactionDefinition.withDefaults()); Object transaction doGetTransaction();boolean debugEnabled logger.isDebugEnabled();if (isExistingTransaction(transaction)) {// Existing transaction found - check propagation behavior to find out how to behave.return handleExistingTransaction(def, transaction, debugEnabled); }// Check definition settings for new transaction.if (def.getTimeout() TransactionDefinition.TIMEOUT_DEFAULT) {throw new InvalidTimeoutException(Invalid transaction timeout, def.getTimeout()); }// No existing transaction found - check propagation behavior to find out how to proceed.if (def.getPropagationBehavior() TransactionDefinition.PROPAGATION_MANDATORY) {throw new IllegalTransactionStateException(No existing transaction found for transaction marked with propagation mandatory); }else if (def.getPropagationBehavior() TransactionDefinition.PROPAGATION_REQUIRED || def.getPropagationBehavior() TransactionDefinition.PROPAGATION_REQUIRES_NEW || def.getPropagationBehavior() TransactionDefinition.PROPAGATION_NESTED) { SuspendedResourcesHolder suspendedResources suspend(null);if (debugEnabled) { logger.debug(Creating new transaction with name [ def.getName() ]: def); }try {return startTransaction(def, transaction, debugEnabled, suspendedResources); }catch (RuntimeException | Error ex) { resume(null, suspendedResources);throw ex; } }else {// Create empty transaction: no actual transaction, but potentially synchronization.if (def.getIsolationLevel() ! TransactionDefinition.ISOLATION_DEFAULT logger.isWarnEnabled()) { logger.warn(Custom isolation level specified but no actual transaction initiated; isolation level will effectively be ignored: def); }boolean newSynchronization (getTransactionSynchronization() SYNCHRONIZATION_ALWAYS);return prepareTransactionStatus(def, null, true, newSynchronization, debugEnabled, null); }}不用我做过多解释了吧一切明显的逻辑都在代码里。事务就是在这里创建的。4、查询方法可以不开启事务么事务有个readonly控制了事务的只读属性和事务是否开启没半毛钱关系。在以前的一篇文章中谈到通过设置readonly属性来控制语句的路由”MySQL官方驱动“主从分离的神秘面纱(扫盲篇)这其中就用到了事务的其中一个属性readonly它最终是体现在数据库连接层面的。connection.setReadOnly(true);在Spring中的使用方式如下Transactional(readOnlytrue)值得注意的是这个属性设置之后并不是每个底层的数据库都支持。中间层的ORM或者驱动也可能会拿这个属性做一些文章所以与其说这个readonly是功能性的不如说是一种暗示。拿MySQL来说有两种提交模式SET AUTOCOMMIT0 禁止自动提交SET AUTOCOMMIT1 开启自动提交这都是实打实的SQL语句所以如果开启了事务AUTOCOMMIT要为false。我们可以看到Spring做了以下几个操作。con.setAutoCommit(false);如果是只读事务还不忘手动设置一下。if (isEnforceReadOnly() definition.isReadOnly()) { try (Statement stmt con.createStatement()) { stmt.executeUpdate(SET TRANSACTION READ ONLY); }}这种操作是很昂贵的如果不加Transaction注解默认是不开启事务的。单条的查询语句也是没有必要开启事务的数据库默认的配置就能满足需求。但如果你一次执行多条查询语句例如统计查询报表查询在这种场景下多条查询SQL必须保证整体的读一致性否则在前条SQL查询之后后条SQL查询之前数据被其他用户改变就会造成数据的前后不一。也仅有在这种情况下要开启读事务。5、private方法加事务注解有用么Transaction注解加在private上并没有什么卵用。这倒不是事务处理的代码去写的特性。由于事务的这些功能是通过AOP方式强加进去的所以它收到动态代理的控制。private和final修饰的方法不会被代理。但是你却可以把private方法放在带有事务功能的public方法里。这样它看起来也有了事务的一些功能特性但它并没有。End互联网中用到的事务并不多很多都是非常小、速度非常快的接口对于开发人员来说事务是个累赘。但在一些带有金融属性的业务中或者一些企业级开发应用中事务确实一个绕不过的坎。一旦深入其中就会发现这个知识点露着血盆大口等君入瓮。xjjdog从源码层次聊到了几个面试常问的问题。不要觉得奇怪有的人确实一直在拿着脏读、幻读这样的名词来面试。而这些东西都属于当时看了恍然大悟第二天就继续懵逼的内容。什么时候才能务实一点呢近期热门文章《996的乐趣你是无法想象的》魔幻现实主义关爱神经衰弱《一切荒诞的傲慢皆来源于认知》不要被标题给骗了画面感十足的消遣文章《必看java后端亮剑诛仙》后端技术索引中肯火爆。全网转载上百次。《学完这100多技术能当架构师么(非广告)》精准点评100多框架帮你选型作者简介小姐姐味道 (xjjdog)一个不允许程序员走弯路的公众号。聚焦基础架构和Linux。十年架构日百亿流量与你探讨高并发世界给你不一样的味道。我的个人微信xjjdog0欢迎添加好友进一步交流