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

mybatis / ibatis-2 / 730

28 Dec 2025 10:16PM UTC coverage: 65.615% (+0.5%) from 65.146%
730

push

github

web-flow
Update README.md

1602 of 2802 branches covered (57.17%)

5053 of 7701 relevant lines covered (65.61%)

0.66 hits per line

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

95.0
/src/main/java/com/ibatis/sqlmap/engine/mapping/result/ResultObjectFactoryUtil.java
1
/*
2
 * Copyright 2004-2025 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 com.ibatis.sqlmap.engine.mapping.result;
17

18
import com.ibatis.common.resources.Resources;
19

20
import java.util.ArrayDeque;
21
import java.util.ArrayList;
22
import java.util.Collection;
23
import java.util.Deque;
24
import java.util.HashSet;
25
import java.util.List;
26
import java.util.Set;
27

28
/**
29
 * This class is used to create instances of result objects. It will use the configured ResultObjectFactory if there is
30
 * one, otherwise it will use iBATIS' normal methods.
31
 * <p>
32
 * Note that this class is somewhat tightly coupled with SqlExecuter - SqlExecute must call the setStatementId() and
33
 * setResultObjectFactory() methods before executing a statement. This is a result of using a ThreadLocal to hold the
34
 * current configuration for the statement under execution. Using a ThreadLocal is a solution for IBATIS-366. Without a
35
 * ThreadLocal, the current factory and statement id would have to be added to many method signatures - often in
36
 * inappropriate places.
37
 *
38
 * @author Jeff Butler
39
 */
