今天下班后,以前同事小胖問我Spring Service類中的注解@Transactional readOnly=true的作用。做為他眼中的高人,我自然要裝下A-C。居然想都沒有想就說是注解事務控制,然后給他扯了一通數據庫的隔離級別,什么read uncommit之類的,說readOnly是隔離級別最低的,在spring查詢方法里用,性能最高。
ps:之前的項目多數基于xml,親自用annotation的機會很少,都是小弟們在實戰。
示例:
- @Component("channelService")
- @Transactional(readOnly = true)
- public class ChannelServiceImpl implements IChannelService {
- @Resource(name = "productService")
- private IProductService productService;
- /**
- * 根據頻道名字查詢頻道。沒有就返回NULL。
- *
- * @param name 頻道名稱不能為空。
- * @return (假設頻道名稱是加了唯一約束的,否則可能結果不止一條導致異常)
- */
- @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);
- //增加頻道對應商品分類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;
- }
- }
數據庫隔離相關內容
在一個應用中,依據事務的隔離級別將會有三種情況發生。
◆臟讀(dirty read):當一個事務讀取另一個事務尚未提交的修改時,產生臟讀。
◆ 不可重復讀(non-repeatable read):同一查詢在同一事務中多次進行,由于其他提交事務所做的修改或刪除,每次返回不同的結果集,此時發生非重復讀。:
◆ 幻像讀(phantom read):同一查詢在同一事務中多次進行,由于其他提交事務所做的插入操作,每次返回不同的結果集,此時發生幻像讀。
1.Read Committed:
假設A事務對正在讀取數據Data放置了共享鎖,那么Data不能被其它事務改寫,所以當B事務對Data進行讀取時總和A讀取的Data數據是一致的,所以避免了臟讀。由于在A沒有提交之前可以對Data進行改寫,那么B讀取到的某個值可能會在其讀取后被A更改從而導致了該值不能被重復取得;或者當B再次用相同的where字句時得到了和前一次不一樣數據的結果集,也就是幻像數據。
2.Read Uncommitted:
假設A事務即不發布共享鎖,也不接受獨占鎖,那么并發的B或者其它事務可以改寫A事務讀取的數據,那么并發的C事務讀取到的數據的狀態和A的或者B的數據都可能不一致,那么。臟讀、不可重復讀、幻象數據都可能存在。
3.Repeatable Read:
(注意MSDN原文中的第一句話:在查詢中使用的所有數據上放置鎖,所以不存在臟讀的情況)。
假設A事務對讀取的所有數據Data放置了鎖,以阻止其它事務對Data的更改,在A沒有提交之前,新的并發事務讀取到的數據如果存在于Data中,那么該數據的狀態和A事務中的數據是一致的,從而避免了不可重復的讀取。但在A事務沒有結束之前,B事務可以插入新記錄到Data所在的表中,那么其它事務再次用相同的where字句查詢時,得到的結果數可能上一次的不一致,也就是幻像數據。
4.Serializable:
在數據表上放置了排他鎖,以防止在事務完成之前由其他用戶更新行或向數據集中插入行,這是最嚴格的鎖。它防止了臟讀、不可重復讀取和幻象數據。
以下是對照表:

一般的應用作隔離級別時,往往采用(2),(3)兩種,(1),(4)兩種前者輕后者重,但是Hibernate為了提高performance和scalability,在數據庫一層中采用的是(2)的隔離級別,然后在程序中進行控制,從而實現了(3)的隔離級別,因此提高了企業應用的事務處理效率。當然Hibernate對于數據庫一層的隔離級別也可以顯示指定。Hibernate在程序中的控制方法有:version number, timestamp,對于遺留database也有optimistic-lock,而version number有時不能解決特殊的事務并發引起來的(3)問題,那么就需要針對特殊情況進行細粒度的事務控制,可以看一下LOCKMODE。
回家反思緩存策略
回家一想,不對,應該不完全是隔離級別的東西,自已可能肯定搞錯了,于是趕緊翻看hibernate3.3 reference.果然沒說對。應該是二級緩存的策略問題。但不知為什么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.
如果應用只需要查詢,不需要修改,那么read-only緩存將使用,這是最簡單、最理想的性能策略,它在集群環境下也是安全的。
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.
如果應用需要修改,read-write cache是最合適的。這個cache策略永遠不會使用,如果數據庫事務隔離級別是可序列化serializable的。這里看和隔離級別又有點關系。
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.
如果應用僅僅偶爾的修改數據,并不需要嚴格的數據庫隔離級別。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提供完全的事務緩存機制,例如JBoss TreeCache.這樣的緩存只能用在JTA環境中,你也必需指定相關類。
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的東西,于是繼續糾結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事務控制,代替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 注解是規定接口,類或者方法必需應用事務的元數據語法。
看看spring默認的設置:
- //默認傳播機制:PROPAGATION_REQUIRED
- * Propagation setting is PROPAGATION_REQUIRED.
- //默認隔離級別:ISOLATION_DEFAULT
- * Isolation level is ISOLATION_DEFAULT.
- //默認事務是read/write.到此知道查詢配readOnly的作用。
- * Transaction is read/write.
- //事務默認超時時間取決于事務系統,也有可能沒有,如果事務系統不支持。
- * Transaction timeout defaults to the default timeout of the underlying transaction system, or to none if time outs are not supported.
- //任何的RuntimeExcetipn將觸發回滾,任何的checkedException不觸發回滾。
- * Any RuntimeException triggers rollback,and any checkedException does not.
明細配置:

