今天下班后,以前同事小胖問我Spring Service類中的注解@Transactional readOnly=true的作用。做為他眼中的高人,我自然要裝下A-C。居然想都沒有想就說是注解事務(wù)控制,然后給他扯了一通數(shù)據(jù)庫的隔離級別,什么read uncommit之類的,說readOnly是隔離級別最低的,在spring查詢方法里用,性能最高。
ps:之前的項(xiàng)目多數(shù)基于xml,親自用annotation的機(jī)會很少,都是小弟們在實(shí)戰(zhàn)。
示例:
- @Component("channelService")
- @Transactional(readOnly = true)
- public class ChannelServiceImpl implements IChannelService {
- @Resource(name = "productService")
- private IProductService productService;
- /**
- * 根據(jù)頻道名字查詢頻道。沒有就返回NULL。
- *
- * @param name 頻道名稱不能為空。
- * @return (假設(shè)頻道名稱是加了唯一約束的,否則可能結(jié)果不止一條導(dǎo)致異常)
- */
- @MethodCache(expire = 3600)
- @Transactional(readOnly = true)
- public ChannelVO getChannelByName(String name) {
- if (name == null || "".equals(name.trim())) {
- throw new IllegalArgumentException("name=" + name);
- }
- ShopChannels channel = (ShopChannels) channelDao.createCriteria()
- .add(Restrictions.eq("name", name))
- .uniqueResult();
- if (channel == null) {
- return new ChannelVO();
- }
- ChannelVO vo = new ChannelVO();
- DozerMapper.getInstance().map(channel, vo);
- //增加頻道對應(yīng)商品分類id 只取第一個分類
- Set<ProductClass> listCates = channel.getProductClasses();
- if (listCates != null && !listCates.isEmpty()) {
- vo.setProductClass("" + listCates.iterator().next().getSid());
- }
- List<Integer> list = new ArrayList<Integer>();
- Iterator<ProductClass> iterator = listCates.iterator();
- while (iterator.hasNext()) {
- ProductClass productClass = iterator.next();
- list.add(productClass.getSid());
- }
- vo.setAllProductClass(list);
- return vo;
- }
- }
數(shù)據(jù)庫隔離相關(guān)內(nèi)容
在一個應(yīng)用中,依據(jù)事務(wù)的隔離級別將會有三種情況發(fā)生。
◆臟讀(dirty read):當(dāng)一個事務(wù)讀取另一個事務(wù)尚未提交的修改時,產(chǎn)生臟讀。
◆ 不可重復(fù)讀(non-repeatable read):同一查詢在同一事務(wù)中多次進(jìn)行,由于其他提交事務(wù)所做的修改或刪除,每次返回不同的結(jié)果集,此時發(fā)生非重復(fù)讀。:
◆ 幻像讀(phantom read):同一查詢在同一事務(wù)中多次進(jìn)行,由于其他提交事務(wù)所做的插入操作,每次返回不同的結(jié)果集,此時發(fā)生幻像讀。
1.Read Committed:
假設(shè)A事務(wù)對正在讀取數(shù)據(jù)Data放置了共享鎖,那么Data不能被其它事務(wù)改寫,所以當(dāng)B事務(wù)對Data進(jìn)行讀取時總和A讀取的Data數(shù)據(jù)是一致的,所以避免了臟讀。由于在A沒有提交之前可以對Data進(jìn)行改寫,那么B讀取到的某個值可能會在其讀取后被A更改從而導(dǎo)致了該值不能被重復(fù)取得;或者當(dāng)B再次用相同的where字句時得到了和前一次不一樣數(shù)據(jù)的結(jié)果集,也就是幻像數(shù)據(jù)。
2.Read Uncommitted:
假設(shè)A事務(wù)即不發(fā)布共享鎖,也不接受獨(dú)占鎖,那么并發(fā)的B或者其它事務(wù)可以改寫A事務(wù)讀取的數(shù)據(jù),那么并發(fā)的C事務(wù)讀取到的數(shù)據(jù)的狀態(tài)和A的或者B的數(shù)據(jù)都可能不一致,那么。臟讀、不可重復(fù)讀、幻象數(shù)據(jù)都可能存在。
3.Repeatable Read:
(注意MSDN原文中的第一句話:在查詢中使用的所有數(shù)據(jù)上放置鎖,所以不存在臟讀的情況)。
假設(shè)A事務(wù)對讀取的所有數(shù)據(jù)Data放置了鎖,以阻止其它事務(wù)對Data的更改,在A沒有提交之前,新的并發(fā)事務(wù)讀取到的數(shù)據(jù)如果存在于Data中,那么該數(shù)據(jù)的狀態(tài)和A事務(wù)中的數(shù)據(jù)是一致的,從而避免了不可重復(fù)的讀取。但在A事務(wù)沒有結(jié)束之前,B事務(wù)可以插入新記錄到Data所在的表中,那么其它事務(wù)再次用相同的where字句查詢時,得到的結(jié)果數(shù)可能上一次的不一致,也就是幻像數(shù)據(jù)。
4.Serializable:
在數(shù)據(jù)表上放置了排他鎖,以防止在事務(wù)完成之前由其他用戶更新行或向數(shù)據(jù)集中插入行,這是最嚴(yán)格的鎖。它防止了臟讀、不可重復(fù)讀取和幻象數(shù)據(jù)。
以下是對照表:

