View Javadoc
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.beans.ClassInfo;
19  import com.ibatis.common.beans.Probe;
20  import com.ibatis.common.beans.ProbeFactory;
21  import com.ibatis.sqlmap.client.SqlMapException;
22  import com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate;
23  import com.ibatis.sqlmap.engine.scope.StatementScope;
24  import com.ibatis.sqlmap.engine.type.DomTypeMarker;
25  
26  import java.sql.ResultSet;
27  import java.sql.ResultSetMetaData;
28  import java.sql.SQLException;
29  import java.util.ArrayList;
30  import java.util.HashMap;
31  import java.util.List;
32  import java.util.Map;
33  
34  /**
35   * An automatic result map for simple stuff.
36   */
37  public class AutoResultMap extends ResultMap {
38  
39    /**
40     * Constructor to pass in the SqlMapExecutorDelegate.
41     *
42     * @param delegate
43     *          - the delegate
44     * @param allowRemapping
45     *          the allow remapping
46     */
47    public AutoResultMap(SqlMapExecutorDelegate delegate, boolean allowRemapping) {
48      super(delegate);
49      this.allowRemapping = allowRemapping;
50    }
51  
52    @Override
53    public synchronized Object[] getResults(StatementScope statementScope, ResultSet rs) throws SQLException {
54      if (allowRemapping || getResultMappings() == null) {
55        initialize(rs);
56      }
57      return super.getResults(statementScope, rs);
58    }
59  
60    @Override
61    public Object setResultObjectValues(StatementScope statementScope, Object resultObject, Object[] values) {
62      // synchronization is only needed when remapping is enabled
63      if (allowRemapping) {
64        synchronized (this) {
65          return super.setResultObjectValues(statementScope, resultObject, values);
66        }
67      }
68      return super.setResultObjectValues(statementScope, resultObject, values);
69    }
70  
71    /**
72     * Initialize.
73     *
74     * @param rs
75     *          the rs
76     */
77    private void initialize(ResultSet rs) {
78      if (getResultClass() == null) {
79        throw new SqlMapException(
80            "The automatic ResultMap named " + this.getId() + " had a null result class (not allowed).");
81      }
82      if (Map.class.isAssignableFrom(getResultClass())) {
83        initializeMapResults(rs);
84      } else if (getDelegate().getTypeHandlerFactory().getTypeHandler(getResultClass()) != null) {
85        initializePrimitiveResults(rs);
86      } else if (DomTypeMarker.class.isAssignableFrom(getResultClass())) {
87        initializeXmlResults(rs);
88      } else {
89        initializeBeanResults(rs);
90      }
91    }
92  
93    /**
94     * Initialize bean results.
95     *
96     * @param rs
97     *          the rs
98     */
99    private void initializeBeanResults(ResultSet rs) {
100     try {
101       ClassInfo classInfo = ClassInfo.getInstance(getResultClass());
102       String[] propertyNames = classInfo.getWriteablePropertyNames();
103 
104       Map propertyMap = new HashMap<>();
105       for (String propertyName : propertyNames) {
106         propertyMap.put(propertyName.toUpperCase(java.util.Locale.ENGLISH), propertyName);
107       }
108 
109       List resultMappingList = new ArrayList<>();
110       ResultSetMetaData rsmd = rs.getMetaData();
111       for (int i = 0, n = rsmd.getColumnCount(); i < n; i++) {
112         String columnName = getColumnIdentifier(rsmd, i + 1);
113         String upperColumnName = columnName.toUpperCase(java.util.Locale.ENGLISH);
114         String matchedProp = (String) propertyMap.get(upperColumnName);
115         Class type = null;
116         if (matchedProp == null) {
117           Probe p = ProbeFactory.getProbe(this.getResultClass());
118           try {
119             type = p.getPropertyTypeForSetter(this.getResultClass(), columnName);
120           } catch (Exception e) {
121             // TODO - add logging to this class?
122           }
123         } else {
124           type = classInfo.getSetterType(matchedProp);
125         }
126         if (type != null || matchedProp != null) {
127           ResultMapping resultMapping = new ResultMapping();
128           resultMapping.setPropertyName(matchedProp != null ? matchedProp : columnName);
129           resultMapping.setColumnName(columnName);
130           resultMapping.setColumnIndex(i + 1);
131           // map SQL to JDBC type
132           resultMapping.setTypeHandler(getDelegate().getTypeHandlerFactory().getTypeHandler(type));
133           resultMappingList.add(resultMapping);
134         }
135       }
136       setResultMappingList(resultMappingList);
137 
138     } catch (SQLException e) {
139       throw new RuntimeException("Error automapping columns. Cause: " + e);
140     }
141 
142   }
143 
144   /**
145    * Initialize xml results.
146    *
147    * @param rs
148    *          the rs
149    */
150   private void initializeXmlResults(ResultSet rs) {
151     try {
152       List resultMappingList = new ArrayList<>();
153       ResultSetMetaData rsmd = rs.getMetaData();
154       for (int i = 0, n = rsmd.getColumnCount(); i < n; i++) {
155         String columnName = getColumnIdentifier(rsmd, i + 1);
156         ResultMapping resultMapping = new ResultMapping();
157         resultMapping.setPropertyName(columnName);
158         resultMapping.setColumnName(columnName);
159         resultMapping.setColumnIndex(i + 1);
160         resultMapping.setTypeHandler(getDelegate().getTypeHandlerFactory().getTypeHandler(String.class));
161         resultMappingList.add(resultMapping);
162       }
163       setResultMappingList(resultMappingList);
164     } catch (SQLException e) {
165       throw new RuntimeException("Error automapping columns. Cause: " + e);
166     }
167   }
168 
169   /**
170    * Initialize map results.
171    *
172    * @param rs
173    *          the rs
174    */
175   private void initializeMapResults(ResultSet rs) {
176     try {
177       List resultMappingList = new ArrayList<>();
178       ResultSetMetaData rsmd = rs.getMetaData();
179       for (int i = 0, n = rsmd.getColumnCount(); i < n; i++) {
180         String columnName = getColumnIdentifier(rsmd, i + 1);
181         ResultMapping resultMapping = new ResultMapping();
182         resultMapping.setPropertyName(columnName);
183         resultMapping.setColumnName(columnName);
184         resultMapping.setColumnIndex(i + 1);
185         resultMapping.setTypeHandler(getDelegate().getTypeHandlerFactory().getTypeHandler(Object.class));
186         resultMappingList.add(resultMapping);
187       }
188 
189       setResultMappingList(resultMappingList);
190 
191     } catch (SQLException e) {
192       throw new RuntimeException("Error automapping columns. Cause: " + e);
193     }
194   }
195 
196   /**
197    * Initialize primitive results.
198    *
199    * @param rs
200    *          the rs
201    */
202   private void initializePrimitiveResults(ResultSet rs) {
203     try {
204       ResultSetMetaData rsmd = rs.getMetaData();
205       String columnName = getColumnIdentifier(rsmd, 1);
206       ResultMapping resultMapping = new ResultMapping();
207       resultMapping.setPropertyName(columnName);
208       resultMapping.setColumnName(columnName);
209       resultMapping.setColumnIndex(1);
210       resultMapping.setTypeHandler(getDelegate().getTypeHandlerFactory().getTypeHandler(getResultClass()));
211 
212       List resultMappingList = new ArrayList<>();
213       resultMappingList.add(resultMapping);
214 
215       setResultMappingList(resultMappingList);
216 
217     } catch (SQLException e) {
218       throw new RuntimeException("Error automapping columns. Cause: " + e);
219     }
220   }
221 
222   /**
223    * Gets the column identifier.
224    *
225    * @param rsmd
226    *          the rsmd
227    * @param i
228    *          the i
229    *
230    * @return the column identifier
231    *
232    * @throws SQLException
233    *           the SQL exception
234    */
235   private String getColumnIdentifier(ResultSetMetaData rsmd, int i) throws SQLException {
236     if (delegate.isUseColumnLabel()) {
237       return rsmd.getColumnLabel(i);
238     }
239     return rsmd.getColumnName(i);
240   }
241 
242 }