企业Java中事务隔离级别的初学者指南

介绍

基于ACID事务属性的关系数据库强一致性模型。 在本文中,我们将阐明对资源本地事务和JTA事务使用不同的事务隔离级别和各种配置模式的背后原因。

隔离和一致性

在关系数据库系统中,原子性和持久性是严格的属性,而一致性和隔离性或多或少是可配置的。 我们甚至不能将一致性与隔离性分开,因为这两个属性始终是相关的。

隔离级别越低,系统获得的一致性越差。 从最小到最一致,有四个隔离级别:

  • 读未提交
  • READ COMMITTED(防止脏读)
  • 可重复读取(防止脏和不可重复读取)
  • 可序列化(防止脏的,不可重复的读取和幻像读取)

尽管最一致的SERIALIZABLE隔离级别是最安全的选择,但大多数数据库默认改为READ COMMITTED。 根据阿姆达尔定律 ,为了容纳更多的并发事务,我们必须减少数据处理的串行部分。 锁获取间隔越短,数据库可以处理的请求越多。

隔离度

如我们先前所展示的, 应用程序级可重复读取与乐观锁定机制配对,对于防止长时间对话中的更新丢失非常方便。

在高度并发的环境中,乐观锁定可能会导致高事务失败率。 与其他任何排队机制一样,悲观锁定在提供足够的锁定获取时间间隔时可能会容纳更多事务。

数据库和隔离级别

除MySQL(使用REPEATABLE_READ)外,大多数关系数据库系统的默认隔离级别为READ_COMMITTED。 所有数据库都允许您设置默认的事务隔离级别。

通常,数据库在多个应用程序之间共享,并且每个应用程序都有其自己的特定交易要求。 对于大多数事务,READ_COMMITTED隔离级别是最佳选择,我们仅应针对特定业务案例覆盖它。

这种策略被证明是非常有效的,它使我们对所有SQL事务的子集具有更严格的隔离级别。

数据源隔离级别

JDBC Connection对象允许我们为在该特定连接上发出的所有事务设置隔离级别。 建立新的数据库连接是一个资源消耗过程,因此大多数应用程序都使用连接池 DataSource 。 连接池数据源还可以设置默认的事务隔离级别:

  • DBCP
  • DBCP2
  • 光ikaCP
  • 骨CP
  • Bitronix交易管理器

与全局数据库隔离级别设置相比,DataSource级别的事务隔离配置更加方便。 每个应用程序可以设置自己的特定并发控制级别。

我们甚至可以定义多个数据源,每个数据源都有一个定义的隔离级别。 这样,我们可以动态选择特定的隔离级别的JDBC连接。

休眠隔离级别

因为它必须同时支持资源本地事务和JTA事务,所以Hibernate提供了一种非常灵活的连接提供程序机制。

JTA事务需要XAConnection,并且JTA事务管理器负责提供XA兼容连接。