一般的應(yīng)用作隔離級別時,往往采用(2),(3)兩種,(1),(4)兩種前者輕后者重,但是Hibernate為了提高performance和scalability,在數(shù)據(jù)庫一層中采用的是(2)的隔離級別,然后在程序中進(jìn)行控制,從而實(shí)現(xiàn)了(3)的隔離級別,因此提高了企業(yè)應(yīng)用的事務(wù)處理效率。當(dāng)然Hibernate對于數(shù)據(jù)庫一層的隔離級別也可以顯示指定。Hibernate在程序中的控制方法有:version number, timestamp,對于遺留database也有optimistic-lock,而version number有時不能解決特殊的事務(wù)并發(fā)引起來的(3)問題,那么就需要針對特殊情況進(jìn)行細(xì)粒度的事務(wù)控制,可以看一下LOCKMODE。
回家反思緩存策略
回家一想,不對,應(yīng)該不完全是隔離級別的東西,自已可能肯定搞錯了,于是趕緊翻看hibernate3.3 reference.果然沒說對。應(yīng)該是二級緩存的策略問題。但不知為什么reference中沒有講到query cache annotation的東西,只是一頁帶過。提高性能中二級緩存策略倒是講了不少。
備忘:
Chapter 19. Improving performance
19.2. The Second Level Cache
19.2.1. Cache mappings
The <cache> element of a class or collection mapping has the following form:
- <cache
- usage="transactional|read-write|nonstrict-read-write|read-only"
- region="RegionName"
- include="all|non-lazy"
- />
read-write or read-only
region (optional: defaults to the class or collection role name): specifies the name of the
second level cache region
include (optional: defaults to all) non-lazy: specifies that properties of the entity mapped
with lazy="true" cannot be cached when attribute-level lazy fetching is enabled
Alternatively, you can specify <class-cache> and <collection-cache> elements in
hibernate.cfg.xml.
The usage attribute specifies a cache concurrency strategy.
直接簡要的看各種策略說明:
Strategy: read only
If your application needs to read, but not modify, instances of a persistent class, a read-only
cache can be used. This is the simplest and optimal performing strategy. It is even safe for use
in a cluster.
如果應(yīng)用只需要查詢,不需要修改,那么read-only緩存將使用,這是最簡單、最理想的性能策略,它在集群環(huán)境下也是安全的。
Strategy: read/write
If the application needs to update data, a read-write cache might be appropriate.This cache strategy should never be used if serializable transaction isolation level is required.
如果應(yīng)用需要修改,read-write cache是最合適的。這個cache策略永遠(yuǎn)不會使用,如果數(shù)據(jù)庫事務(wù)隔離級別是可序列化serializable的。這里看和隔離級別又有點(diǎn)關(guān)系。
Strategy: nonstrict read/write
If the application only occasionally needs to update data (i.e. if it is extremely unlikely that two
transactions would try to update the same item simultaneously), and strict transaction isolation
is not required, a nonstrict-read-write cache might be appropriate. If the cache is used in a
JTA environment, you must specify hibernate.transaction.manager_lookup_class. In other
environments, you should ensure that the transaction is completed when Session.close() or
Session.disconnect() is called.
如果應(yīng)用僅僅偶爾的修改數(shù)據(jù),并不需要嚴(yán)格的數(shù)據(jù)庫隔離級別。Nonstrict-read-write 緩存是最合適的。
Strategy: transactional
The transactional cache strategy provides support for fully transactional cache providers such
as JBoss TreeCache. Such a cache can only be used in a JTA environment and you must specify
hibernate.transaction.manager_lookup_class.
The transactional cache strategy提供完全的事務(wù)緩存機(jī)制,例如JBoss TreeCache.這樣的緩存只能用在JTA環(huán)境中,你也必需指定相關(guān)類。
Important
None of the cache providers support all of the cache concurrency strategies.
The following table shows which providers are compatible with which concurrency strategies.

