TA的每日心情 | 衰 2021-2-2 11:21 |
---|
签到天数: 36 天 [LV.5]常住居民I
|
一、Propagation (事务的传播属性)
; O" K; t. l1 \) B; r. A8 I6 Q& E ]% d
Propagation : key属性确定代理应该给哪个方法增加事务行为。这样的属性最重要的部份是传播行为。有以下选项可供使用:PROPAGATION_REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。 d, A# Z* S& J5 M _
; {/ V* d" X3 F4 P5 LPROPAGATION_SUPPORTS--支持当前事务,如果当前没有事务,就以非事务方式执行。
5 F1 d1 b) f# ?$ x
3 s6 @4 c# @' G$ [8 ]" KPROPAGATION_MANDATORY--支持当前事务,如果当前没有事务,就抛出异常。
5 G0 x$ z. x+ i
9 ~% O0 M4 n$ f: K7 V1 \PROPAGATION_REQUIRES_NEW--新建事务,如果当前存在事务,把当前事务挂起。
+ z7 h4 C. N! ]- a8 n+ C7 {( |* J7 P% u7 K; y
PROPAGATION_NOT_SUPPORTED--以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。5 u9 h p9 k. X8 N
0 O- u5 ?$ V) d0 j- \( WPROPAGATION_NEVER--以非事务方式执行,如果当前存在事务,则抛出异常。
* a _# Y+ n, O
( S, y" H1 R1 ?5 r) K" ^) U1: PROPAGATION_REQUIRED
3 n2 M: w& U& a1 d0 s6 ~) a, l' T# @2 Z$ ~
加入当前正要执行的事务不在另外一个事务里,那么就起一个新的事务- R8 K9 g( @" T! Q0 u, }
4 u- m# {; c- o) W
比如说,ServiceB.methodB的事务级别定义为PROPAGATION_REQUIRED, 那么由于执行ServiceA.methodA的时候,9 l* C( P4 r4 g3 Y- v( _/ }
) Z* a2 K& R- o' K( g; _
ServiceA.methodA已经起了事务,这时调用ServiceB.methodB,ServiceB.methodB看到自己已经运行在ServiceA.methodA
' \: b0 x9 }& r5 e' ]. s: x p# b: H. u" O) Q2 L1 Y) d1 v
的事务内部,就不再起新的事务。而假如ServiceA.methodA运行的时候发现自己没有在事务中,他就会为自己分配一个事务。+ K- y0 A) W- n \2 z( V
, O& Z( L0 Z) e& a
这样,在ServiceA.methodA或者在ServiceB.methodB内的任何地方出现异常,事务都会被回滚。即使ServiceB.methodB的事务已经被
* O! i# [6 w/ b; [& e4 f3 G$ D9 m% m& _" \) [
提交,但是ServiceA.methodA在接下来fail要回滚,ServiceB.methodB也要回滚
- T5 H. d l% j4 _/ q2 q
: ?$ [. h0 x& h/ ]0 _2: PROPAGATION_SUPPORTS
! h7 v/ k+ M% ]- z P0 u: W6 T+ T# b" W2 s9 o6 y
如果当前在事务中,即以事务的形式运行,如果当前不再一个事务中,那么就以非事务的形式运行" B1 n! W% D% x* E1 T
! F/ L2 Y; w- h
3: PROPAGATION_MANDATORY
& S# y4 I9 d( Z$ }+ m( Y
- Q1 _9 [2 y, V: W: u4 `必须在一个事务中运行。也就是说,他只能被一个父事务调用。否则,他就要抛出异常; [# r+ n, h8 ]2 ^# p$ @, d4 R
% r6 z: ~$ H& V
4: PROPAGATION_REQUIRES_NEW- Y3 q5 T% a/ h) B" D) C
9 e5 r6 x$ O6 V/ `6 D& L# h# q1 n1 M
这个就比较绕口了。 比如我们设计ServiceA.methodA的事务级别为PROPAGATION_REQUIRED,ServiceB.methodB的事务级别为PROPAGATION_REQUIRES_NEW,
0 v# a0 u1 Z' b% D8 V1 t% t! g: M( U$ p; c
那么当执行到ServiceB.methodB的时候,ServiceA.methodA所在的事务就会挂起,ServiceB.methodB会起一个新的事务,等待ServiceB.methodB的事务完成以后,% c: U6 b9 i/ u7 C- G' I6 R
6 L$ |$ b7 f7 H2 q: ^他才继续执行。他与PROPAGATION_REQUIRED 的事务区别在于事务的回滚程度了。因为ServiceB.methodB是新起一个事务,那么就是存在. C' S, T0 S5 D' O, F# z' Q
; c! c. e5 L: R7 ?
两个不同的事务。如果ServiceB.methodB已经提交,那么ServiceA.methodA失败回滚,ServiceB.methodB是不会回滚的。如果ServiceB.methodB失败回滚,0 G" i, B5 e! S
8 I8 i% _; s7 b3 k
如果他抛出的异常被ServiceA.methodA捕获,ServiceA.methodA事务仍然可能提交。6 f0 i2 l! B. Q* j/ n7 J6 z; Q4 Z
+ k/ K; v$ X' R5: PROPAGATION_NOT_SUPPORTED& C1 J/ x, g5 o: z+ q
! E5 ^, U1 M; R当前不支持事务。比如ServiceA.methodA的事务级别是PROPAGATION_REQUIRED ,而ServiceB.methodB的事务级别是PROPAGATION_NOT_SUPPORTED ,0 X$ O4 O6 b6 R# p
1 {% q7 P; m1 S& J7 |那么当执行到ServiceB.methodB时,ServiceA.methodA的事务挂起,而他以非事务的状态运行完,再继续ServiceA.methodA的事务。6 }% ]8 R- g! ?7 J5 S
" u0 M+ o3 |; l& \3 T
6: PROPAGATION_NEVER& I- K0 z' c0 n
# c& a& z8 e% b3 ]5 h4 t不能在事务中运行。假设ServiceA.methodA的事务级别是PROPAGATION_REQUIRED, 而ServiceB.methodB的事务级别是PROPAGATION_NEVER ,
- L3 L+ X# q7 O0 P0 a: G$ E: u1 H4 S H- B M9 k2 |
那么ServiceB.methodB就要抛出异常了。
. V0 `5 N3 i8 T6 g% ^2 p0 i/ G
* [" V' E9 y [/ y' d7: PROPAGATION_NESTED
) {# ], B$ ?" f& Z+ q7 Y6 l+ H8 b4 H% s
理解Nested的关键是savepoint。他与PROPAGATION_REQUIRES_NEW的区别是,PROPAGATION_REQUIRES_NEW另起一个事务,将会与他的父事务相互独立,
: R' y/ a$ r" S7 j1 Z8 x* |6 R: J* |$ P! a2 A
而Nested的事务和他的父事务是相依的,他的提交是要等和他的父事务一块提交的。也就是说,如果父事务最后回滚,他也要回滚的。8 \/ ]/ j, l/ J6 F& j3 Y" b- l
, `' H8 K! B1 }, D% o) |) R. h
而Nested事务的好处是他有一个savepoint。
& l4 N' g7 R |
1 h; o8 `3 g8 I: E*****************************************# R3 v' M0 C. i
" C5 L/ \4 H/ `% I
ServiceA {
6 D, r: p' I/ x5 S( Z8 @
3 q3 y, L% N/ C5 H( J! ]$ ?6 i% H$ Y/**3 w2 ^' L; T1 k. m
0 D, |9 [4 A' {% }7 u2 N7 F9 P
* 事务属性配置为 PROPAGATION_REQUIRED, E% x) U4 a- Q) c6 O5 B/ K! M
1 j. t4 ?) y0 P( V
*/+ Y# Z& o6 [5 u; Y% c% m6 F6 V
% X- D9 I9 [) _( k9 G& Avoid methodA() {4 [# I2 L4 h3 c. M3 B1 f
7 Y0 i I$ n& R! q
try {
6 {* p2 w. b3 K) C( T0 D5 k9 o
2 H9 b! l( O$ s0 W( M4 C" O//savepoint
* n5 C4 x# z* X( W. ]( _; P3 M+ Q, ?5 A3 L2 h0 Q r3 s
ServiceB.methodB(); //PROPAGATION_NESTED 级别9 q9 _1 C6 J d: d( E# v' v
; H# |4 Y) B/ y: d' ~- @1 _0 k
} catch (SomeException) {. {! Y7 V- u# n8 W
: L3 r! h4 v2 O7 W4 T
// 执行其他业务, 如 ServiceC.methodC();; X. L& v" I; r8 m
5 @* X! I, k9 j1 Z' c" _9 v& x}
! V8 l% i, E7 E& B+ Z3 @4 H/ F, E: F; |+ q
}
& z: D5 S% K- a4 v% p/ A5 y! P1 p& o8 O9 j0 m z I4 d5 F( O, b
} n# W3 P' P: U* W* t
! g# o4 X3 n5 l
********************************************
( v) E, q" E P7 K! ?' ?9 U5 u' P2 B
也就是说ServiceB.methodB失败回滚,那么ServiceA.methodA也会回滚到savepoint点上,ServiceA.methodA可以选择另外一个分支,比如
. Q c3 J& d* a. N) s& j4 k! W
- i8 ^. z1 v S) C5 KServiceC.methodC,继续执行,来尝试完成自己的事务。' S0 v a9 _. d" q
) r$ M6 Q# j3 ?9 O
但是这个事务并没有在EJB标准中定义。2 G E8 n' k8 }0 v0 |0 F3 V5 i* T
" h' V- O6 p. i二、Isolation Level(事务隔离等级):
, t1 r9 N* T% y9 ?$ |! ~
2 p$ O! m- t* T& B4 {1、Serializable:最严格的级别,事务串行执行,资源消耗最大;8 ]! f3 o- Z' P$ L. {
( n9 T. X8 s# k: J9 j- V. ?, I
2、REPEATABLE READ:保证了一个事务不会修改已经由另一个事务读取但未提交(回滚)的数据。避免了“脏读取”和“不可重复读取”的情况,但是带来了更多的性能损失。
2 @* B9 N0 { z4 k g6 A |' O5 D: \1 a3 O( F
3、READ COMMITTED:大多数主流数据库的默认事务等级,保证了一个事务不会读到另一个并行事务已修改但未提交的数据,避免了“脏读取”。该级别适用于大多数系统。8 W9 j9 q8 ?0 ~- {
/ J- Q) R" r. g _+ }2 \4、Read Uncommitted:保证了读取过程中不会读取到非法数据。隔离级别在于处理多事务的并发问题。& q6 ^& G" r5 v |" ]+ x1 @" {
6 P2 M, E2 M$ M- E; E: `
我们知道并行可以提高数据库的吞吐量和效率,但是并不是所有的并发事务都可以并发运行,这需要查看数据库教材的可串行化条件判断了。' A' g" S- E7 r. Z$ P* ^* F
$ x# z# q5 y+ o- E: W# W8 k. I
这里就不阐述。
, h! _0 W3 ?; `& J# {1 s4 ^, _6 a( N3 {
我们首先说并发中可能发生的3中不讨人喜欢的事情
! P9 p8 r( l9 d6 i" U+ ?# ~$ h0 W5 `/ m! o$ O
1: Dirty reads--读脏数据。也就是说,比如事务A的未提交(还依然缓存)的数据被事务B读走,如果事务A失败回滚,会导致事务B所读取的的数据是错误的。1 k4 E' t1 y: @) _5 e' T
: E% u3 Q- h, w7 r2: non-repeatable reads--数据不可重复读。比如事务A中两处读取数据-total-的值。在第一读的时候,total是100,然后事务B就把total的数据改成 200,事务A再读一次,结果就发现,total竟然就变成200了,造成事务A数据混乱。
- [5 ?! w+ Y) r# p# v9 [) p/ D' T6 \" ?9 |7 j
3: phantom reads--幻象读数据,这个和non-repeatable reads相似,也是同一个事务中多次读不一致的问题。但是non-repeatable reads的不一致是因为他所要取的数据集被改变了(比如total的数据),但是phantom reads所要读的数据的不一致却不是他所要读的数据集改变,而是他的条件数据集改变。比如Select account.id where account.name="ppgogo*",第一次读去了6个符合条件的id,第二次读取的时候,由于事务b把一个帐号的名字由"dd"改成"ppgogo1",结果取出来了7个数据。
0 c: m9 k: x/ `! s' F+ ^* ]5 X! c! a4 u( x+ F, H
! l0 ^/ j6 n0 I6 W# J4 L. o7 N" C4 L6 c3 o8 ^9 ]% ^, u
Dirty reads non-repeatable reads phantom reads
: _" i- p# |9 E {% w9 BSerializable 不会 不会 不会4 p, j/ I0 a& |" `, G
REPEATABLE READ 不会 不会 会" G% d, U6 H2 D& \: [2 Z
READ COMMITTED 不会 会 会$ h U* \3 Z6 z# b
Read Uncommitted 会 会 会
+ H" U. R/ `( `' j7 s6 C; i% _& i7 I+ K# `9 c3 S
+ N$ N. L+ p: Z
0 z' q4 \5 R1 M( ?0 t
2 ~3 ^9 D, R' Q! h三、readOnly
3 u& m- y9 c5 {5 f" f+ E! E/ ]/ l) {5 j' S: W. G+ z
事务属性中的readOnly标志表示对应的事务应该被最优化为只读事务。- I# _/ x% M) w3 n* W
1 B9 ^8 v- s( {
这是一个最优化提示。在一些情况下,一些事务策略能够起到显著的最优化效果,例如在使用Object/Relational映射工具(如:hibernate或TopLink)时避免dirty checking(试图“刷新”)。' M& D2 v7 m o$ T# L8 S
. o; y$ }. c9 T4 o四、Timeout
i% k% N; I7 s5 @0 J6 n9 W$ ~2 [3 G$ ^! Y; \) t8 r
在事务属性中还有定义“timeout”值的选项,指定事务超时为几秒。在JTA中,这将被简单地传递到J2EE服务器的事务协调程序,并据此得到相应的解释
" X5 F2 B* T- I; ~# r
' B2 H1 _" x! R5 ?# N; |( W! e; ]4 I; s1 f+ I5 Q/ M
2 k7 s0 Z: X( ]
& U3 p& |% F" S
: a: m- D0 }0 Y3 u$ F) L1 A总结:8 ?' l8 x7 U+ n
( R3 m2 {+ j7 F7 x
<aop:config proxy-target-class="true"> 4 j8 q& E, t! H" j/ s
<aop:advisor pointcut="execution(* com.company..*Manager.*(..))"
- T; |4 N5 Q) I( L/ T advice-ref="txAdvice" /> 5 z& x1 x2 T$ s( Q4 l! q( Q
</aop:config>
# a: _: q. j6 M7 [2 F& [0 S1 a* K* Q( `3 X2 ^3 i. }# _# @
<tx:advice id="txAdvice"> 8 \+ F- H( r. M9 I. Q: V
<tx:attributes> $ ?8 g" y' w* J. @, B
<tx:method name="save*" isolation="DEFAULT/READ_COMMITTED/READ_UNCOMMITTED/REPEATABLE_READ/SERIALIZABLE]"/> ! D% D: B8 ` J# v
<tx:method name="update*" <tx:method name="update*" propagation="MANDATORY/NESTED/NEVER/NOT_SUPPORTED/REQUIRED/REQUIRES_NEW/SUPPORTS">/> - ]6 ?* w/ Q$ a1 E" t# n
<tx:method name="delete*" />
0 q; l2 d! S) l+ d9 |$ u. L3 N$ U <tx:method name="find*" read-only="true" /> 3 H. W, N0 O5 T0 O, O& ^6 `0 ^$ e6 s
</tx:attributes>
: Y( B' r2 o( ^. I- P$ M* r& R& t</tx:advice> 4 h- s2 j. z. r" R. p
- [; s: G' `4 q1 J+ K4 O5 v8 V9 @; D* O7 K) q6 `
' t z5 Y' v; s7 U C
1 h( w9 A4 L' Z
isolation设定事务的隔离级别,事务管理器根据它来控制另外一个事务可以看到本事务内的哪些数据。 定义的5个不同的事务隔离级别: DEFAULT:默认的隔离级别,使用数据库默认的事务隔离级别 READ_COMMITTED:保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据。这种事务隔离级别可以避免脏读出现,但是可能会出现不可重复读和幻像读。 READ_UNCOMMITTED:这是事务最低的隔离级别,它充许别外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻像读。 REPEATABLE_READ:这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免不可重复读。 SERIALIZABLE:这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读,不可重复读外,还避免了幻像读。 3 C- z! C+ q* B- A) D
propagation定义了7个事务传播行为 REQUIRED: 如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务。 SUPPORTS: 如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行。但是对于事务同步的事务管理器,SUPPORTS与不使用事务有少许不同。 REQUIRES_NEW 总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起。 NOT_SUPPORTED 总是非事务地执行,并挂起任何存在的事务。 NEVER 总是非事务地执行,如果存在一个活动事务,则抛出异常 NESTED:如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行。
' ]! B7 U! J7 D! e9 |
' `1 H6 R! F' `- G. p1 W: |嵌套事务一个非常重要的概念就是内层事务依赖于外层事务。外层事务失败时,会回滚内层事务所做的动作。而内层事务操作失败并不会引起外层事务的回滚。
' x& ]$ J. E5 }0 d. y4 e8 G. \$ N' D
原文转自:http://www.cnblogs.com/shitianzeng/articles/2319090.html
& \9 g7 s$ H0 Y Y# L/ [# d9 B
) v3 C' a% e$ o |
|