• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mybatis / cdi / 1360

25 Jul 2026 06:22PM UTC coverage: 98.34% (-0.02%) from 98.361%
1360

push

github

web-flow
Merge pull request #653 from hazendaz/master

General improvements on code base

82 of 90 branches covered (91.11%)

14 of 16 new or added lines in 2 files covered. (87.5%)

1 existing line in 1 file now uncovered.

237 of 241 relevant lines covered (98.34%)

0.98 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.0
/src/main/java/org/mybatis/cdi/LocalTransactionInterceptor.java
1
/*
2
 *    Copyright 2013-2026 the original author or authors.
3
 *
4
 *    Licensed under the Apache License, Version 2.0 (the "License");
5
 *    you may not use this file except in compliance with the License.
6
 *    You may obtain a copy of the License at
7
 *
8
 *       https://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 *    Unless required by applicable law or agreed to in writing, software
11
 *    distributed under the License is distributed on an "AS IS" BASIS,
12
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 *    See the License for the specific language governing permissions and
14
 *    limitations under the License.
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
 * Best-effort interceptor for local transactions. It locates all the instances of {@code SqlSssionManager} and starts
36
 * transactions on all them. It cannot guarantee atomiticy if there is more than one {@code SqlSssionManager}. Use XA
37
 * drivers, a JTA container and the {@link JtaTransactionInterceptor} in that case.
38
 *
39
 * @see JtaTransactionInterceptor
40
 */
41
@Transactional
42
@Interceptor
43
public class LocalTransactionInterceptor implements Serializable {
1✔
44

45
  private static final long serialVersionUID = 1L;
46

47
  @Inject
48
  private transient SqlSessionManagerRegistry registry;
49

50
  /**
51
   * Invoke.
52
   *
53
   * @param ctx
54
   *          the ctx
55
   *
56
   * @return the object
57
   *
58
   * @throws Exception
59
   *           the exception
60
   */
61
  @AroundInvoke
62
  public Object invoke(InvocationContext ctx) throws Exception {
63
    Transactional transactional = getTransactionalAnnotation(ctx);
1✔
64
    boolean isInitiator = start(transactional);
1✔
65
    boolean isExternalJta = isTransactionActive();
1✔
66
    if (isInitiator && !isExternalJta) {
1!
67
      beginJta();
1✔
68
    }
69
    boolean needsRollback = transactional.rollbackOnly();
1✔
70
    Object result;
71
    try {
72
      result = ctx.proceed();
1✔
73
    } catch (Exception ex) {
1✔
74
      Exception unwrapped = unwrapException(ex);
1✔
75
      needsRollback = needsRollback || needsRollback(transactional, unwrapped);
1!
76
      throw unwrapped;
1✔
77
    } finally {
78
      if (isInitiator) {
1!
79
        try {
80
          if (needsRollback) {
1✔
81
            rollback(transactional);
1✔
82
          } else {
83
            commit(transactional);
1✔
84
          }
85
        } finally {
86
          close();
1✔
87
          endJta(isExternalJta, needsRollback);
1✔
88
        }
89
      }
90
    }
91
    return result;
1✔
92
  }
93

94
  /**
95
   * Checks if is transaction active.
96
   *
97
   * @return true, if is transaction active
98
   *
99
   * @throws SystemException
100
   *           used by jtaTransactionInterceptor
101
   */
102
  protected boolean isTransactionActive() throws SystemException {
103
    return false;
1✔
104
  }
105

106
  /**
107
   * Begin jta.
108
   *
109
   * @throws NotSupportedException
110
   *           used by jtaTransactionInterceptor
111
   * @throws SystemException
112
   *           used by jtaTransactionInterceptor
113
   */
114
  protected void beginJta() throws NotSupportedException, SystemException {
115
    // nothing to do
116
  }
1✔
117

118
  /**
119
   * End jta.
120
   *
121
   * @param isExternaTransaction
122
   *          the is externa transaction
123
   * @param commit
124
   *          the commit
125
   *
126
   * @throws SystemException
127
   *           used by jtaTransactionInterceptor
128
   * @throws RollbackException
129
   *           used by jtaTransactionInterceptor
130
   * @throws HeuristicMixedException
131
   *           used by jtaTransactionInterceptor
132
   * @throws HeuristicRollbackException
133
   *           used by jtaTransactionInterceptor
134
   */
135
  protected void endJta(boolean isExternaTransaction, boolean commit)
136
      throws SystemException, RollbackException, HeuristicMixedException, HeuristicRollbackException {
137
    // nothing to do
138
  }
1✔
139

140
  private boolean needsRollback(Transactional transactional, Throwable throwable) {
141
    if (RuntimeException.class.isAssignableFrom(throwable.getClass())) {
1✔
142
      return true;
1✔
143
    }
144
    for (Class<? extends Throwable> exceptionClass : transactional.rollbackFor()) {
1✔
145
      if (exceptionClass.isAssignableFrom(throwable.getClass())) {
1✔
146
        return true;
1✔
147
      }
148
    }
149
    return false;
1✔
150
  }
151

152
  protected Transactional getTransactionalAnnotation(InvocationContext ctx) {
153
    Transactional t = ctx.getMethod().getAnnotation(Transactional.class);
1✔
154
    if (t == null) {
1✔
155
      t = ctx.getMethod().getDeclaringClass().getAnnotation(Transactional.class);
1✔
156
    }
157
    return t;
1✔
158
  }
159

160
  private boolean start(Transactional transactional) {
161
    boolean started = false;
1✔
162
    for (SqlSessionManager manager : this.registry.getManagers()) {
1✔
163
      if (!manager.isManagedSessionStarted()) {
1!
164
        manager.startManagedSession(transactional.executorType(),
1✔
165
            transactional.isolation().getTransactionIsolationLevel());
1✔
166
        started = true;
1✔
167
      }
168
    }
1✔
169
    return started;
1✔
170
  }
171

172
  private void commit(Transactional transactional) {
173
    for (SqlSessionManager manager : this.registry.getManagers()) {
1✔
174
      manager.commit(transactional.force());
1✔
175
    }
1✔
176
  }
1✔
177

178
  private void rollback(Transactional transactional) {
179
    for (SqlSessionManager manager : this.registry.getManagers()) {
1✔
180
      manager.rollback(transactional.force());
1✔
181
    }
1✔
182
  }
1✔
183

184
  private void close() {
185
    for (SqlSessionManager manager : this.registry.getManagers()) {
1✔
186
      manager.close();
1✔
187
    }
1✔
188
  }
1✔
189

190
  private Exception unwrapException(Exception wrapped) {
191
    Throwable unwrapped = wrapped;
1✔
192
    while (true) {
193
      if (unwrapped instanceof InvocationTargetException invocationTargetException) {
1!
NEW
194
        unwrapped = invocationTargetException.getTargetException();
×
195
      } else if (unwrapped instanceof UndeclaredThrowableException undeclaredThrowableException) {
1!
NEW
196
        unwrapped = undeclaredThrowableException.getUndeclaredThrowable();
×
197
      } else if (!(unwrapped instanceof Exception exception)) {
1!
UNCOV
198
        return new RuntimeException(unwrapped);
×
199
      } else {
200
        return exception;
1✔
201
      }
202
    }
203
  }
204

205
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc