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

mybatis / guice / #1210

04 Nov 2023 08:23PM CUT coverage: 80.045%. Remained the same
#1210

Pull #633

github

web-flow
Merge 9d1860fb9 into 408340d1e
Pull Request #633: [pom] Sortpom

1408 of 1759 relevant lines covered (80.05%)

0.8 hits per line

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

72.5
/src/main/java/org/mybatis/guice/transactional/TransactionalMethodInterceptor.java
1
/*
2
 *    Copyright 2009-2023 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.guice.transactional;
17

18
import static java.lang.String.format;
19
import static java.lang.Thread.currentThread;
20

21
import jakarta.inject.Inject;
22

23
import java.lang.reflect.Constructor;
24
import java.lang.reflect.Method;
25
import java.util.Arrays;
26

27
import org.aopalliance.intercept.MethodInterceptor;
28
import org.aopalliance.intercept.MethodInvocation;
29
import org.apache.ibatis.logging.Log;
30
import org.apache.ibatis.logging.LogFactory;
31
import org.apache.ibatis.session.SqlSessionManager;
32

33
/**
34
 * Method interceptor for {@link Transactional} annotation.
35
 */
36
public final class TransactionalMethodInterceptor implements MethodInterceptor {
1✔
37

38
  private static final Class<?>[] CAUSE_TYPES = new Class[] { Throwable.class };
1✔
39

40
  private static final Class<?>[] MESSAGE_CAUSE_TYPES = new Class[] { String.class, Throwable.class };
1✔
41

42
  /**
43
   * This class logger.
44
   */
45
  private final Log log = LogFactory.getLog(getClass());
1✔
46

47
  /**
48
   * The {@code SqlSessionManager} reference.
49
   */
50
  @Inject
51
  private SqlSessionManager sqlSessionManager;
52

53
  /**
54
   * Sets the SqlSessionManager instance.
55
   *
56
   * @param sqlSessionManager
57
   *          the SqlSessionManager instance.
58
   */
59
  public void setSqlSessionManager(SqlSessionManager sqlSessionManager) {
60
    this.sqlSessionManager = sqlSessionManager;
1✔
61
  }
1✔
62

63
  /**
64
   * {@inheritDoc}
65
   */
66
  @Override
67
  public Object invoke(MethodInvocation invocation) throws Throwable {
68
    Method interceptedMethod = invocation.getMethod();
1✔
69
    Transactional transactional = interceptedMethod.getAnnotation(Transactional.class);
1✔
70

71
    // The annotation may be present at the class level instead
72
    if (transactional == null) {
1✔
73
      transactional = interceptedMethod.getDeclaringClass().getAnnotation(Transactional.class);
1✔
74
    }
75

76
    String debugPrefix = null;
1✔
77
    if (this.log.isDebugEnabled()) {
1✔
78
      debugPrefix = format("[Intercepted method: %s]", interceptedMethod.toGenericString());
×
79
    }
80

81
    boolean isSessionInherited = this.sqlSessionManager.isManagedSessionStarted();
1✔
82

83
    if (isSessionInherited) {
1✔
84
      if (log.isDebugEnabled()) {
1✔
85
        log.debug(format("%s - SqlSession already set for thread: %s", debugPrefix, currentThread().getId()));
×
86
      }
87
    } else {
88
      if (log.isDebugEnabled()) {
1✔
89
        log.debug(
×
90
            format("%s - SqlSession not set for thread: %s, creating a new one", debugPrefix, currentThread().getId()));
×
91
      }
92

93
      sqlSessionManager.startManagedSession(transactional.executorType(),
1✔
94
          transactional.isolation().getTransactionIsolationLevel());
1✔
95
    }
96

97
    Object object = null;
1✔
98
    boolean needsRollback = transactional.rollbackOnly();
1✔
99
    try {
100
      object = invocation.proceed();
1✔
101
    } catch (Throwable t) {
1✔
102
      needsRollback = true;
1✔
103
      throw convertThrowableIfNeeded(invocation, transactional, t);
1✔
104
    } finally {
105
      if (!isSessionInherited) {
1✔
106
        try {
107
          if (needsRollback) {
1✔
108
            if (log.isDebugEnabled()) {
1✔
109
              log.debug(debugPrefix + " - SqlSession of thread: " + currentThread().getId() + " rolling back");
×
110
            }
111

112
            sqlSessionManager.rollback(true);
1✔
113
          } else {
114
            if (log.isDebugEnabled()) {
1✔
115
              log.debug(debugPrefix + " - SqlSession of thread: " + currentThread().getId() + " committing");
×
116
            }
117

118
            sqlSessionManager.commit(transactional.force());
1✔
119
          }
120
        } finally {
121
          if (log.isDebugEnabled()) {
1✔
122
            log.debug(format("%s - SqlSession of thread: %s terminated its life-cycle, closing it", debugPrefix,
×
123
                currentThread().getId()));
×
124
          }
125

126
          sqlSessionManager.close();
1✔
127
        }
1✔
128
      } else if (log.isDebugEnabled()) {
1✔
129
        log.debug(format("%s - SqlSession of thread: %s is inherited, skipped close operation", debugPrefix,
×
130
            currentThread().getId()));
×
131
      }
132
    }
133

134
    return object;
1✔
135
  }
136

137
  private Throwable convertThrowableIfNeeded(MethodInvocation invocation, Transactional transactional, Throwable t) {
138
    Method interceptedMethod = invocation.getMethod();
1✔
139

140
    // check the caught exception is declared in the invoked method
141
    for (Class<?> exceptionClass : interceptedMethod.getExceptionTypes()) {
1✔
142
      if (exceptionClass.isAssignableFrom(t.getClass())) {
1✔
143
        return t;
1✔
144
      }
145
    }
146

147
    // check the caught exception is of same rethrow type
148
    if (transactional.rethrowExceptionsAs().isAssignableFrom(t.getClass())) {
1✔
149
      return t;
1✔
150
    }
151

152
    // rethrow the exception as new exception
153
    String errorMessage;
154
    Object[] initargs;
155
    Class<?>[] initargsType;
156

157
    if (transactional.exceptionMessage().length() != 0) {
1✔
158
      errorMessage = format(transactional.exceptionMessage(), invocation.getArguments());
1✔
159
      initargs = new Object[] { errorMessage, t };
1✔
160
      initargsType = MESSAGE_CAUSE_TYPES;
1✔
161
    } else {
162
      initargs = new Object[] { t };
1✔
163
      initargsType = CAUSE_TYPES;
1✔
164
    }
165

166
    Constructor<? extends Throwable> exceptionConstructor = getMatchingConstructor(transactional.rethrowExceptionsAs(),
1✔
167
        initargsType);
168
    Throwable rethrowEx = null;
1✔
169
    if (exceptionConstructor != null) {
1✔
170
      try {
171
        rethrowEx = exceptionConstructor.newInstance(initargs);
1✔
172
      } catch (Exception e) {
×
173
        errorMessage = format("Impossible to re-throw '%s', it needs the constructor with %s argument(s).",
×
174
            transactional.rethrowExceptionsAs().getName(), Arrays.toString(initargsType));
×
175
        log.error(errorMessage, e);
×
176
        rethrowEx = new RuntimeException(errorMessage, e);
×
177
      }
1✔
178
    } else {
179
      errorMessage = format("Impossible to re-throw '%s', it needs the constructor with %s or %s argument(s).",
×
180
          transactional.rethrowExceptionsAs().getName(), Arrays.toString(CAUSE_TYPES),
×
181
          Arrays.toString(MESSAGE_CAUSE_TYPES));
×
182
      log.error(errorMessage);
×
183
      rethrowEx = new RuntimeException(errorMessage);
×
184
    }
185

186
    return rethrowEx;
1✔
187
  }
188

189
  @SuppressWarnings("unchecked")
190
  private static <E extends Throwable> Constructor<E> getMatchingConstructor(Class<E> type, Class<?>[] argumentsType) {
191
    Class<? super E> currentType = type;
1✔
192
    while (Object.class != currentType) {
1✔
193
      for (Constructor<?> constructor : currentType.getConstructors()) {
1✔
194
        if (Arrays.equals(argumentsType, constructor.getParameterTypes())) {
1✔
195
          return (Constructor<E>) constructor;
1✔
196
        }
197
      }
198
      currentType = currentType.getSuperclass();
×
199
    }
200
    return null;
×
201
  }
202

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

© 2025 Coveralls, Inc