View Javadoc
1   /*
2    *    Copyright 2006-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.generator.codegen.mybatis3.javamapper.elements;
17  
18  import static org.mybatis.generator.api.dom.OutputUtilities.javaIndent;
19  import static org.mybatis.generator.codegen.mybatis3.MyBatis3FormattingUtilities.getEscapedColumnName;
20  import static org.mybatis.generator.codegen.mybatis3.MyBatis3FormattingUtilities.getParameterClause;
21  import static org.mybatis.generator.codegen.mybatis3.MyBatis3FormattingUtilities.getRenamedColumnNameForResultMap;
22  import static org.mybatis.generator.codegen.mybatis3.MyBatis3FormattingUtilities.getSelectListPhrase;
23  import static org.mybatis.generator.internal.util.StringUtility.escapeStringForJava;
24  import static org.mybatis.generator.internal.util.StringUtility.stringHasValue;
25  
26  import java.util.ArrayList;
27  import java.util.Collections;
28  import java.util.HashSet;
29  import java.util.Iterator;
30  import java.util.List;
31  import java.util.Optional;
32  import java.util.Set;
33  
34  import org.mybatis.generator.api.IntrospectedColumn;
35  import org.mybatis.generator.api.dom.java.FullyQualifiedJavaType;
36  import org.mybatis.generator.api.dom.java.Interface;
37  import org.mybatis.generator.api.dom.java.JavaVisibility;
38  import org.mybatis.generator.api.dom.java.Method;
39  import org.mybatis.generator.api.dom.java.Parameter;
40  import org.mybatis.generator.codegen.AbstractGenerator;
41  import org.mybatis.generator.codegen.mybatis3.ListUtilities;
42  import org.mybatis.generator.config.GeneratedKey;
43  
44  public abstract class AbstractJavaMapperMethodGenerator extends AbstractGenerator {
45      public abstract void addInterfaceElements(Interface interfaze);
46  
47      protected AbstractJavaMapperMethodGenerator() {
48          super();
49      }
50  
51      protected static String getResultAnnotation(Interface interfaze, IntrospectedColumn introspectedColumn,
52              boolean idColumn, boolean constructorBased) {
53          StringBuilder sb = new StringBuilder();
54          if (constructorBased) {
55              interfaze.addImportedType(introspectedColumn.getFullyQualifiedJavaType());
56              sb.append("@Arg(column=\""); //$NON-NLS-1$
57              sb.append(getRenamedColumnNameForResultMap(introspectedColumn));
58              sb.append("\", javaType="); //$NON-NLS-1$
59              sb.append(introspectedColumn.getFullyQualifiedJavaType().getShortName());
60              sb.append(".class"); //$NON-NLS-1$
61          } else {
62              sb.append("@Result(column=\""); //$NON-NLS-1$
63              sb.append(getRenamedColumnNameForResultMap(introspectedColumn));
64              sb.append("\", property=\""); //$NON-NLS-1$
65              sb.append(introspectedColumn.getJavaProperty());
66              sb.append('\"');
67          }
68  
69          if (stringHasValue(introspectedColumn.getTypeHandler())) {
70              FullyQualifiedJavaType fqjt = new FullyQualifiedJavaType(introspectedColumn.getTypeHandler());
71              interfaze.addImportedType(fqjt);
72              sb.append(", typeHandler="); //$NON-NLS-1$
73              sb.append(fqjt.getShortName());
74              sb.append(".class"); //$NON-NLS-1$
75          }
76  
77          sb.append(", jdbcType=JdbcType."); //$NON-NLS-1$
78          sb.append(introspectedColumn.getJdbcTypeName());
79          if (idColumn) {
80              sb.append(", id=true"); //$NON-NLS-1$
81          }
82          sb.append(')');
83  
84          return sb.toString();
85      }
86  
87      protected Optional<String> buildGeneratedKeyAnnotation() {
88          return introspectedTable.getGeneratedKey().flatMap(this::buildGeneratedKeyAnnotation);
89      }
90  
91      private Optional<String> buildGeneratedKeyAnnotation(GeneratedKey gk) {
92          return introspectedTable.getColumn(gk.getColumn()).map(ic -> buildGeneratedKeyAnnotation(gk, ic));
93      }
94  
95      private String buildGeneratedKeyAnnotation(GeneratedKey gk, IntrospectedColumn introspectedColumn) {
96          StringBuilder sb = new StringBuilder();
97          if (gk.isJdbcStandard()) {
98              sb.append("@Options(useGeneratedKeys=true,keyProperty=\""); //$NON-NLS-1$
99              sb.append(introspectedColumn.getJavaProperty());
100             sb.append("\")"); //$NON-NLS-1$
101         } else {
102             sb.append("@SelectKey(statement=\""); //$NON-NLS-1$
103             sb.append(gk.getRuntimeSqlStatement());
104             sb.append("\", keyProperty=\""); //$NON-NLS-1$
105             sb.append(introspectedColumn.getJavaProperty());
106             sb.append("\", before="); //$NON-NLS-1$
107             sb.append(gk.isIdentity() ? "false" : "true"); //$NON-NLS-1$ //$NON-NLS-2$
108             sb.append(", resultType="); //$NON-NLS-1$
109             sb.append(introspectedColumn.getFullyQualifiedJavaType().getShortName());
110             sb.append(".class)"); //$NON-NLS-1$
111         }
112         return sb.toString();
113     }
114 
115     protected Set<FullyQualifiedJavaType> buildGeneratedKeyImportsIfRequired() {
116         return introspectedTable.getGeneratedKey().map(this::buildGeneratedKeyImportsIfRequired)
117                 .orElseGet(Collections::emptySet);
118     }
119 
120     private Set<FullyQualifiedJavaType> buildGeneratedKeyImportsIfRequired(GeneratedKey gk) {
121         return introspectedTable.getColumn(gk.getColumn()).map(ic -> buildGeneratedKeyImports(gk, ic))
122                 .orElseGet(Collections::emptySet);
123     }
124 
125     private Set<FullyQualifiedJavaType> buildGeneratedKeyImports(GeneratedKey gk,
126             IntrospectedColumn introspectedColumn) {
127         Set<FullyQualifiedJavaType> answer = new HashSet<>();
128         if (gk.isJdbcStandard()) {
129             answer.add(new FullyQualifiedJavaType("org.apache.ibatis.annotations.Options")); //$NON-NLS-1$
130         } else {
131             answer.add(new FullyQualifiedJavaType("org.apache.ibatis.annotations.SelectKey")); //$NON-NLS-1$
132             answer.add(introspectedColumn.getFullyQualifiedJavaType());
133         }
134 
135         return answer;
136     }
137 
138     protected void addAnnotatedSelectImports(Interface interfaze) {
139         interfaze.addImportedType(new FullyQualifiedJavaType("org.apache.ibatis.type.JdbcType")); //$NON-NLS-1$
140 
141         if (introspectedTable.isConstructorBased()) {
142             interfaze.addImportedType(new FullyQualifiedJavaType("org.apache.ibatis.annotations.Arg")); //$NON-NLS-1$
143             interfaze.addImportedType(
144                     new FullyQualifiedJavaType("org.apache.ibatis.annotations.ConstructorArgs")); //$NON-NLS-1$
145         } else {
146             interfaze.addImportedType(
147                     new FullyQualifiedJavaType("org.apache.ibatis.annotations.Result")); //$NON-NLS-1$
148             interfaze.addImportedType(
149                     new FullyQualifiedJavaType("org.apache.ibatis.annotations.Results")); //$NON-NLS-1$
150         }
151     }
152 
153     protected List<String> buildByPrimaryKeyWhereClause() {
154         List<String> answer = new ArrayList<>();
155         StringBuilder sb = new StringBuilder();
156         boolean and = false;
157         Iterator<IntrospectedColumn> iter = introspectedTable.getPrimaryKeyColumns().iterator();
158         while (iter.hasNext()) {
159             sb.setLength(0);
160             javaIndent(sb, 1);
161             if (and) {
162                 sb.append("  \"and "); //$NON-NLS-1$
163             } else {
164                 sb.append("\"where "); //$NON-NLS-1$
165                 and = true;
166             }
167 
168             IntrospectedColumn introspectedColumn = iter.next();
169             sb.append(escapeStringForJava(getEscapedColumnName(introspectedColumn)));
170             sb.append(" = "); //$NON-NLS-1$
171             sb.append(getParameterClause(introspectedColumn));
172             sb.append('\"');
173             if (iter.hasNext()) {
174                 sb.append(',');
175             }
176             answer.add(sb.toString());
177         }
178 
179         return answer;
180     }
181 
182     protected List<String> buildUpdateByPrimaryKeyAnnotations(List<IntrospectedColumn> columnList) {
183         List<String> answer = new ArrayList<>();
184         answer.add("@Update({"); //$NON-NLS-1$
185 
186         StringBuilder sb = new StringBuilder();
187         javaIndent(sb, 1);
188         sb.append("\"update "); //$NON-NLS-1$
189         sb.append(escapeStringForJava(introspectedTable.getFullyQualifiedTableNameAtRuntime()));
190         sb.append("\","); //$NON-NLS-1$
191         answer.add(sb.toString());
192 
193         // set up for first column
194         sb.setLength(0);
195         javaIndent(sb, 1);
196         sb.append("\"set "); //$NON-NLS-1$
197 
198         Iterator<IntrospectedColumn> iter = ListUtilities.removeGeneratedAlwaysColumns(columnList).iterator();
199         while (iter.hasNext()) {
200             IntrospectedColumn introspectedColumn = iter.next();
201 
202             sb.append(escapeStringForJava(getEscapedColumnName(introspectedColumn)));
203             sb.append(" = "); //$NON-NLS-1$
204             sb.append(getParameterClause(introspectedColumn));
205 
206             if (iter.hasNext()) {
207                 sb.append(',');
208             }
209 
210             sb.append("\","); //$NON-NLS-1$
211             answer.add(sb.toString());
212 
213             // set up for the next column
214             if (iter.hasNext()) {
215                 sb.setLength(0);
216                 javaIndent(sb, 1);
217                 sb.append("  \""); //$NON-NLS-1$
218             }
219         }
220 
221         answer.addAll(buildByPrimaryKeyWhereClause());
222 
223         answer.add("})"); //$NON-NLS-1$
224         return answer;
225     }
226 
227     protected void addPrimaryKeyMethodParameters(boolean isSimple, Method method,
228             Set<FullyQualifiedJavaType> importedTypes) {
229         if (!isSimple && introspectedTable.getRules().generatePrimaryKeyClass()) {
230             FullyQualifiedJavaType type = new FullyQualifiedJavaType(introspectedTable.getPrimaryKeyType());
231             importedTypes.add(type);
232             method.addParameter(new Parameter(type, "key")); //$NON-NLS-1$
233         } else {
234             // no primary key class - fields are in the base class
235             // if more than one PK field, then we need to annotate the
236             // parameters for MyBatis3
237             List<IntrospectedColumn> introspectedColumns = introspectedTable.getPrimaryKeyColumns();
238             boolean annotate = introspectedColumns.size() > 1;
239             if (annotate) {
240                 importedTypes.add(new FullyQualifiedJavaType("org.apache.ibatis.annotations.Param")); //$NON-NLS-1$
241             }
242             StringBuilder sb = new StringBuilder();
243             for (IntrospectedColumn introspectedColumn : introspectedColumns) {
244                 FullyQualifiedJavaType type = introspectedColumn.getFullyQualifiedJavaType();
245                 importedTypes.add(type);
246                 Parameter parameter = new Parameter(type, introspectedColumn.getJavaProperty());
247                 if (annotate) {
248                     sb.setLength(0);
249                     sb.append("@Param(\""); //$NON-NLS-1$
250                     sb.append(introspectedColumn.getJavaProperty());
251                     sb.append("\")"); //$NON-NLS-1$
252                     parameter.addAnnotation(sb.toString());
253                 }
254                 method.addParameter(parameter);
255             }
256         }
257     }
258 
259     protected void addAnnotatedResults(Interface interfaze, Method method,
260             List<IntrospectedColumn> nonPrimaryKeyColumns) {
261 
262         if (introspectedTable.isConstructorBased()) {
263             method.addAnnotation("@ConstructorArgs({"); //$NON-NLS-1$
264         } else {
265             method.addAnnotation("@Results({"); //$NON-NLS-1$
266         }
267 
268         StringBuilder sb = new StringBuilder();
269 
270         Iterator<IntrospectedColumn> iterPk = introspectedTable.getPrimaryKeyColumns().iterator();
271         Iterator<IntrospectedColumn> iterNonPk = nonPrimaryKeyColumns.iterator();
272         while (iterPk.hasNext()) {
273             IntrospectedColumn introspectedColumn = iterPk.next();
274             sb.setLength(0);
275             javaIndent(sb, 1);
276             sb.append(getResultAnnotation(interfaze, introspectedColumn, true,
277                     introspectedTable.isConstructorBased()));
278 
279             if (iterPk.hasNext() || iterNonPk.hasNext()) {
280                 sb.append(',');
281             }
282 
283             method.addAnnotation(sb.toString());
284         }
285 
286         while (iterNonPk.hasNext()) {
287             IntrospectedColumn introspectedColumn = iterNonPk.next();
288             sb.setLength(0);
289             javaIndent(sb, 1);
290             sb.append(getResultAnnotation(interfaze, introspectedColumn, false,
291                     introspectedTable.isConstructorBased()));
292 
293             if (iterNonPk.hasNext()) {
294                 sb.append(',');
295             }
296 
297             method.addAnnotation(sb.toString());
298         }
299 
300         method.addAnnotation("})"); //$NON-NLS-1$
301     }
302 
303     protected Method buildBasicUpdateByExampleMethod(String statementId, FullyQualifiedJavaType parameterType,
304             Set<FullyQualifiedJavaType> importedTypes) {
305         Method method = new Method(statementId);
306         method.setVisibility(JavaVisibility.PUBLIC);
307         method.setAbstract(true);
308         method.setReturnType(FullyQualifiedJavaType.getIntInstance());
309 
310         method.addParameter(new Parameter(parameterType, "row", "@Param(\"row\")")); //$NON-NLS-1$ //$NON-NLS-2$
311 
312         importedTypes.add(parameterType);
313 
314         FullyQualifiedJavaType exampleType = new FullyQualifiedJavaType(introspectedTable.getExampleType());
315         method.addParameter(new Parameter(exampleType, "example", "@Param(\"example\")")); //$NON-NLS-1$ //$NON-NLS-2$
316         importedTypes.add(exampleType);
317 
318         importedTypes.add(new FullyQualifiedJavaType("org.apache.ibatis.annotations.Param")); //$NON-NLS-1$
319 
320         context.getCommentGenerator().addGeneralMethodComment(method, introspectedTable);
321 
322         return method;
323     }
324 
325     protected Method buildBasicUpdateByPrimaryKeyMethod(String statementId, FullyQualifiedJavaType parameterType) {
326         Method method = new Method(statementId);
327         method.setVisibility(JavaVisibility.PUBLIC);
328         method.setAbstract(true);
329         method.setReturnType(FullyQualifiedJavaType.getIntInstance());
330         method.addParameter(new Parameter(parameterType, "row")); //$NON-NLS-1$
331 
332         context.getCommentGenerator().addGeneralMethodComment(method, introspectedTable);
333         return method;
334     }
335 
336     protected List<String> buildInitialSelectAnnotationStrings() {
337         List<String> answer = new ArrayList<>();
338         answer.add("@Select({"); //$NON-NLS-1$
339         StringBuilder sb = new StringBuilder();
340         javaIndent(sb, 1);
341         sb.append("\"select\","); //$NON-NLS-1$
342         answer.add(sb.toString());
343 
344         sb.setLength(0);
345         javaIndent(sb, 1);
346         sb.append('"');
347         boolean hasColumns = false;
348         Iterator<IntrospectedColumn> iter = introspectedTable.getAllColumns().iterator();
349         while (iter.hasNext()) {
350             sb.append(escapeStringForJava(getSelectListPhrase(iter.next())));
351             hasColumns = true;
352 
353             if (iter.hasNext()) {
354                 sb.append(", "); //$NON-NLS-1$
355             }
356 
357             if (sb.length() > 80) {
358                 sb.append("\","); //$NON-NLS-1$
359                 answer.add(sb.toString());
360 
361                 sb.setLength(0);
362                 javaIndent(sb, 1);
363                 sb.append('"');
364                 hasColumns = false;
365             }
366         }
367 
368         if (hasColumns) {
369             sb.append("\","); //$NON-NLS-1$
370             answer.add(sb.toString());
371         }
372 
373         return answer;
374     }
375 }