1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.mybatis.cdi;
17
18 import jakarta.inject.Inject;
19 import jakarta.interceptor.AroundInvoke;
20 import jakarta.interceptor.Interceptor;
21 import jakarta.interceptor.InvocationContext;
22 import jakarta.transaction.HeuristicMixedException;
23 import jakarta.transaction.HeuristicRollbackException;
24 import jakarta.transaction.NotSupportedException;
25 import jakarta.transaction.RollbackException;
26 import jakarta.transaction.SystemException;
27
28 import java.io.Serializable;
29 import java.lang.reflect.InvocationTargetException;
30 import java.lang.reflect.UndeclaredThrowableException;
31
32 import org.apache.ibatis.session.SqlSessionManager;
33
34
35
36
37
38
39
40
41 @Transactional
42 @Interceptor
43 public class LocalTransactionInterceptor implements Serializable {
44
45 private static final long serialVersionUID = 1L;
46
47 @Inject
48 private transient SqlSessionManagerRegistry registry;
49
50
51
52
53
54
55
56
57
58
59
60
61 @AroundInvoke
62 public Object invoke(InvocationContext ctx) throws Exception {
63 Transactional transactional = getTransactionalAnnotation(ctx);
64 boolean isInitiator = start(transactional);
65 boolean isExternalJta = isTransactionActive();
66 if (isInitiator && !isExternalJta) {
67 beginJta();
68 }
69 boolean needsRollback = transactional.rollbackOnly();
70 Object result;
71 try {
72 result = ctx.proceed();
73 } catch (Exception ex) {
74 Exception unwrapped = unwrapException(ex);
75 needsRollback = needsRollback || needsRollback(transactional, unwrapped);
76 throw unwrapped;
77 } finally {
78 if (isInitiator) {
79 try {
80 if (needsRollback) {
81 rollback(transactional);
82 } else {
83 commit(transactional);
84 }
85 } finally {
86 close();
87 endJta(isExternalJta, needsRollback);
88 }
89 }
90 }
91 return result;
92 }
93
94
95
96
97
98
99
100
101
102 protected boolean isTransactionActive() throws SystemException {
103 return false;
104 }
105
106
107
108
109
110
111
112
113
114 protected void beginJta() throws NotSupportedException, SystemException {
115
116 }
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135 protected void endJta(boolean isExternaTransaction, boolean commit)
136 throws SystemException, RollbackException, HeuristicMixedException, HeuristicRollbackException {
137
138 }
139
140 private boolean needsRollback(Transactional transactional, Throwable throwable) {
141 if (RuntimeException.class.isAssignableFrom(throwable.getClass())) {
142 return true;
143 }
144 for (Class<? extends Throwable> exceptionClass : transactional.rollbackFor()) {
145 if (exceptionClass.isAssignableFrom(throwable.getClass())) {
146 return true;
147 }
148 }
149 return false;
150 }
151
152 protected Transactional getTransactionalAnnotation(InvocationContext ctx) {
153 Transactional t = ctx.getMethod().getAnnotation(Transactional.class);
154 if (t == null) {
155 t = ctx.getMethod().getDeclaringClass().getAnnotation(Transactional.class);
156 }
157 return t;
158 }
159
160 private boolean start(Transactional transactional) {
161 boolean started = false;
162 for (SqlSessionManager manager : this.registry.getManagers()) {
163 if (!manager.isManagedSessionStarted()) {
164 manager.startManagedSession(transactional.executorType(),
165 transactional.isolation().getTransactionIsolationLevel());
166 started = true;
167 }
168 }
169 return started;
170 }
171
172 private void commit(Transactional transactional) {
173 for (SqlSessionManager manager : this.registry.getManagers()) {
174 manager.commit(transactional.force());
175 }
176 }
177
178 private void rollback(Transactional transactional) {
179 for (SqlSessionManager manager : this.registry.getManagers()) {
180 manager.rollback(transactional.force());
181 }
182 }
183
184 private void close() {
185 for (SqlSessionManager manager : this.registry.getManagers()) {
186 manager.close();
187 }
188 }
189
190 private Exception unwrapException(Exception wrapped) {
191 Throwable unwrapped = wrapped;
192 while (true) {
193 if (unwrapped instanceof InvocationTargetException invocationTargetException) {
194 unwrapped = invocationTargetException.getTargetException();
195 } else if (unwrapped instanceof UndeclaredThrowableException undeclaredThrowableException) {
196 unwrapped = undeclaredThrowableException.getUndeclaredThrowable();
197 } else if (unwrapped instanceof Exception exception) {
198 return exception;
199 } else {
200 return new RuntimeException(unwrapped);
201 }
202 }
203 }
204
205 }