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连接。

Hibernate隔离级别

因为它必须同时支持资源本地事务和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自省,因此将连接获取请求委托给可以为JDBC连接提供相同事务隔离级别设置的特定DataSource实现。

因此,即使在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

java初学者指南

java初学者指南_企业Java中事务隔离级别的初学者指南相关推荐

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

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

  2. neo4j图形算法综合指南_网页设计中色彩使用的综合指南

    neo4j图形算法综合指南 There is a lot of material about color to be found online. But none of us has the time ...

  3. java coin介绍_代码示例中的Java 7:Project Coin

    java coin介绍 该博客通过代码示例介绍了一些新的Java 7功能,这些项目在Project Coin一词下进行了概述. Project Coin的目标是向JDK 7添加一组小的语言更改.这些更 ...

  4. Java main方法_解释Java中的main方法,及其作用_一个java文件中可包含多个main方法

    public static void main(String[] args) {} 或者 public static void main(String args[]) {} main方法是我们学习Ja ...

  5. java final 类_在Java中,final修饰的类有什么特点

    展开全部 关于Java中的32313133353236313431303231363533e4b893e5b19e31333264663736final(2010-09-09 14:19:48)转载▼ ...

  6. java logging包_用JDK中提供的java.util.logging.*包创建Logger对象----原创

    由于项目中用到了Applet与Servlet之间(客户端用Applet.Server端用Servlet)的通信,要求: 1:>客户端与服务端必须出Log. 2:>浏览器在加载Applet时 ...

  7. java jar 目录_将Java类路径中的所有jar包括在一个目录中

    有没有一种方法可以将所有的jar文件包含在类路径的目录中? 我正在尝试java -classpath lib / *.jar :. my.package.Program,它无法找到当然在这些罐子里的类 ...

  8. java gradle构建_在Gradle中为JPMS构建Java 6-8库

    java gradle构建 通过提供Java 9 module-info.class了解如何使用Gradle构建支持JPMS( Java平台模块系统 )的Java 6-8库. 介绍 如果您需要JPMS ...

  9. java避免空指针异常_避免Java中的空指针异常

    java避免空指针异常 空指针异常是Java中最常见,最烦人的异常. 在这篇文章中,我想避免这种不希望的异常. 首先让我们创建引发空指针异常的示例 private Boolean isFinished ...

最新文章

  1. Linux配置vlan网关
  2. python怎么字体加阴影_如何在pythonptx中给文本添加阴影?
  3. Java算法测试的输入模板
  4. 论文,质量管理+进度管理(主质量)
  5. flutter中list相关操作汇总(有这一篇就够啦)
  6. How to show out three rows from the same databand On A4?
  7. 蓝桥杯 公约数公倍数
  8. python 的内置函数
  9. VS Code 快捷键设置
  10. 统计学习方法——统计学习基础(一)
  11. 项目管理软件: 禅道、JIRA
  12. 太极周易罗盘计算机器人图片,周易八卦--罗盘的使用
  13. canon iPF 系列保养墨盒清零方法
  14. 软件构造笔记——Rep Invariantand Abstraction Function
  15. CodeForces 964A Splits
  16. python监听多个udp端口_尝试实现非阻塞python-udp多端口获取wierd异常
  17. 在QQ群和QQ空间中挂马
  18. android10新功能,三星A80升级安卓10 更新One UI 2.0内容新功能介绍
  19. 余弦相似度:通过计算两个向量的夹角余弦值来评估他们的相似度
  20. 第1天-代码随想录刷题训练| 704二分查找、26移除元素

热门文章

  1. jzoj6312-Lottery【dp,前缀和】
  2. 洛谷P1434-滑雪【线性化Dp】
  3. 纪中B组模拟赛总结(2020.2.1)
  4. UOJ284 快乐游戏鸡(树上动态规划问题、长链剖分+单调栈)
  5. Codeforces1142D
  6. P3768 简单的数学题 [狄利克雷卷积,杜教筛,莫比乌斯反演]
  7. 3、数据库中的字符集和校对集
  8. 用eclipse创建动态web项目手动生成web.xml方法
  9. java爬虫的2种爬取方式(HTTP||Socket)简单Demo(一)
  10. 02-MyBatis配置SQL打印