回歸正題@Transactional
再想想,還是不對,hibernate中并沒有提到最開始annotation的東西,于是繼續(xù)糾結(jié)Spring3.0 Reference Documentation。
Using @Transactional
In addition to the XML-based declarative approach to transaction configuration, you can use an
annotation-based approach. Declaring transaction semantics directly in the Java source code puts the declarations much closer to the affected code. There is not much danger of undue coupling, because code that is meant to be used transactionally is almost always deployed that way anyway.
annotation事務(wù)控制,代替xml的。其它廢話少翻。
You can place the @Transactional annotation before an interface definition, a method on an Interface, a class definition, or a public method on a class. However, the mere presence of the
@Transactional annotation is not enough to activate the transactional behavior. The
@Transactional annotation is simply metadata that can be consumed by some runtime infrastructure that is @Transactional-aware and that can use the metadata to configure the appropriate beans with transactional behavior. In the preceding example, the <tx:annotation-driven/> element switches
onthetransactionalbehavior.
- //文檔例子:
- @Transactional(readOnly = true)
- public class DefaultFooService implements FooService {
- public Foo getFoo(String fooName) {
- // do something
- }
- // these settings have precedence for this method
- @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
- public void updateFoo(Foo foo) {
- // do something
- }
- }
@Transactional settings
The @Transactional annotation is metadata that specifies that an interface, class, or method must have transactional semantics; for example, “start a brand new read-only transaction when this method is invoked。
@Transactional 注解是規(guī)定接口,類或者方法必需應(yīng)用事務(wù)的元數(shù)據(jù)語法。
看看spring默認(rèn)的設(shè)置:
- //默認(rèn)傳播機(jī)制:PROPAGATION_REQUIRED
- * Propagation setting is PROPAGATION_REQUIRED.
- //默認(rèn)隔離級別:ISOLATION_DEFAULT
- * Isolation level is ISOLATION_DEFAULT.
- //默認(rèn)事務(wù)是read/write.到此知道查詢配readOnly的作用。
- * Transaction is read/write.
- //事務(wù)默認(rèn)超時時間取決于事務(wù)系統(tǒng),也有可能沒有,如果事務(wù)系統(tǒng)不支持。
- * Transaction timeout defaults to the default timeout of the underlying transaction system, or to none if time outs are not supported.
- //任何的RuntimeExcetipn將觸發(fā)回滾,任何的checkedException不觸發(fā)回滾。
- * Any RuntimeException triggers rollback,and any checkedException does not.
明細(xì)配置:

