腾讯域名怎么建设网站,网站统计系统,品牌建设的可行性论证,企业宣传片制作软件介绍 基于ACID事务属性的关系数据库强一致性模型。 在本文中#xff0c;我们将阐明对资源本地事务和JTA事务使用不同的事务隔离级别和各种配置模式的背后原因。 隔离和一致性 在关系数据库系统中#xff0c;原子性和持久性是严格的属性#xff0c;而一致性和隔离性或多或少… 介绍 基于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的事务隔离级别配置因此我们将检查所有上述连接提供者在获得此特定设置后的行为。 为此我们将要 创建一个SessionFactory Override
protected SessionFactory newSessionFactory() {Properties properties getProperties();return new Configuration().addProperties(properties).addAnnotatedClass(SecurityId.class).buildSessionFactory(new StandardServiceRegistryBuilder().applySettings(properties).build());
} 打开一个新的会话并测试关联的连接事务隔离级别 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不会强迫您使用特定的连接提供程序机制。 您只需提供一个DataSourceHibernate就会在请求新的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 SERIALIZABLEJava Enterprise事务隔离支持 Hibernate具有内置的Transaction API抽象层 可将数据访问层与事务管理拓扑 资源本地或JTA隔离开。 虽然我们只能使用Hibernate事务抽象来开发应用程序但将这种责任委托给中间件技术 JEE或Spring 更为常见。 Java企业版 JTAJava事务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;OverrideTransactional(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 idtransactionManager classorg.springframework.orm.jpa.JpaTransactionManagerproperty nameentityManagerFactory refentityManagerFactory //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 idjtaTransactionManager factory-methodgetTransactionManagerclassbitronix.tm.TransactionManagerServices depends-onbtmConfig, dataSourcedestroy-methodshutdown/bean idtransactionManager classorg.springframework.transaction.jta.JtaTransactionManagerproperty nametransactionManager refjtaTransactionManager/property nameuserTransaction refjtaTransactionManager/
/bean 运行先前的测试时我们得到以下异常 org.springframework.transaction.InvalidIsolationLevelException: JtaTransactionManager does not support custom isolation levels by default - switch allowCustomIsolationLevels to true 因此让我们启用自定义隔离级别设置并重新运行测试 bean idtransactionManager classorg.springframework.transaction.jta.JtaTransactionManagerproperty nametransactionManager refjtaTransactionManager/property nameuserTransaction refjtaTransactionManager/property nameallowCustomIsolationLevels valuetrue/
/bean 该测试为我们提供了以下输出 DEBUG [main]: c.v.s.i.StoreServiceImpl - Transaction isolation level is READ_COMMITTED 即使有了这种额外的配置事务范围的隔离级别也不会传播到底层数据库连接因为这是JTA事务管理器的默认行为。 对于WebLogicSpring提供了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(WebLogics Transaction.setProperty(String, Serializable) method failed, ex.getTargetException());}catch (Exception ex) {throw new TransactionSystemException(Could not invoke WebLogics 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