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

mybatis / mybatis-3 / 2686

01 Feb 2025 09:55PM UTC coverage: 87.093% (-0.1%) from 87.217%
2686

Pull #3379

github

web-flow
Merge c97c5c598 into 3d71c862a
Pull Request #3379: Resolve type handler based on `java.lang.reflect.Type` instead of `Class` and respect runtime JDBC type

3825 of 4663 branches covered (82.03%)

515 of 579 new or added lines in 36 files covered. (88.95%)

28 existing lines in 6 files now uncovered.

9912 of 11381 relevant lines covered (87.09%)

0.87 hits per line

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

84.54
/src/main/java/org/apache/ibatis/scripting/defaults/DefaultParameterHandler.java
1
/*
2
 *    Copyright 2009-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 org.apache.ibatis.scripting.defaults;
17

18
import java.lang.reflect.Type;
19
import java.sql.ParameterMetaData;
20
import java.sql.PreparedStatement;
21
import java.sql.SQLException;
22
import java.util.HashMap;
23
import java.util.List;
24
import java.util.Map.Entry;
25

26
import org.apache.ibatis.binding.MapperMethod.ParamMap;
27
import org.apache.ibatis.executor.ErrorContext;
28
import org.apache.ibatis.executor.parameter.ParameterHandler;
29
import org.apache.ibatis.mapping.BoundSql;
30
import org.apache.ibatis.mapping.MappedStatement;
31
import org.apache.ibatis.mapping.ParameterMapping;
32
import org.apache.ibatis.mapping.ParameterMode;
33
import org.apache.ibatis.reflection.MetaClass;
34
import org.apache.ibatis.reflection.MetaObject;
35
import org.apache.ibatis.reflection.ParamNameResolver;
36
import org.apache.ibatis.reflection.property.PropertyTokenizer;
37
import org.apache.ibatis.session.Configuration;
38
import org.apache.ibatis.type.JdbcType;
39
import org.apache.ibatis.type.ObjectTypeHandler;
40
import org.apache.ibatis.type.TypeException;
41
import org.apache.ibatis.type.TypeHandler;
42
import org.apache.ibatis.type.TypeHandlerRegistry;
43

44
/**
45
 * @author Clinton Begin
46
 * @author Eduardo Macarron
47
 */
48
public class DefaultParameterHandler implements ParameterHandler {
49

50
  private final TypeHandlerRegistry typeHandlerRegistry;
51

52
  private final MappedStatement mappedStatement;
53
  private final Object parameterObject;
54
  private final BoundSql boundSql;
55
  private final Configuration configuration;
56

57
  private ParameterMetaData paramMetaData;
58
  private MetaObject paramMetaObject;
59
  private HashMap<Class<?>, MetaClass> metaClassCache = new HashMap<>();
1✔
60
  private static final TypeHandler<?> nullTypeHandler = new ObjectTypeHandler();
1✔
61
  private static final ParameterMetaData nullParameterMetaData = new ParameterMetaData() {
1✔
62
    // @formatter:off
NEW
63
    public <T> T unwrap(Class<T> iface) throws SQLException { return null; }
×
NEW
64
    public boolean isWrapperFor(Class<?> iface) throws SQLException { return false; }
×
NEW
65
    public boolean isSigned(int param) throws SQLException { return false; }
×
NEW
66
    public int isNullable(int param) throws SQLException { return 0; }
×
NEW
67
    public int getScale(int param) throws SQLException { return 0; }
×
NEW
68
    public int getPrecision(int param) throws SQLException { return 0; }
×
NEW
69
    public String getParameterTypeName(int param) throws SQLException { return null; }
×
NEW
70
    public int getParameterType(int param) throws SQLException { return 0; }
×
NEW
71
    public int getParameterMode(int param) throws SQLException { return 0; }
×
NEW
72
    public int getParameterCount() throws SQLException { return 0; }
×
NEW
73
    public String getParameterClassName(int param) throws SQLException { return null; }
×
74
    // @formatter:on
75
  };
76

77
  public DefaultParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
1✔
78
    this.mappedStatement = mappedStatement;
1✔
79
    this.configuration = mappedStatement.getConfiguration();
1✔
80
    this.typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry();
1✔
81
    this.parameterObject = parameterObject;
1✔
82
    this.boundSql = boundSql;
1✔
83
  }
1✔
84

85
  @Override
86
  public Object getParameterObject() {
87
    return parameterObject;
1✔
88
  }
89

90
  @SuppressWarnings({ "rawtypes", "unchecked" })
91
  @Override