40
public class ResultObjectFactoryUtil {
41

42
  /**
43
   * Use a ThreadLocal to hold the current statementId and factory. This is much easier than passing these items all
44
   * over the place, and it has no measurable impact on performance (I did a test with 100000 result rows and found no
45
   * impact - Jeff Butler).
46
   */
47
  private static ThreadLocal<Deque<FactorySettings>> factorySettings = new ThreadLocal<>();
1✔
48

49
  /**
50
   * Utility class - no instances.
51
   */
52
  private ResultObjectFactoryUtil() {
53
    super();
54
  }
55

56
  /**
57
   * Algorithm:
58
   * <ul>
59
   * <li>If factory is null, then create object internally()</li>
60
   * <li>Otherwise try to create object through factory</li>
61
   * <li>If null returned from factory, then create object internally</li>
62
   * </ul>
63
   * This allows the factory to selectively create objects, also allows for the common possibility that a factory is not
64
   * configured.
65
   *
66
   * @param clazz
67
   *          the type of object to create
68
   *
69
   * @return a new instance of the specified class. The instance must be castable to the specified class.
70
   *
71
   * @throws InstantiationException
72
   *           if the instance cannot be created. If you throw this Exception, iBATIS will throw a runtime exception in
73
   *           response and will end.
74
   * @throws IllegalAccessException
75
   *           if the constructor cannot be accessed. If you throw this Exception, iBATIS will throw a runtime exception
76
   *           in response and will end.
77
   */
78
  public static Object createObjectThroughFactory(Class clazz) throws InstantiationException, IllegalAccessException {
79

80
    FactorySettings fs = getCurrentFactorySettings();
1✔
81

82
    Object obj;
83
    if (fs.getResultObjectFactory() == null) {
1✔
84
      obj = createObjectInternally(clazz);
1✔
85
    } else {
86
      obj = fs.getResultObjectFactory().createInstance(fs.getStatementId(), clazz);
1✔
87
      if (obj == null) {
1!
88
        obj = createObjectInternally(clazz);
×
89
      }
90
    }
91

92
    return obj;
1✔
93
  }
94

95
  /**
96
   * This method creates object using iBATIS' normal mechanism. We translate List and Collection to ArrayList, and Set
97
   * to HashSet because these interfaces may be requested in nested resultMaps and we want to supply default
98
   * implementations.
99
   *
100
   * @param clazz
101
   *          the clazz
102
   *
103
   * @return the object
104
   *
105
   * @throws InstantiationException
106
   *           the instantiation exception
107
   * @throws IllegalAccessException
108
   *           the illegal access exception
109
   */
110
  private static Object createObjectInternally(Class clazz) throws InstantiationException, IllegalAccessException {
111
    Class classToCreate;
112
    if (clazz == List.class || clazz == Collection.class) {
1✔
113
      classToCreate = ArrayList.class;
1✔
114
    } else if (clazz == Set.class) {
1!
115
      classToCreate = HashSet.class;
×
116
    } else {
117
      classToCreate = clazz;
1✔
118
    }
119

120
    return Resources.instantiate(classToCreate);
1✔
121
  }
122

123
  /**
124
   * This method pushes a new result object factory configuration onto the stack. We use a stack because the method can
125
   * be called in a "nested" fashion if there are sub-selects. Calls to this method should be equally balanced with
126
   * calls to cleanupResultObjectFactory().
127
   *
128
   * @param resultObjectFactory
129
   *          the result object factory
130
   * @param statementId
131
   *          the statement id
132
   */
133
  public static void setupResultObjectFactory(ResultObjectFactory resultObjectFactory, String statementId) {
134
    Deque<FactorySettings> fss = factorySettings.get();
1✔
135
    if (fss == null) {
1✔
136
      fss = new ArrayDeque<>();
1✔
137
      factorySettings.set(fss);
1✔
138
    }
139

140
    FactorySettings fs = new FactorySettings();
1✔
141
    fs.setResultObjectFactory(resultObjectFactory);
1✔
142
    fs.setStatementId(statementId);
1✔
143
    fss.push(fs);
1✔
144
  }
1✔
145

146
  /**
147
   * Removes the FactorySettings bound to the current thread to avoid classloader leak issues. This method pops the top
148
   * item off the stack, and kills the stack if there are no items left.
149
   */
150
  public static void cleanupResultObjectFactory() {
151
    Deque<FactorySettings> fss = factorySettings.get();
1✔
152
    if (!fss.isEmpty()) {
1!
153
      fss.pop();
1✔
154
    }
155

156
    if (fss.isEmpty()) {
1✔
157
      factorySettings.remove();
1✔
158
    }
159
  }
1✔
160

161
  /**
162
   * Gets the current factory settings.
163
   *
164
   * @return the current factory settings
165
   */
166
  private static FactorySettings getCurrentFactorySettings() {
167
    Deque<FactorySettings> fss = factorySettings.get();
1✔
168
    FactorySettings fs;
169
    if (fss == null || fss.isEmpty()) {
1!
170
      // this shouldn't happen if the SqlExecuter is behaving correctly
171
      fs = new FactorySettings();
1✔
172
    } else {
173
      fs = fss.peek();
1✔
174
    }
175

176
    return fs;
1✔
177
  }
178

179
  /**
180
   * The Class FactorySettings.
181
   */
182
  private static class FactorySettings {
183

184
    /** The result object factory. */
185
    private ResultObjectFactory resultObjectFactory;
186

187
    /** The statement id. */
188
    private String statementId;
189

190
    /**
191
     * Gets the result object factory.
192
     *
193
     * @return the result object factory
194
     */
195
    public ResultObjectFactory getResultObjectFactory() {
196
      return resultObjectFactory;
1✔
197
    }
198

199
    /**
200
     * Sets the result object factory.
201
     *
202
     * @param resultObjectFactory
203
     *          the new result object factory
204
     */
205
    public void setResultObjectFactory(ResultObjectFactory resultObjectFactory) {
206
      this.resultObjectFactory = resultObjectFactory;
1✔
207
    }
1✔
208

209
    /**
210
     * Gets the statement id.
211
     *
212
     * @return the statement id
213
     */
214
    public String getStatementId() {
215
      return statementId;
1✔
216
    }
217

218
    /**
219
     * Sets the statement id.
220
     *
221
     * @param statementId
222
     *          the new statement id
223
     */
224
    public void setStatementId(String statementId) {
225
      this.statementId = statementId;
1✔
226
    }
1✔
227
  }
228
}
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