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.xmlmapper.elements;
17  
18  import static org.mybatis.generator.internal.util.StringUtility.stringHasValue;
19  
20  import java.util.ArrayList;
21  import java.util.Iterator;
22  import java.util.List;
23  
24  import org.mybatis.generator.api.IntrospectedColumn;
25  import org.mybatis.generator.api.dom.OutputUtilities;
26  import org.mybatis.generator.api.dom.java.FullyQualifiedJavaType;
27  import org.mybatis.generator.api.dom.xml.Attribute;
28  import org.mybatis.generator.api.dom.xml.TextElement;
29  import org.mybatis.generator.api.dom.xml.XmlElement;
30  import org.mybatis.generator.codegen.AbstractGenerator;
31  import org.mybatis.generator.codegen.mybatis3.ListUtilities;
32  import org.mybatis.generator.codegen.mybatis3.MyBatis3FormattingUtilities;
33  import org.mybatis.generator.config.GeneratedKey;
34  
35  public abstract class AbstractXmlElementGenerator extends AbstractGenerator {
36      public abstract void addElements(XmlElement parentElement);
37  
38      protected AbstractXmlElementGenerator() {
39          super();
40      }
41  
42      /**
43       * This method should return an XmlElement for the select key used to
44       * automatically generate keys.
45       *
46       * @param introspectedColumn
47       *            the column related to the select key statement
48       * @param generatedKey
49       *            the generated key for the current table
50       *
51       * @return the selectKey element
52       */
53      protected XmlElement getSelectKey(IntrospectedColumn introspectedColumn,
54              GeneratedKey generatedKey) {
55          String identityColumnType = introspectedColumn.getFullyQualifiedJavaType().getFullyQualifiedName();
56  
57          XmlElement answer = new XmlElement("selectKey"); //$NON-NLS-1$
58          answer.addAttribute(new Attribute("resultType", identityColumnType)); //$NON-NLS-1$
59          answer.addAttribute(new Attribute(
60                  "keyProperty", introspectedColumn.getJavaProperty())); //$NON-NLS-1$
61          answer.addAttribute(new Attribute("order", generatedKey.getMyBatis3Order())); //$NON-NLS-1$
62  
63          answer.addElement(new TextElement(generatedKey.getRuntimeSqlStatement()));
64  
65          return answer;
66      }
67  
68      protected XmlElement getBaseColumnListElement() {
69          XmlElement answer = new XmlElement("include"); //$NON-NLS-1$
70          answer.addAttribute(new Attribute("refid", introspectedTable.getBaseColumnListId())); //$NON-NLS-1$
71          return answer;
72      }
73  
74      protected XmlElement getBlobColumnListElement() {
75          XmlElement answer = new XmlElement("include"); //$NON-NLS-1$
76          answer.addAttribute(new Attribute("refid", introspectedTable.getBlobColumnListId())); //$NON-NLS-1$
77          return answer;
78      }
79  
80      protected XmlElement getExampleIncludeElement() {
81          XmlElement ifElement = new XmlElement("if"); //$NON-NLS-1$
82          ifElement.addAttribute(new Attribute("test", "_parameter != null")); //$NON-NLS-1$ //$NON-NLS-2$
83  
84          XmlElement includeElement = new XmlElement("include"); //$NON-NLS-1$
85          includeElement.addAttribute(new Attribute("refid", introspectedTable.getExampleWhereClauseId())); //$NON-NLS-1$
86          ifElement.addElement(includeElement);
87  
88          return ifElement;
89      }
90  
91      protected XmlElement getUpdateByExampleIncludeElement() {
92          XmlElement ifElement = new XmlElement("if"); //$NON-NLS-1$
93          ifElement.addAttribute(new Attribute("test", "example != null")); //$NON-NLS-1$ //$NON-NLS-2$
94  
95          XmlElement includeElement = new XmlElement("include"); //$NON-NLS-1$
96          includeElement.addAttribute(new Attribute("refid", //$NON-NLS-1$
97                  introspectedTable.getMyBatis3UpdateByExampleWhereClauseId()));
98          ifElement.addElement(includeElement);
99  
100         return ifElement;
101     }
102 
103     protected List<TextElement> buildSelectList(List<IntrospectedColumn> columns) {
104         return buildSelectList("", columns); //$NON-NLS-1$
105     }
106 
107     protected List<TextElement> buildSelectList(String initial, List<IntrospectedColumn> columns) {
108         List<TextElement> answer = new ArrayList<>();
109         StringBuilder sb = new StringBuilder(initial);
110         Iterator<IntrospectedColumn> iter = columns.iterator();
111         while (iter.hasNext()) {
112             sb.append(MyBatis3FormattingUtilities.getSelectListPhrase(iter.next()));
113 
114             if (iter.hasNext()) {
115                 sb.append(", "); //$NON-NLS-1$
116             }
117 
118             if (sb.length() > 80) {
119                 answer.add(new TextElement(sb.toString()));
120                 sb.setLength(0);
121             }
122         }
123 
124         if (sb.length() > 0) {
125             answer.add(new TextElement(sb.toString()));
126         }
127 
128         return answer;
129     }
130 
131     protected List<TextElement> buildPrimaryKeyWhereClause() {
132         List<TextElement> answer = new ArrayList<>();
133         boolean first = true;
134         for (IntrospectedColumn introspectedColumn : introspectedTable.getPrimaryKeyColumns()) {
135             String line;
136             if (first) {
137                 line = "where "; //$NON-NLS-1$
138                 first = false;
139             } else {
140                 line = "  and "; //$NON-NLS-1$
141             }
142 
143             line += MyBatis3FormattingUtilities.getEscapedColumnName(introspectedColumn);
144             line += " = "; //$NON-NLS-1$
145             line += MyBatis3FormattingUtilities.getParameterClause(introspectedColumn);
146             answer.add(new TextElement(line));
147         }
148 
149         return answer;
150     }
151 
152     protected XmlElement buildInitialInsert(String statementId, FullyQualifiedJavaType parameterType) {
153         XmlElement answer = new XmlElement("insert"); //$NON-NLS-1$
154 
155         answer.addAttribute(new Attribute("id", statementId)); //$NON-NLS-1$
156 
157         answer.addAttribute(new Attribute("parameterType", parameterType.getFullyQualifiedName())); //$NON-NLS-1$
158 
159         context.getCommentGenerator().addComment(answer);
160 
161         introspectedTable.getGeneratedKey().ifPresent(gk ->
162                 introspectedTable.getColumn(gk.getColumn()).ifPresent(introspectedColumn -> {
163                     // if the column is null, then it's a configuration error. The
164                     // warning has already been reported
165                     if (gk.isJdbcStandard()) {
166                         answer.addAttribute(new Attribute("useGeneratedKeys", "true")); //$NON-NLS-1$ //$NON-NLS-2$
167                         answer.addAttribute(
168                                 new Attribute("keyProperty", introspectedColumn.getJavaProperty())); //$NON-NLS-1$
169                         answer.addAttribute(
170                                 new Attribute("keyColumn", introspectedColumn.getActualColumnName())); //$NON-NLS-1$
171                     } else {
172                         answer.addElement(getSelectKey(introspectedColumn, gk));
173                     }
174                 })
175         );
176 
177         return answer;
178     }
179 
180     protected enum ResultElementType {
181         ID("id"), //$NON-NLS-1$
182         RESULT("result"); //$NON-NLS-1$
183 
184         private final String value;
185 
186         ResultElementType(String value) {
187             this.value = value;
188         }
189     }
190 
191     protected List<XmlElement> buildResultMapItems(ResultElementType elementType, List<IntrospectedColumn> columns) {
192         List<XmlElement> answer = new ArrayList<>();
193         for (IntrospectedColumn introspectedColumn : columns) {
194             XmlElement resultElement = new XmlElement(elementType.value);
195 
196             resultElement.addAttribute(buildColumnAttribute(introspectedColumn));
197             resultElement.addAttribute(new Attribute("property", introspectedColumn.getJavaProperty())); //$NON-NLS-1$
198             resultElement.addAttribute(new Attribute("jdbcType", introspectedColumn.getJdbcTypeName())); //$NON-NLS-1$
199 
200             if (stringHasValue(introspectedColumn.getTypeHandler())) {
201                 resultElement.addAttribute(
202                         new Attribute("typeHandler", introspectedColumn.getTypeHandler())); //$NON-NLS-1$
203             }
204 
205             answer.add(resultElement);
206         }
207 
208         return answer;
209     }
210 
211     protected XmlElement buildConstructorElement(boolean includeBlobColumns) {
212         XmlElement constructor = new XmlElement("constructor"); //$NON-NLS-1$
213 
214         for (IntrospectedColumn introspectedColumn : introspectedTable.getPrimaryKeyColumns()) {
215             XmlElement resultElement = new XmlElement("idArg"); //$NON-NLS-1$
216 
217             resultElement.addAttribute(buildColumnAttribute(introspectedColumn));
218             resultElement.addAttribute(new Attribute("jdbcType", //$NON-NLS-1$
219                     introspectedColumn.getJdbcTypeName()));
220             resultElement.addAttribute(new Attribute("javaType", //$NON-NLS-1$
221                     introspectedColumn.getFullyQualifiedJavaType().getFullyQualifiedName()));
222 
223             if (stringHasValue(introspectedColumn.getTypeHandler())) {
224                 resultElement.addAttribute(
225                         new Attribute("typeHandler", introspectedColumn.getTypeHandler())); //$NON-NLS-1$
226             }
227 
228             constructor.addElement(resultElement);
229         }
230 
231         List<IntrospectedColumn> columns;
232         if (includeBlobColumns) {
233             columns = introspectedTable.getNonPrimaryKeyColumns();
234         } else {
235             columns = introspectedTable.getBaseColumns();
236         }
237         for (IntrospectedColumn introspectedColumn : columns) {
238             XmlElement resultElement = new XmlElement("arg"); //$NON-NLS-1$
239 
240             resultElement.addAttribute(buildColumnAttribute(introspectedColumn));
241             resultElement.addAttribute(new Attribute("jdbcType", //$NON-NLS-1$
242                     introspectedColumn.getJdbcTypeName()));
243 
244             if (introspectedColumn.getFullyQualifiedJavaType().isPrimitive()) {
245                 // need to use the MyBatis type alias for a primitive byte
246                 String s = '_'
247                         + introspectedColumn.getFullyQualifiedJavaType().getShortName();
248                 resultElement.addAttribute(new Attribute("javaType", s)); //$NON-NLS-1$
249             } else if ("byte[]".equals(introspectedColumn.getFullyQualifiedJavaType() //$NON-NLS-1$
250                     .getFullyQualifiedName())) {
251                 // need to use the MyBatis type alias for a primitive byte arry
252                 resultElement.addAttribute(new Attribute("javaType", //$NON-NLS-1$
253                         "_byte[]")); //$NON-NLS-1$
254             } else {
255                 resultElement.addAttribute(new Attribute("javaType", //$NON-NLS-1$
256                         introspectedColumn.getFullyQualifiedJavaType().getFullyQualifiedName()));
257             }
258 
259             if (stringHasValue(introspectedColumn.getTypeHandler())) {
260                 resultElement.addAttribute(new Attribute(
261                         "typeHandler", introspectedColumn.getTypeHandler())); //$NON-NLS-1$
262             }
263 
264             constructor.addElement(resultElement);
265         }
266 
267         return constructor;
268     }
269 
270     protected Attribute buildColumnAttribute(IntrospectedColumn introspectedColumn) {
271         return new Attribute("column", //$NON-NLS-1$
272                 MyBatis3FormattingUtilities.getRenamedColumnNameForResultMap(introspectedColumn));
273     }
274 
275     protected XmlElement buildUpdateByExampleElement(String statementId, List<IntrospectedColumn> columns) {
276         XmlElement answer = new XmlElement("update"); //$NON-NLS-1$
277 
278         answer.addAttribute(new Attribute("id", statementId)); //$NON-NLS-1$
279 
280         answer.addAttribute(new Attribute("parameterType", "map")); //$NON-NLS-1$ //$NON-NLS-2$
281         context.getCommentGenerator().addComment(answer);
282 
283         StringBuilder sb = new StringBuilder();
284         sb.append("update "); //$NON-NLS-1$
285         sb.append(introspectedTable.getAliasedFullyQualifiedTableNameAtRuntime());
286         answer.addElement(new TextElement(sb.toString()));
287 
288         // set up for first column
289         sb.setLength(0);
290         sb.append("set "); //$NON-NLS-1$
291 
292         Iterator<IntrospectedColumn> iter = ListUtilities.removeGeneratedAlwaysColumns(columns).iterator();
293         while (iter.hasNext()) {
294             IntrospectedColumn introspectedColumn = iter.next();
295 
296             sb.append(MyBatis3FormattingUtilities.getAliasedEscapedColumnName(introspectedColumn));
297             sb.append(" = "); //$NON-NLS-1$
298             sb.append(MyBatis3FormattingUtilities.getParameterClause(introspectedColumn, "row.")); //$NON-NLS-1$
299 
300             if (iter.hasNext()) {
301                 sb.append(',');
302             }
303 
304             answer.addElement(new TextElement(sb.toString()));
305 
306             // set up for the next column
307             if (iter.hasNext()) {
308                 sb.setLength(0);
309                 OutputUtilities.xmlIndent(sb, 1);
310             }
311         }
312 
313         answer.addElement(getUpdateByExampleIncludeElement());
314         return answer;
315     }
316 
317     protected XmlElement buildUpdateByPrimaryKeyElement(String statementId, String parameterType,
318                                                         List<IntrospectedColumn> columns) {
319         XmlElement answer = new XmlElement("update"); //$NON-NLS-1$
320 
321         answer.addAttribute(new Attribute("id", statementId)); //$NON-NLS-1$
322         answer.addAttribute(new Attribute("parameterType", parameterType)); //$NON-NLS-1$
323 
324         context.getCommentGenerator().addComment(answer);
325 
326         StringBuilder sb = new StringBuilder();
327         sb.append("update "); //$NON-NLS-1$
328         sb.append(introspectedTable.getFullyQualifiedTableNameAtRuntime());
329         answer.addElement(new TextElement(sb.toString()));
330 
331         // set up for first column
332         sb.setLength(0);
333         sb.append("set "); //$NON-NLS-1$
334 
335         Iterator<IntrospectedColumn> iter = ListUtilities.removeGeneratedAlwaysColumns(columns).iterator();
336         while (iter.hasNext()) {
337             IntrospectedColumn introspectedColumn = iter.next();
338 
339             sb.append(MyBatis3FormattingUtilities.getEscapedColumnName(introspectedColumn));
340             sb.append(" = "); //$NON-NLS-1$
341             sb.append(MyBatis3FormattingUtilities.getParameterClause(introspectedColumn));
342 
343             if (iter.hasNext()) {
344                 sb.append(',');
345             }
346 
347             answer.addElement(new TextElement(sb.toString()));
348 
349             // set up for the next column
350             if (iter.hasNext()) {
351                 sb.setLength(0);
352                 OutputUtilities.xmlIndent(sb, 1);
353             }
354         }
355 
356         buildPrimaryKeyWhereClause().forEach(answer::addElement);
357 
358         return answer;
359     }
360 }