java初学者指南_企业Java中事务隔离级别的初学者指南
java初學(xué)者指南
介紹
基于ACID事務(wù)屬性的關(guān)系數(shù)據(jù)庫(kù)強(qiáng)一致性模型。 在本文中,我們將闡明對(duì)資源本地事務(wù)和JTA事務(wù)使用不同的事務(wù)隔離級(jí)別和各種配置模式的背后原因。
隔離和一致性
在關(guān)系數(shù)據(jù)庫(kù)系統(tǒng)中,原子性和持久性是嚴(yán)格的屬性,而一致性和隔離性或多或少是可配置的。 我們甚至無(wú)法將一致性與隔離性分開,因?yàn)檫@兩個(gè)屬性始終是相關(guān)的。
隔離級(jí)別越低,系統(tǒng)獲得的一致性越差。 從最小到最一致,有四個(gè)隔離級(jí)別:
- 讀未提交
- READ COMMITTED(防止臟讀)
- 可重復(fù)讀取(防止臟和不可重復(fù)讀取)
- 可序列化(防止臟的,不可重復(fù)的讀取和幻像讀取)
盡管最一致的SERIALIZABLE隔離級(jí)別是最安全的選擇,但是大多數(shù)數(shù)據(jù)庫(kù)默認(rèn)改為READ COMMITTED。 根據(jù)阿姆達(dá)爾定律 ,為了容納更多的并發(fā)事務(wù),我們必須減少數(shù)據(jù)處理的串行部分。 鎖獲取間隔越短,數(shù)據(jù)庫(kù)可以處理的請(qǐng)求越多。
隔離度
如前所述, 應(yīng)用程序級(jí)可重復(fù)讀取與樂觀鎖定機(jī)制配合使用非常方便,可以防止長(zhǎng)時(shí)間對(duì)話中丟失更新 。
在高度并發(fā)的環(huán)境中,樂觀鎖定可能會(huì)導(dǎo)致高事務(wù)失敗率。 與其他任何排隊(duì)機(jī)制一樣,悲觀鎖定在提供足夠的鎖定獲取時(shí)間間隔時(shí)可能會(huì)容納更多事務(wù)。
數(shù)據(jù)庫(kù)和隔離級(jí)別
除MySQL(使用REPEATABLE_READ)外,大多數(shù)關(guān)系數(shù)據(jù)庫(kù)系統(tǒng)的默認(rèn)隔離級(jí)別為READ_COMMITTED。 所有數(shù)據(jù)庫(kù)都允許您設(shè)置默認(rèn)的事務(wù)隔離級(jí)別。
通常,數(shù)據(jù)庫(kù)在多個(gè)應(yīng)用程序之間共享,并且每個(gè)應(yīng)用程序都有其自己的特定交易要求。 對(duì)于大多數(shù)事務(wù),READ_COMMITTED隔離級(jí)別是最佳選擇,我們僅應(yīng)針對(duì)特定業(yè)務(wù)案例覆蓋它。
這種策略被證明是非常有效的,它使我們對(duì)所有SQL事務(wù)的子集具有更嚴(yán)格的隔離級(jí)別。
數(shù)據(jù)源隔離級(jí)別
JDBC Connection對(duì)象允許我們?yōu)樵谠撎囟ㄟB接上發(fā)出的所有事務(wù)設(shè)置隔離級(jí)別。 建立新的數(shù)據(jù)庫(kù)連接是一個(gè)資源消耗過程,因此大多數(shù)應(yīng)用程序都使用連接池 DataSource 。 連接池?cái)?shù)據(jù)源還可以設(shè)置默認(rèn)的事務(wù)隔離級(jí)別:
- DBCP
- DBCP2
- 光ikaCP
- 骨CP
- Bitronix交易管理器
與全局?jǐn)?shù)據(jù)庫(kù)隔離級(jí)別設(shè)置相比,DataSource級(jí)別的事務(wù)隔離配置更加方便。 每個(gè)應(yīng)用程序可以設(shè)置自己的特定并發(fā)控制級(jí)別。
我們甚至可以定義多個(gè)數(shù)據(jù)源,每個(gè)數(shù)據(jù)源都有一個(gè)定義的隔離級(jí)別。 這樣,我們可以動(dòng)態(tài)地選擇特定的隔離級(jí)別的JDBC連接。
Hibernate隔離級(jí)別
因?yàn)樗仨毻瑫r(shí)支持資源本地事務(wù)和JTA事務(wù),所以Hibernate提供了一種非常靈活的連接提供程序機(jī)制。
JTA事務(wù)需要XAConnection,并且JTA事務(wù)管理器負(fù)責(zé)提供XA兼容連接。
資源本地事務(wù)可以使用資源本地 DataSource ,對(duì)于這種情況,Hibernate提供了多個(gè)連接提供程序選項(xiàng):
- 驅(qū)動(dòng)程序管理器連接提供程序(不合并連接,因此僅用于簡(jiǎn)單的測(cè)試方案)
- C3P0連接提供程序(委派連接以獲取對(duì)內(nèi)部C3P0連接池?cái)?shù)據(jù)源的調(diào)用)
- 數(shù)據(jù)源連接提供程序(委派連接以獲取對(duì)外部數(shù)據(jù)源的調(diào)用。
Hibernate提供了一個(gè)名為hibernate.connection.isolation的事務(wù)隔離級(jí)別配置,因此我們將檢查所有上述連接提供者在獲得此特定設(shè)置后的行為。
為此,我們將要:
唯一不同的是連接提供程序配置。
驅(qū)動(dòng)程序管理器連接提供程序
驅(qū)動(dòng)程序管理器連接提供程序?yàn)榕渲玫臄?shù)據(jù)庫(kù)驅(qū)動(dòng)程序提供了基本的DataSource包裝器。 您僅應(yīng)將其用于測(cè)試方案,因?yàn)樗惶峁I(yè)的連接池機(jī)制。
@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; }測(cè)試生成以下輸出:
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 SERIALIZABLEHibernate會(huì)話關(guān)聯(lián)的JDBC連接正在使用SERIALIZABLE事務(wù)隔離級(jí)別,因此hibernate.connection.isolation配置適用于此特定的連接提供程序。
C3P0連接提供者
Hibernate還提供了一個(gè)內(nèi)置的C3P0連接提供程序。 像前面的示例一樣,我們只需要提供驅(qū)動(dòng)程序配置設(shè)置,然后Hibernate代表我們實(shí)例化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; }測(cè)試生成以下輸出:
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配置也適用于內(nèi)部C3P0連接提供程序。
數(shù)據(jù)源連接提供程序
Hibernate不會(huì)強(qiáng)迫您使用特定的連接提供程序機(jī)制。 您只需提供一個(gè)DataSource,Hibernate就會(huì)在請(qǐng)求新的Connection時(shí)使用它。 這次,我們將創(chuàng)建一個(gè)成熟的DataSource對(duì)象,并將其傳遞給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; }測(cè)試生成以下輸出:
DEBUG [main]: c.v.h.m.l.t.TransactionIsolationExternalDataSourceConnectionProviderTest - Transaction isolation level is READ_COMMITTED這次似乎沒有考慮hibernate.connection.isolation 。 Hibernate不會(huì)覆蓋外部數(shù)據(jù)源,因此此設(shè)置在這種情況下是無(wú)用的。
如果使用外部數(shù)據(jù)源(例如,通過JNDI),則需要在外部數(shù)據(jù)源級(jí)別設(shè)置事務(wù)隔離。
要修復(fù)前面的示例,我們只需將外部數(shù)據(jù)源配置為使用特定的隔離級(jí)別:
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事務(wù)隔離支持
Hibernate具有一個(gè)內(nèi)置的Transaction API抽象層 ,將數(shù)據(jù)訪問層與事務(wù)管理拓?fù)?#xff08; 資源本地或JTA)隔離開。 雖然我們只能使用Hibernate事務(wù)抽象來(lái)開發(fā)應(yīng)用程序,但將這種責(zé)任委托給中間件技術(shù)( JEE或Spring )更為常見。
Java企業(yè)版
JTA(Java事務(wù)API規(guī)范)定義了應(yīng)如何由符合JEE的應(yīng)用服務(wù)器管理事務(wù)。 在客戶端,我們可以使用TransactionAttribute批注來(lái)劃分事務(wù)邊界。 雖然我們可以選擇正確的事務(wù)傳播設(shè)置,但是對(duì)于隔離級(jí)別我們不能做同樣的事情。
JTA不支持事務(wù)范圍的隔離級(jí)別,因此我們必須訴諸于供應(yīng)商特定的配置才能為XA DataSource提供特定的事務(wù)隔離設(shè)置。
彈簧
Spring @Transactional批注用于定義事務(wù)邊界。 與JEE相對(duì),此批注允許我們配置:
- 隔離級(jí)別
- 異常類型回滾策略
- 傳播
- 只讀
- 超時(shí)
正如我將在本文后面演示的那樣,隔離級(jí)別設(shè)置僅可用于資源本地事務(wù)。 由于JTA不支持事務(wù)范圍的隔離級(jí)別,因此Spring提供了IsolationLevelDataSourceRouter來(lái)克服使用應(yīng)用程序服務(wù)器JTA DataSources時(shí)的這一缺點(diǎn)。
因?yàn)榇蠖鄶?shù)DataSource實(shí)現(xiàn)只能采用默認(rèn)的事務(wù)隔離級(jí)別,所以我們可以有多個(gè)這樣的DataSource,每個(gè)為特定的事務(wù)隔離級(jí)別提供連接。
邏輯事務(wù)(例如@Transactional )隔離級(jí)別設(shè)置由IsolationLevelDataSourceRouter自省,因此將連接獲取請(qǐng)求委托給可以為JDBC連接提供相同事務(wù)隔離級(jí)別設(shè)置的特定DataSource實(shí)現(xiàn)。
因此,即使在JTA環(huán)境中,事務(wù)隔離路由器也可以提供獨(dú)立于供應(yīng)商的解決方案,以基于每個(gè)事務(wù)覆蓋默認(rèn)數(shù)據(jù)庫(kù)隔離級(jí)別。
Spring事務(wù)范圍的隔離級(jí)別
接下來(lái),我將測(cè)試Spring事務(wù)管理對(duì)資源本地事務(wù)和JTA事務(wù)的支持。
為此,我將介紹一個(gè)事務(wù)性業(yè)務(wù)邏輯服務(wù)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框架提供了一個(gè)事務(wù)管理抽象,它將應(yīng)用程序邏輯代碼與基礎(chǔ)事務(wù)特定的配置分離。 Spring事務(wù)管理器只是實(shí)際資源本地或JTA事務(wù)管理器的基礎(chǔ)。
從本地資源到XA事務(wù)的遷移只是一個(gè)配置細(xì)節(jié),而實(shí)際的業(yè)務(wù)邏輯代碼則保持不變。 如果沒有額外的事務(wù)管理抽象層和跨領(lǐng)域AOP支持,這將是不可能的。
接下來(lái),我們將測(cè)試各種特定的事務(wù)管理器如何支持事務(wù)范圍隔離級(jí)別覆蓋。
JPA交易經(jīng)理
首先,我們將測(cè)試JPA事務(wù)管理器:
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"><property name="entityManagerFactory" ref="entityManagerFactory" /></bean>當(dāng)調(diào)用我們的業(yè)務(wù)邏輯服務(wù)時(shí),這是我們得到的:
DEBUG [main]: c.v.s.i.StoreServiceImpl - Transaction isolation level is SERIALIZABLEJPA事務(wù)管理器只能使用一個(gè)數(shù)據(jù)源,因此它只能發(fā)出資源本地事務(wù)。 在這種情況下,Spring事務(wù)管理器能夠覆蓋默認(rèn)的DataSource隔離級(jí)別(在本例中為READ COMMITTED)。
JTA交易經(jīng)理
現(xiàn)在,讓我們看看切換到JTA事務(wù)時(shí)會(huì)發(fā)生什么。 如前所述,Spring僅提供邏輯事務(wù)管理器,這意味著我們還必須提供物理JTA事務(wù)管理器。
傳統(tǒng)上,企業(yè)應(yīng)用服務(wù)器(例如Wildfly和WebLogic )負(fù)責(zé)提供符合JTA的事務(wù)管理器。 如今,還有各種各樣的獨(dú)立JTA事務(wù)管理器:
- 比特龍
- Atomikos
- 紅帽納拉亞納(RedHat Narayana)
在此測(cè)試中,我們將使用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>運(yùn)行先前的測(cè)試時(shí),我們得到以下異常:
org.springframework.transaction.InvalidIsolationLevelException: JtaTransactionManager does not support custom isolation levels by default - switch 'allowCustomIsolationLevels' to 'true'因此,讓我們啟用自定義隔離級(jí)別設(shè)置并重新運(yùn)行測(cè)試:
<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>該測(cè)試為我們提供了以下輸出:
DEBUG [main]: c.v.s.i.StoreServiceImpl - Transaction isolation level is READ_COMMITTED即使有了這種額外的配置,事務(wù)范圍的隔離級(jí)別也不會(huì)傳播到基礎(chǔ)數(shù)據(jù)庫(kù)連接,因?yàn)檫@是JTA事務(wù)管理器的默認(rèn)行為。
對(duì)于WebLogic,Spring提供了WebLogicJtaTransactionManager來(lái)解決此限制,正如我們?cè)谝韵耂pring源代碼片段中所看到的:
// 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()); }結(jié)論
事務(wù)管理絕對(duì)不是一件容易的事,并且有了所有可用的框架和抽象層,它確實(shí)變得比人們想象的要復(fù)雜。
因?yàn)閿?shù)據(jù)完整性對(duì)于大多數(shù)業(yè)務(wù)應(yīng)用程序非常重要,所以唯一的選擇是掌握當(dāng)前的項(xiàng)目數(shù)據(jù)層框架堆棧。
- Hibernate和JPA可用的代碼。
翻譯自: https://www.javacodegeeks.com/2014/12/a-beginners-guide-to-transaction-isolation-levels-in-enterprise-java.html
java初學(xué)者指南
總結(jié)
以上是生活随笔為你收集整理的java初学者指南_企业Java中事务隔离级别的初学者指南的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 当地农保备案怎么办理(当地农保备案)
- 下一篇: rest服务swagger_在Java