View Javadoc
1   /**
2    *    Copyright 2009-2020 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    *       http://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.submitted.sqlprovider;
17  
18  import java.lang.reflect.Field;
19  import java.lang.reflect.Method;
20  import java.lang.reflect.ParameterizedType;
21  import java.lang.reflect.Type;
22  import java.util.LinkedHashMap;
23  import java.util.List;
24  import java.util.Map;
25  
26  import org.apache.ibatis.annotations.Param;
27  import org.apache.ibatis.builder.annotation.ProviderContext;
28  import org.apache.ibatis.jdbc.SQL;
29  
30  public class OurSqlBuilder {
31  
32    public String buildGetUsersQuery(Map<String, Object> parameter) {
33      // MyBatis wraps a single List parameter in a Map with the key="list",
34      // so need to pull it out
35      @SuppressWarnings("unchecked")
36      List<Integer> ids = (List<Integer>) parameter.get("list");
37      StringBuilder sb = new StringBuilder();
38      sb.append("select * from users where id in (");
39      for (int i = 0; i < ids.size(); i++) {
40        if (i > 0) {
41          sb.append(",");
42        }
43        sb.append("#{list[");
44        sb.append(i);
45        sb.append("]}");
46      }
47      sb.append(") order by id");
48      return sb.toString();
49    }
50  
51    public String buildGetUserQuery(Number parameter) {
52      // parameter is not a single List or Array,
53      // so it is passed as is from the mapper
54      return "select * from users where id = #{value}";
55    }
56  
57    public String buildGetAllUsersQuery() {
58      return "select * from users order by id";
59    }
60  
61    public String buildGetUsersByCriteriaQuery(final User criteria) {
62      return new SQL(){{
63        SELECT("*");
64        FROM("users");
65        if (criteria.getId() != null) {
66          WHERE("id = #{id}");
67        }
68        if (criteria.getName() != null) {
69          WHERE("name like #{name} || '%'");
70        }
71      }}.toString();
72    }
73  
74    public String buildGetUsersByCriteriaMapQuery(final Map<String, Object> criteria) {
75      return new SQL(){{
76        SELECT("*");
77        FROM("users");
78        if (criteria.get("id") != null) {
79          WHERE("id = #{id}");
80        }
81        if (criteria.get("name") != null) {
82          WHERE("name like #{name} || '%'");
83        }
84      }}.toString();
85    }
86  
87    public String buildGetUsersByCriteriaMapWithParamQuery(@Param("id") Integer id, @Param("name") String name) {
88      return new SQL() {{
89        SELECT("*");
90        FROM("users");
91        if (id != null) {
92          WHERE("id = #{id}");
93        }
94        if (name != null) {
95          WHERE("name like #{name} || '%'");
96        }
97      }}.toString();
98    }
99  
100   public String buildGetUsersByNameQuery(final String name, final String orderByColumn) {
101     return new SQL(){{
102       SELECT("*");
103       FROM("users");
104       if (name != null) {
105         WHERE("name like #{param1} || '%'");
106       }
107       ORDER_BY(orderByColumn);
108     }}.toString();
109   }
110 
111   public String buildGetUsersByNameUsingMap(Map<String, Object> params) {
112     final String name = String.class.cast(params.get("param1"));
113     final String orderByColumn = String.class.cast(params.get("param2"));
114     return new SQL(){{
115       SELECT("*");
116       FROM("users");
117       if (name != null) {
118         WHERE("name like #{param1} || '%'");
119       }
120       ORDER_BY(orderByColumn);
121     }}.toString();
122   }
123 
124   public String buildGetUsersByNameWithParamNameAndOrderByQuery(@Param("orderByColumn") final String orderByColumn, @Param("name") final String name) {
125     return new SQL(){{
126       SELECT("*");
127       FROM("users");
128       if (name != null) {
129         WHERE("name like #{name} || '%'");
130       }
131       ORDER_BY(orderByColumn);
132     }}.toString();
133   }
134 
135   public String buildGetUsersByNameWithParamNameQuery(@Param("name") final String name) {
136     return new SQL(){{
137       SELECT("*");
138       FROM("users");
139       if (name != null) {
140         WHERE("name like #{name} || '%'");
141       }
142       ORDER_BY("id DESC");
143     }}.toString();
144   }
145 
146   public String buildGetUsersByNameWithParamNameQueryUsingMap(Map<String, Object> params) {
147     final String name = String.class.cast(params.get("name"));
148     final String orderByColumn = String.class.cast(params.get("orderByColumn"));
149     return new SQL(){{
150       SELECT("*");
151       FROM("users");
152       if (name != null) {
153         WHERE("name like #{param1} || '%'");
154       }
155       ORDER_BY(orderByColumn);
156     }}.toString();
157   }
158 
159   public String buildInsert() {
160     return "insert into users (id, name) values (#{id}, #{name})";
161   }
162 
163   public String buildUpdate() {
164     return "update users set name = #{name} where id = #{id}";
165   }
166 
167   public String buildDelete() {
168     return "delete from users where id = #{id}";
169   }
170 
171   public String buildSelectByIdProviderContextOnly(ProviderContext context) {
172     final boolean containsLogicalDelete = context.getMapperMethod().getAnnotation(BaseMapper.ContainsLogicalDelete.class) != null;
173     final String tableName = context.getMapperType().getAnnotation(BaseMapper.Meta.class).tableName();
174     return new SQL(){{
175       SELECT("*");
176       FROM(tableName);
177       WHERE("id = #{id}");
178       if (!containsLogicalDelete){
179         WHERE("logical_delete = ${Constants.LOGICAL_DELETE_OFF}");
180       }
181     }}.toString();
182   }
183 
184   public String buildSelectByNameOneParamAndProviderContext(ProviderContext context, final String name) {
185     final boolean containsLogicalDelete = context.getMapperMethod().getAnnotation(BaseMapper.ContainsLogicalDelete.class) != null;
186     final String tableName = context.getMapperType().getAnnotation(BaseMapper.Meta.class).tableName();
187     return new SQL(){{
188       SELECT("*");
189       FROM(tableName);
190       if (name != null) {
191         WHERE("name like #{name} || '%'");
192       }
193       if (!containsLogicalDelete){
194         WHERE("logical_delete = ${LOGICAL_DELETE_OFF:0}");
195       }
196     }}.toString();
197   }
198 
199   public String buildSelectByIdAndNameMultipleParamAndProviderContextWithAtParam(@Param("id") final Integer id, ProviderContext context, @Param("name") final String name) {
200     final boolean containsLogicalDelete = context.getMapperMethod().getAnnotation(BaseMapper.ContainsLogicalDelete.class) != null;
201     final String tableName = context.getMapperType().getAnnotation(BaseMapper.Meta.class).tableName();
202     return new SQL(){{
203       SELECT("*");
204       FROM(tableName);
205       if (id != null) {
206         WHERE("id = #{id}");
207       }
208       if (name != null) {
209         WHERE("name like #{name} || '%'");
210       }
211       if (!containsLogicalDelete){
212         WHERE("logical_delete = false");
213       }
214     }}.toString();
215   }
216 
217   public String buildSelectByIdAndNameMultipleParamAndProviderContext(final Integer id, final String name, ProviderContext context) {
218     final boolean containsLogicalDelete = context.getMapperMethod().getAnnotation(BaseMapper.ContainsLogicalDelete.class) != null;
219     final String tableName = context.getMapperType().getAnnotation(BaseMapper.Meta.class).tableName();
220     return new SQL(){{
221       SELECT("*");
222       FROM(tableName);
223       if (id != null) {
224         WHERE("id = #{param1}");
225       }
226       if (name != null) {
227         WHERE("name like #{param2} || '%'");
228       }
229       if (!containsLogicalDelete){
230         WHERE("logical_delete = false");
231       }
232     }}.toString();
233   }
234 
235   private Class<?> getEntityClass(ProviderContext providerContext) {
236     Method mapperMethod = providerContext.getMapperMethod();
237     Class<?> declaringClass = mapperMethod.getDeclaringClass();
238     Class<?> mapperClass = providerContext.getMapperType();
239 
240     Type[] types = mapperClass.getGenericInterfaces();
241     for (Type type : types) {
242       if (type instanceof ParameterizedType) {
243         ParameterizedType t = (ParameterizedType) type;
244         if (t.getRawType() == declaringClass || mapperClass.isAssignableFrom((Class<?>) t.getRawType())) {
245           Class<?> returnType = (Class<?>) t.getActualTypeArguments()[0];
246           return returnType;
247         }
248       }
249     }
250     throw new RuntimeException("The interface [" + mapperClass.getCanonicalName() + "] must specify a generic type.");
251   }
252 
253   private Map<String, String> getColumnMap(ProviderContext context) {
254     Class<?> entityClass = getEntityClass(context);
255     Field[] fields = entityClass.getDeclaredFields();
256     Map<String, String> columnMap = new LinkedHashMap<String, String>();
257     for (Field field : fields) {
258       BaseMapper.Column column = field.getAnnotation(BaseMapper.Column.class);
259       if (column != null) {
260         String columnName = column.value();
261         if (columnName == null || columnName.length() == 0) {
262           columnName = field.getName();
263         }
264         columnMap.put(columnName, field.getName());
265       }
266     }
267     if (columnMap.size() == 0) {
268       throw new RuntimeException("There is no field in the class [" + entityClass.getCanonicalName()
269           + "] that specifies the @BaseMapper.Column annotation.");
270     }
271     return columnMap;
272   }
273 
274   public String buildInsertSelective(ProviderContext context) {
275     final String tableName = context.getMapperType().getAnnotation(BaseMapper.Meta.class).tableName();
276     Map<String, String> columnMap = getColumnMap(context);
277     StringBuilder sqlBuffer = new StringBuilder();
278     sqlBuffer.append("<script>");
279     sqlBuffer.append("insert into ");
280     sqlBuffer.append(tableName);
281     sqlBuffer.append("<trim prefix=\"(\" suffix=\")\" suffixOverrides=\",\">");
282     for (Map.Entry<String, String> entry : columnMap.entrySet()) {
283       sqlBuffer.append("<if test=\"").append(entry.getValue()).append(" != null\">");
284       sqlBuffer.append(entry.getKey()).append(",");
285       sqlBuffer.append("</if>");
286     }
287     sqlBuffer.append("</trim>");
288     sqlBuffer.append("<trim prefix=\"VALUES (\" suffix=\")\" suffixOverrides=\",\">");
289     for (String field : columnMap.values()) {
290       sqlBuffer.append("<if test=\"").append(field).append(" != null\">");
291       sqlBuffer.append("#{").append(field).append("} ,");
292       sqlBuffer.append("</if>");
293     }
294     sqlBuffer.append("</trim>");
295     sqlBuffer.append("</script>");
296     return sqlBuffer.toString();
297   }
298 
299   public String buildUpdateSelective(ProviderContext context) {
300     final String tableName = context.getMapperType().getAnnotation(BaseMapper.Meta.class).tableName();
301     Map<String, String> columnMap = getColumnMap(context);
302     StringBuilder sqlBuffer = new StringBuilder();
303     sqlBuffer.append("<script>");
304     sqlBuffer.append("update ");
305     sqlBuffer.append(tableName);
306     sqlBuffer.append("<set>");
307     for (Map.Entry<String, String> entry : columnMap.entrySet()) {
308       sqlBuffer.append("<if test=\"").append(entry.getValue()).append(" != null\">");
309       sqlBuffer.append(entry.getKey()).append(" = #{").append(entry.getValue()).append("} ,");
310       sqlBuffer.append("</if>");
311     }
312     sqlBuffer.append("</set>");
313     // For simplicity, there is no @Id annotation here, using default id directly
314     sqlBuffer.append("where id = #{id}");
315     sqlBuffer.append("</script>");
316     return sqlBuffer.toString();
317   }
318 
319   public String buildGetByEntityQuery(ProviderContext context) {
320     final String tableName = context.getMapperType().getAnnotation(BaseMapper.Meta.class).tableName();
321     Map<String, String> columnMap = getColumnMap(context);
322     StringBuilder sqlBuffer = new StringBuilder();
323     sqlBuffer.append("<script>");
324     sqlBuffer.append("select * from ");
325     sqlBuffer.append(tableName);
326     sqlBuffer.append("<where>");
327     for (Map.Entry<String, String> entry : columnMap.entrySet()) {
328       sqlBuffer.append("<if test=\"").append(entry.getValue()).append(" != null\">");
329       sqlBuffer.append("and ").append(entry.getKey()).append(" = #{").append(entry.getValue()).append("}");
330       sqlBuffer.append("</if>");
331     }
332     sqlBuffer.append("</where>");
333     sqlBuffer.append("</script>");
334     return sqlBuffer.toString();
335   }
336 
337 }