92
  public void setParameters(PreparedStatement ps) {
93
    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
1✔
94
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
1✔
95
    if (parameterMappings != null) {
1!
96
      ParamNameResolver paramNameResolver = mappedStatement.getParamNameResolver();
1✔
97
      for (int i = 0; i < parameterMappings.size(); i++) {
1✔
98
        ParameterMapping parameterMapping = parameterMappings.get(i);
1✔
99
        if (parameterMapping.getMode() != ParameterMode.OUT) {
1✔
100
          Object value;
101
          String propertyName = parameterMapping.getProperty();
1✔
102
          JdbcType jdbcType = parameterMapping.getJdbcType();
1✔
103
          JdbcType actualJdbcType = jdbcType == null ? getParamJdbcType(ps, i + 1) : jdbcType;
1✔
104
          Type propertyGenericType = null;
1✔
105
          TypeHandler typeHandler = parameterMapping.getTypeHandler();
1✔
106
          if (parameterMapping.hasValue()) {
1✔
107
            value = parameterMapping.getValue();
1✔
108
          } else if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
1✔
109
            value = boundSql.getAdditionalParameter(propertyName);
1✔
110
            if (typeHandler == null) {
1✔
111
              typeHandler = typeHandlerRegistry.resolve(value.getClass(), null, null, actualJdbcType, null);
1✔
112
            }
113
          } else if (parameterObject == null) {
1✔
114
            value = null;
1✔
115
          } else {
116
            Class<? extends Object> parameterClass = parameterObject.getClass();
1✔
117
            TypeHandler paramTypeHandler = typeHandlerRegistry.getTypeHandler(parameterClass, actualJdbcType);
1✔
118
            if (paramTypeHandler != null) {
1✔
119
              value = parameterObject;
1✔
120
              typeHandler = paramTypeHandler;
1✔
121
            } else {
122
              paramMetaObject = getParamMetaObject();
1✔
123
              value = paramMetaObject.getValue(propertyName);
1✔
124
              if (typeHandler == null && value != null) {
1✔
125
                if (paramNameResolver != null && ParamMap.class.equals(parameterClass)) {
1✔
126
                  Type actualParamType = paramNameResolver.getType(propertyName);
1✔
127
                  if (actualParamType instanceof Class) {
1!
128
                    Class<?> actualParamClass = (Class<?>) actualParamType;
1✔
129
                    MetaClass metaClass = metaClassCache.computeIfAbsent(actualParamClass,
1✔
130
                        k -> MetaClass.forClass(k, configuration.getReflectorFactory()));
1✔
131
                    PropertyTokenizer propertyTokenizer = new PropertyTokenizer(propertyName);
1✔
132
                    String multiParamsPropertyName;
133
                    if (propertyTokenizer.hasNext()) {
1✔
134
                      multiParamsPropertyName = propertyTokenizer.getChildren();
1✔
135
                      if (metaClass.hasGetter(multiParamsPropertyName)) {
1!
136
                        Entry<Type, Class<?>> getterType = metaClass.getGenericGetterType(multiParamsPropertyName);
1✔
137
                        propertyGenericType = getterType.getKey();
1✔
138
                      }
1✔
139
                    } else {
140
                      propertyGenericType = actualParamClass;
1✔
141
                    }
142
                  }
143
                } else {
1✔
144
                  try {
145
                    propertyGenericType = paramMetaObject.getGenericGetterType(propertyName).getKey();
1✔
146
                    typeHandler = configuration.getTypeHandlerRegistry().resolve(parameterClass, propertyGenericType,
1✔
147
                        propertyName, actualJdbcType, null);
148
                  } catch (Exception e) {
1✔
149
                    // Not always resolvable
150
                  }
1✔
151
                }
152
              }
153
            }
154
          }
155
          if (value == null) {
1✔
156
            if (jdbcType == null) {
1✔
157
              jdbcType = configuration.getJdbcTypeForNull();
1✔
158
            }
159
            if (typeHandler == null) {
1✔
160
              typeHandler = nullTypeHandler;
1✔
161
            }
162
          } else if (typeHandler == null) {
1✔
163
            if (propertyGenericType == null) {
1✔
164
              propertyGenericType = value.getClass();
1✔
165
            }
166
            typeHandler = typeHandlerRegistry.resolve(parameterObject.getClass(), propertyGenericType, propertyName,
1✔
167
                actualJdbcType, null);
168
          }
169
          if (typeHandler == null) {
1✔
170
            typeHandler = typeHandlerRegistry.getTypeHandler(actualJdbcType);
1✔
171
          }
172
          try {
173
            typeHandler.setParameter(ps, i + 1, value, jdbcType);
1✔
174
          } catch (TypeException | SQLException e) {
1✔
175
            throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
1✔
176
          }
1✔
177
        }
178
      }
179
    }
180
  }
1✔
181

182
  private MetaObject getParamMetaObject() {
183
    if (paramMetaObject != null) {
1✔
184
      return paramMetaObject;
1✔
185
    }
186
    paramMetaObject = configuration.newMetaObject(parameterObject);
1✔
187
    return paramMetaObject;
1✔
188
  }
189

190
  private JdbcType getParamJdbcType(PreparedStatement ps, int paramIndex) {
191
    try {
192
      if (paramMetaData == null) {
1✔
193
        try {
194
          paramMetaData = ps.getParameterMetaData();
1✔
NEW
195
        } catch (SQLException e) {
×
NEW
196
          paramMetaData = nullParameterMetaData;
×
197
        }
1✔
198
      }
199
      return JdbcType.forCode(paramMetaData.getParameterType(paramIndex));
1✔
NEW
200
    } catch (SQLException e) {
×
NEW
201
      return null;
×
202
    }
203
  }
204

205
}
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