资源本地事务可以使用资源本地 DataSource ,在这种情况下,Hibernate提供了多个连接提供程序选项:

  • 驱动程序管理器连接提供程序(不合并连接,因此仅用于简单的测试方案)
  • C3P0连接提供程序(委派连接以获取对内部C3P0连接池数据源的调用)
  • 数据源连接提供程序(委派连接以获取对外部数据源的调用。

Hibernate提供了一个称为hibernate.connection.isolation的事务隔离级别配置,因此我们将检查所有上述连接提供者在获得此特定设置后的行为。

为此,我们将要:

  1. 创建一个SessionFactory:
    @Override
    protected SessionFactory newSessionFactory() {Properties properties = getProperties();return new Configuration().addProperties(properties).addAnnotatedClass(SecurityId.class).buildSessionFactory(new StandardServiceRegistryBuilder().applySettings(properties).build());
    }
  2. 打开一个新的会话并测试关联的连接事务隔离级别:
    @Testpublic void test() {Session session = null;Transaction txn = null;try {session = getSessionFactory().openSession();txn = session.beginTransaction();session.doWork(new Work() {@Overridepublic void execute(Connection connection) throws SQLException {LOGGER.debug("Transaction isolation level is {}", Environment.isolationLevelToString(connection.getTransactionIsolation()));}});txn.commit();} catch (RuntimeException e) {if ( txn != null && txn.isActive() ) txn.rollback();throw e;} finally {if (session != null) {session.close();}}}

唯一不同的是连接提供程序配置。

驱动程序管理器连接提供程序

驱动程序管理器连接提供程序为配置的数据库驱动程序提供了基本的DataSource包装器。 您仅应将其用于测试方案,因为它不提供专业的连接池机制。

@Override
protected Properties getProperties() {Properties properties = new Properties();properties.put("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");//driver settingsproperties.put("hibernate.connection.driver_class", "org.hsqldb.jdbcDriver");properties.put("hibernate.connection.url", "jdbc:hsqldb:mem:test");properties.put("hibernate.connection.username", "sa");properties.put("hibernate.connection.password", "");//isolation levelproperties.setProperty("hibernate.connection.isolation", String.valueOf(Connection.TRANSACTION_SERIALIZABLE));return properties;
}

测试生成以下输出:

WARN  [main]: o.h.e.j.c.i.DriverManagerConnectionProviderImpl - HHH000402: Using Hibernate built-in connection pool (not for production use!)
DEBUG [main]: c.v.h.m.l.t.TransactionIsolationDriverConnectionProviderTest - Transaction isolation level is SERIALIZABLE

Hibernate会话关联的JDBC连接正在使用SERIALIZABLE事务隔离级别,因此hibernate.connection.isolation配置适用于此特定的连接提供程序。

C3P0连接提供者

Hibernate还提供了一个内置的C3P0连接提供程序。 像前面的示例一样,我们只需要提供驱动程序配置设置,然后Hibernate代表我们实例化C3P0连接池。

@Override
protected Properties getProperties() {Properties properties = new Properties();properties.put("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");//log settingsproperties.put("hibernate.hbm2ddl.auto", "update");properties.put("hibernate.show_sql", "true");//driver settingsproperties.put("hibernate.connection.driver_class", "org.hsqldb.jdbcDriver");properties.put("hibernate.connection.url", "jdbc:hsqldb:mem:test");properties.put("hibernate.connection.username", "sa");properties.put("hibernate.connection.password", "");//c3p0 settingsproperties.put("hibernate.c3p0.min_size", 1);properties.put("hibernate.c3p0.max_size", 5);//isolation levelproperties.setProperty("hibernate.connection.isolation", String.valueOf(Connection.TRANSACTION_SERIALIZABLE));return properties;
}

测试生成以下输出:

Dec 19, 2014 11:02:56 PM com.mchange.v2.log.MLog <clinit>
INFO: MLog clients using java 1.4+ standard logging.
Dec 19, 2014 11:02:56 PM com.mchange.v2.c3p0.C3P0Registry banner
INFO: Initializing c3p0-0.9.2.1 [built 20-March-2013 10:47:27 +0000; debug? true; trace: 10]
DEBUG [main]: c.v.h.m.l.t.TransactionIsolationInternalC3P0ConnectionProviderTest - Transaction isolation level is SERIALIZABLE

因此, hibernate.connection.isolation配置也适用于内部C3P0连接提供程序。

数据源连接提供程序

Hibernate不会强迫您使用特定的连接提供程序机制。 您只需提供一个DataSource,Hibernate就会在请求新的Connection时使用它。 这次,我们将创建一个成熟的DataSource对象,并将其传递给hibernate.connection.datasource配置。

@Override
protected Properties getProperties() {Properties properties = new Properties();properties.put("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");//log settingsproperties.put("hibernate.hbm2ddl.auto", "update");//data source settingsproperties.put("hibernate.connection.datasource", newDataSource());//isolation levelproperties.setProperty("hibernate.connection.isolation", String.valueOf(Connection.TRANSACTION_SERIALIZABLE));return properties;
}protected ProxyDataSource newDataSource() {JDBCDataSource actualDataSource = new JDBCDataSource();actualDataSource.setUrl("jdbc:hsqldb:mem:test");actualDataSource.setUser("sa");actualDataSource.setPassword("");ProxyDataSource proxyDataSource = new ProxyDataSource();proxyDataSource.setDataSource(actualDataSource);proxyDataSource.setListener(new SLF4JQueryLoggingListener());return proxyDataSource;
}

测试生成以下输出:

DEBUG [main]: c.v.h.m.l.t.TransactionIsolationExternalDataSourceConnectionProviderTest - Transaction isolation level is READ_COMMITTED

这次似乎没有考虑hibernate.connection.isolation 。 Hibernate不会覆盖外部数据源,因此此设置在这种情况下是无用的。

如果使用外部数据源(例如,通过JNDI),则需要在外部数据源级别设置事务隔离。

要修复前面的示例,我们只需将外部数据源配置为使用特定的隔离级别:

protected ProxyDataSource newDataSource() {JDBCDataSource actualDataSource = new JDBCDataSource();actualDataSource.setUrl("jdbc:hsqldb:mem:test");actualDataSource.setUser("sa");actualDataSource.setPassword("");Properties properties = new Properties();properties.setProperty("hsqldb.tx_level", "SERIALIZABLE");actualDataSource.setProperties(properties);ProxyDataSource proxyDataSource = new ProxyDataSource();proxyDataSource.setDataSource(actualDataSource);proxyDataSource.setListener(new SLF4JQueryLoggingListener());return proxyDataSource;
}

生成以下输出:

DEBUG [main]: c.v.h.m.l.t.TransactionIsolationExternalDataSourceExternalconfgiurationConnectionProviderTest - Transaction isolation level is SERIALIZABLE

Java Enterprise事务隔离支持

Hibernate具有内置的Transaction API抽象层 ,可将数据访问层与事务管理拓扑( 资源本地或JTA)隔离开。 虽然我们只能使用Hibernate事务抽象来开发应用程序,但将这种责任委托给中间件技术( JEE或Spring )更为常见。

Java企业版

JTA(Java事务API规范)定义了应如何由符合JEE的应用服务器管理事务。 在客户端,我们可以使用TransactionAttribute批注来划分事务边界。 尽管我们可以选择正确的事务传播设置,但对于隔离级别我们不能这样做。

JTA不支持事务范围的隔离级别,因此我们必须诉诸于供应商特定的配置才能为XA DataSource提供特定的事务隔离设置。

弹簧

Spring @Transactional批注用于定义事务边界。 与JEE相对,此批注允许我们配置:

  • 隔离级别
  • 异常类型回滚策略
  • 传播
  • 只读
  • 超时

正如我将在本文稍后演示的那样,隔离级别设置仅可用于资源本地事务。 因为JTA不支持事务范围的隔离级别,所以Spring提供了IsolationLevelDataSourceRouter来克服使用应用程序服务器JTA DataSources时的这一缺点。

因为大多数DataSource实现只能采用默认的事务隔离级别,所以我们可以有多个这样的DataSource,每个为特定的事务隔离级别提供连接。

逻辑事务(例如@Transactional )隔离级别设置由IsolationLevelDataSourceRouter自省,因此将连接获取请求委托给特定的DataSource实现,该实现可以为具有相同事务隔离级别设置的JDBC连接提供服务。

因此,即使在JTA环境中,事务隔离路由器也可以提供独立于供应商的解决方案,以基于每个事务覆盖默认数据库隔离级别。

Spring事务范围的隔离级别

接下来,我将测试Spring事务管理对资源本地事务和JTA事务的支持。

为此,我将介绍一个事务性业务逻辑服务Bean:

@Service
public class StoreServiceImpl implements StoreService {protected final Logger LOGGER = LoggerFactory.getLogger(getClass());@PersistenceContext(unitName = "persistenceUnit")private EntityManager entityManager;@Override@Transactional(isolation = Isolation.SERIALIZABLE)public void purchase(Long productId) {        Session session = (Session) entityManager.getDelegate();session.doWork(new Work() {@Overridepublic void execute(Connection connection) throws SQLException {LOGGER.debug("Transaction isolation level is {}", Environment.isolationLevelToString(connection.getTransactionIsolation()));}});}
}

Spring框架提供了一个事务管理抽象,它将应用程序逻辑代码与基础事务特定的配置分离。 Spring事务管理器只是实际资源本地或JTA事务管理器的基础。

本地资源到XA事务的迁移只是一个配置细节,而实际的业务逻辑代码则保持不变。 如果没有额外的事务管理抽象层和跨领域AOP支持,这将是不可能的。

接下来,我们将测试各种特定的事务管理器如何支持事务范围隔离级别覆盖。

JPA交易经理

首先,我们将测试JPA事务管理器:

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"><property name="entityManagerFactory" ref="entityManagerFactory" /></bean>

当调用我们的业务逻辑服务时,这是我们得到的:

DEBUG [main]: c.v.s.i.StoreServiceImpl - Transaction isolation level is SERIALIZABLE

JPA事务管理器只能使用一个数据源,因此它只能发出资源本地事务。 在这种情况下,Spring事务管理器能够覆盖默认的DataSource隔离级别(在本例中为READ COMMITTED)。

JTA交易经理

现在,让我们看看切换到JTA事务时会发生什么。 如前所述,Spring仅提供逻辑事务管理器,这意味着我们还必须提供物理JTA事务管理器。

传统上,企业应用服务器(例如Wildfly和WebLogic )负责提供符合JTA的事务管理器。 如今,还有各种各样的独立JTA事务管理器:

  • 比特龙
  • Atomikos
  • 红帽纳拉亚纳(RedHat Narayana)

在此测试中,我们将使用Bitronix:

<bean id="jtaTransactionManager" factory-method="getTransactionManager"class="bitronix.tm.TransactionManagerServices" depends-on="btmConfig, dataSource"destroy-method="shutdown"/><bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"><property name="transactionManager" ref="jtaTransactionManager"/><property name="userTransaction" ref="jtaTransactionManager"/>
</bean>

运行先前的测试时,我们得到以下异常:

org.springframework.transaction.InvalidIsolationLevelException: JtaTransactionManager does not support custom isolation levels by default - switch 'allowCustomIsolationLevels' to 'true'

因此,让我们启用自定义隔离级别设置并重新运行测试:

<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"><property name="transactionManager" ref="jtaTransactionManager"/><property name="userTransaction" ref="jtaTransactionManager"/><property name="allowCustomIsolationLevels" value="true"/>
</bean>

该测试为我们提供了以下输出:

DEBUG [main]: c.v.s.i.StoreServiceImpl - Transaction isolation level is READ_COMMITTED

即使有了这种额外的配置,事务范围的隔离级别也不会传播到底层数据库连接,因为这是JTA事务管理器的默认行为。

对于WebLogic,Spring提供了WebLogicJtaTransactionManager来解决此限制,正如我们在以下Spring源代码片段中所看到的:

// Specify isolation level, if any, through corresponding WebLogic transaction property.
if (this.weblogicTransactionManagerAvailable) {if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {try {Transaction tx = getTransactionManager().getTransaction();Integer isolationLevel = definition.getIsolationLevel();/*weblogic.transaction.Transaction wtx = (weblogic.transaction.Transaction) tx;wtx.setProperty(ISOLATION_LEVEL_KEY, isolationLevel);*/this.setPropertyMethod.invoke(tx, ISOLATION_LEVEL_KEY, isolationLevel);}catch (InvocationTargetException ex) {throw new TransactionSystemException("WebLogic's Transaction.setProperty(String, Serializable) method failed", ex.getTargetException());}catch (Exception ex) {throw new TransactionSystemException("Could not invoke WebLogic's Transaction.setProperty(String, Serializable) method", ex);}}
}
else {applyIsolationLevel(txObject, definition.getIsolationLevel());
}

结论

事务管理绝对不是一件容易的事,并且有了所有可用的框架和抽象层,它确实变得比人们想象的要复杂。

因为数据完整性对于大多数业务应用程序非常重要,所以唯一的选择是掌握当前的项目数据层框架堆栈。

  • Hibernate和JPA可用的代码。

翻译自: https://www.javacodegeeks.com/2014/12/a-beginners-guide-to-transaction-isolation-levels-in-enterprise-java.html

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

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

相关文章

点击左侧跳到右侧

效果图 JS部分 function moveOption(e1, e2){ try{ for(var i0;i<e1.options.length;i ){ if(e1.options[i].selected){ var e e1.options[i]; e2.options.add(new Option(e.text, e.value)); e1.remove(i); iii-1 } } } catch(e){}} HTML以及CSS部分 <for…

好的微服务架构=企业服务总线(ESB)的灭亡?

这些天&#xff0c;似乎每个人都在谈论微服务。 您可以在数百篇文章和博客文章中读到很多有关该主题的文章&#xff0c;但是我建议的出发点是Martin Fowler的这篇文章 &#xff0c; 该文章引发了有关这种新建筑概念的大量讨论。 本文介绍了创建良好的微服务体系结构所面临的挑战…

移动端UC /QQ 浏览器的部分私有Meta 属性

<meta name"format-detection" content "telephoneno"/> 格式检测 禁止识别我们页面中的数字&#xff0c;防止把其当作电话识别&#xff0c;emailno 禁止识别邮箱 IOS设备对META的私有属性 <meta name"apple-mobile-web-app-capable&qu…

IDC关于使用JBoss Fuse的商业价值的报告(与Apache Camel一起使用)

这只是一篇博客文章&#xff0c;具有更多的商业性质&#xff0c;但是您不能一无所有。 实际上&#xff0c;这也是使Apache Camel保持活力并保持良好状态的原因&#xff0c;这还归功于其商业上的成功。 希望从JBoss Fuse之类的产品中寻找有关在商业上使用Apache Camel的附加值的…

书评:精通Lambda:多核世界中的Java编程

从版本8开始&#xff0c;λ编程&#xff08;lambda编程&#xff09;终于在Java世界中引入。此功能将在很大程度上改变Java开发人员的编程方式以及针对样板代码的新“武器”。 Java 8通过引入新的Stream API&#xff0c;大部分已将函数式编程应用在Collections API中。 此外&…

js框架

PS&#xff1a;https://www.cnblogs.com/zx0303/p/js-liuxingkuangjia.html 下边这张表简述了js目前的流行框架的优点: AngularJ.js 由google开发&#xff0c;2009年首次发布 很流行的前端框架使用Angular.js创建第一个UI&#xff0c;成本很…

Spring-Quartz (一)

摘自&#xff1a; http://www.blogjava.net/Jay2009/archive/2009/03/25/259176.htmlSpring为创建Quartz的Scheduler、Trigger和JobDetail提供了便利的FactoryBean类&#xff0c;以便能够在Spring 容器中享受注入的好处。此外Spring还提供了一些便利工具类直接将Spring中的Bean…

如何在Java Reflection中的类下获取所有方法信息?

本文以我以前的文章为基础 。 在本文中&#xff0c;我们将看到如何使用Java Reflection检索类相关信息。 我们将重点介绍方法名称。 注意&#xff1a;我将创建一个单独的反射器实用程序类&#xff0c;在该类中&#xff0c;我们在其构造函数中输入一个目标类&#xff0c;然后使…

在Java EE 7上骑骆驼–带有Swagger文档的REST服务

骆驼开箱即用。 Swagger集成就是其中之一。 不幸的是&#xff0c;大多数已经存在的功能都严重依赖于Spring。 但这并不能阻止我们在普通的Java EE 7应用程序中使用它们&#xff0c;因为有时它只是服务器的轻量级变体。 但我不想再对此进行讨论。 相反&#xff0c;我认为在所有情…

怎么隐藏滚动条又能滚动

1 <!DOCTYPE html>2 <html lang"en">3 <head>4 <meta charset"UTF-8">5 <title>滚动条隐藏</title>6 <style>7 body, ul, li {8 margin: 0;9 padding: 0; 10 …

Eclipse to android

JDK Eclipse Android SDK ADT 1 必须软件 Java JDK SE 1.6 (jdk-7u9-windows-i586.exe) Eclipse (Eclipse IDE for Java Developers) Google Android SDK (android-sdk_r15-windows.zip) ADT (ADT-15.0.0.zip) 如果找不到可参考&#xff1a; http://blog.csdn.net/zhenyong…

用多态和组合替换多个条件

这是一种众所周知的重构模式&#xff0c;可以将条件条件替换为多态性。 如果您不熟悉该模式&#xff0c;可以在此处查看 。 但是&#xff0c;一旦该类中有多个条件检查所基于的字段&#xff0c;该基本解决方案便会开始崩溃。 我们将研究一些有关如何使用这些可能性的想法。 有…

canvas画饼图

<style> body { background: black; text-align: center; } #cans { background: white; } </style> <script> function disToRad(n){//将度数表示弧度计算的方法 return n*Math.PI/180;//π用PI表示&#xff0c;π180&#xff0c;所以1PI/180 } w…

Web设计趋势分析

本文译自网站设计公司weavora.com&#xff0c;介绍了在他们眼里 8 个 Web 设计趋势&#xff1a;单页面、用照片做背景、色块设计、超大号的图片、聚焦简洁、响应式设计、视差滚动、强调字体&#xff0c;每个趋势后面都附了数个案例&#xff0c;相信对网站设计师会有一定的参考价…

使用Jasmine,Spock和Nashorn测试JVM服务器端JavaScript

JavaScript使用不仅限于浏览器中的客户端代码或NodeJS支持的服务器端代码。 许多基于JVM的项目都将其用作内部脚本语言。 测试这种功能既不简单也不标准。 在本文中&#xff0c;我打算演示一种使用成熟的工具&#xff08;例如Jasmine &#xff0c; Spock和Nashorn在服务器端JVM…

C#中的多态

封装、继承、多态&#xff0c;面向对象的三大特性&#xff0c;前两项理解相对容易&#xff0c;但要理解多态&#xff0c;特别是深入的了解&#xff0c;对于初学者而言可能就会有一定困难了。我一直认为学习OO的最好方法就是结合实践&#xff0c;封装、继承在实际工作中的应用随…

AJAX JSON

1、AJAX [1] AJAX简介 > 全称&#xff1a; Asynchronous JavaScript And XML > 异步的JavaScript和XML > AJAX就是通过JavaScript向服务器发送请求&#xff0c;并接收响应&#xff0c;然后我们在通过DOM来修改页面。 > XML指的是服务器响应的…

在WildFly 8.2中修补焊接3 – Java EE 8的第一个实验RI

Java EE 8一直在发展&#xff0c;并且已经提出了几个新的组件JSR。 JSR 365将定义CDI 2.0的规范。 红帽公司已经开始研究Weld 3的实现原型&#xff0c;并且Alpha3最近发布了 。 Red Hat的Java EE 8兼容应用服务器将是WildFly&#xff0c;将在其中实现所有不同的技术。 同时&am…

mat-form-field must contain a MatFormFieldControl错误的解决方法

下面的代码竟然出错了&#xff1a; <mat-form-field><input matInput placeholder"输入名称"></mat-form-field> 错误提示的莫名其妙&#xff0c;其实只要导入以下模块就可了&#xff1a; imports: [MatFormFieldModule,MatInputModule,] 更多专业…

lua# lua5.1.4 源码文件作用一览

写了个脚本列出lua源码C文件头部的注释&#xff0c;作为我有一搭没一搭以Lua为对象学习编译原理的开端。 lua5.1.4全部的源码有35个C文件&#xff0c;17216行代码。每个文件基本的功能如下 ./output_lua_sources_comments.sh ~/resources/sources/lua/src …