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.runtime.kotlin.elements;
17  
18  import static org.mybatis.generator.api.dom.OutputUtilities.kotlinIndent;
19  import static org.mybatis.generator.internal.util.StringUtility.escapeStringForKotlin;
20  import static org.mybatis.generator.internal.util.StringUtility.stringHasValue;
21  
22  import java.util.HashSet;
23  import java.util.Iterator;
24  import java.util.List;
25  import java.util.Set;
26  
27  import org.mybatis.generator.api.IntrospectedColumn;
28  import org.mybatis.generator.api.IntrospectedTable;
29  import org.mybatis.generator.api.dom.kotlin.FullyQualifiedKotlinType;
30  import org.mybatis.generator.api.dom.kotlin.JavaToKotlinTypeConverter;
31  import org.mybatis.generator.api.dom.kotlin.KotlinArg;
32  import org.mybatis.generator.codegen.mybatis3.ListUtilities;
33  import org.mybatis.generator.config.GeneratedKey;
34  import org.mybatis.generator.runtime.kotlin.KotlinDynamicSqlSupportClassGenerator;
35  
36  public class KotlinFragmentGenerator {
37  
38      private final IntrospectedTable introspectedTable;
39      private final String resultMapId;
40      private final String supportObjectImport;
41      private final String tableFieldName;
42  
43      private KotlinFragmentGenerator(Builder builder) {
44          introspectedTable = builder.introspectedTable;
45          resultMapId = builder.resultMapId;
46          supportObjectImport = builder.supportObjectImport;
47          tableFieldName = builder.tableFieldName;
48      }
49  
50      public KotlinFunctionParts getPrimaryKeyWhereClauseAndParameters(boolean forUpdate) {
51          KotlinFunctionParts.Builder builder = new KotlinFunctionParts.Builder();
52  
53          int columnCount = introspectedTable.getPrimaryKeyColumns().size();
54          boolean first = true;
55          for (IntrospectedColumn column : introspectedTable.getPrimaryKeyColumns()) {
56              String argName;
57              if (forUpdate) {
58                  argName = "row." + column.getJavaProperty() + "!!"; //$NON-NLS-1$ //$NON-NLS-2$
59              } else {
60                  argName = column.getJavaProperty() + "_"; //$NON-NLS-1$
61                  FullyQualifiedKotlinType kt = JavaToKotlinTypeConverter.convert(column.getFullyQualifiedJavaType());
62                  builder.withImports(kt.getImportList());
63                  builder.withArgument(KotlinArg.newArg(argName)
64                          .withDataType(kt.getShortNameWithTypeArguments())
65                          .build());
66              }
67  
68              AbstractKotlinFunctionGenerator.FieldNameAndImport fieldNameAndImport =
69                      AbstractKotlinFunctionGenerator.calculateFieldNameAndImport(tableFieldName,
70                      supportObjectImport, column);
71  
72              builder.withImport(fieldNameAndImport.importString());
73              if(columnCount == 1) {
74                  builder.withCodeLine(singleColumnWhere(fieldNameAndImport.fieldName(), argName));
75                  first = false;
76              } else if (first) {
77                  builder.withCodeLine("    where {"); //$NON-NLS-1$
78                  builder.withCodeLine(multiColumnWhere(fieldNameAndImport.fieldName(), argName));
79                  first = false;
80              } else {
81                  builder.withCodeLine(multiColumnAnd(fieldNameAndImport.fieldName(), argName));
82              }
83          }
84          if (columnCount > 1) {
85              builder.withCodeLine("    }"); //$NON-NLS-1$
86          }
87  
88          return builder.build();
89      }
90  
91      private String singleColumnWhere(String columName, String property) {
92          return "    where { " + composeIsEqualTo(columName, property)  + " }"; //$NON-NLS-1$ //$NON-NLS-2$
93      }
94  
95      private String multiColumnWhere(String columName, String property) {
96          return "        " + composeIsEqualTo(columName, property); //$NON-NLS-1$
97      }
98  
99      private String multiColumnAnd(String columName, String property) {
100         return "        and { " + composeIsEqualTo(columName, property)  + " }"; //$NON-NLS-1$ //$NON-NLS-2$
101     }
102 
103     private String composeIsEqualTo(String columName, String property) {
104         return columName + " isEqualTo " + property; //$NON-NLS-1$
105     }
106 
107     public KotlinFunctionParts getAnnotatedResults() {
108         KotlinFunctionParts.Builder builder = new KotlinFunctionParts.Builder();
109 
110         builder.withImport("org.apache.ibatis.type.JdbcType"); //$NON-NLS-1$
111         builder.withImport("org.apache.ibatis.annotations.Result"); //$NON-NLS-1$
112         builder.withImport("org.apache.ibatis.annotations.Results"); //$NON-NLS-1$
113 
114         builder.withAnnotation("@Results(id=\"" + resultMapId + "\", value = ["); //$NON-NLS-1$ //$NON-NLS-2$
115 
116         StringBuilder sb = new StringBuilder();
117 
118         Set<String> imports = new HashSet<>();
119         Iterator<IntrospectedColumn> iterPk = introspectedTable.getPrimaryKeyColumns().iterator();
120         Iterator<IntrospectedColumn> iterNonPk = introspectedTable.getNonPrimaryKeyColumns().iterator();
121         while (iterPk.hasNext()) {
122             IntrospectedColumn introspectedColumn = iterPk.next();
123             sb.setLength(0);
124             kotlinIndent(sb, 1);
125             sb.append(getResultAnnotation(imports, introspectedColumn, true));
126 
127             if (iterPk.hasNext() || iterNonPk.hasNext()) {
128                 sb.append(',');
129             }
130 
131             builder.withAnnotation(sb.toString());
132         }
133 
134         while (iterNonPk.hasNext()) {
135             IntrospectedColumn introspectedColumn = iterNonPk.next();
136             sb.setLength(0);
137             kotlinIndent(sb, 1);
138             sb.append(getResultAnnotation(imports, introspectedColumn, false));
139 
140             if (iterNonPk.hasNext()) {
141                 sb.append(',');
142             }
143 
144             builder.withAnnotation(sb.toString());
145         }
146 
147         builder.withAnnotation("])") //$NON-NLS-1$
148                 .withImports(imports);
149 
150         return builder.build();
151     }
152 
153     private String getResultAnnotation(Set<String> imports, IntrospectedColumn introspectedColumn,
154             boolean idColumn) {
155         StringBuilder sb = new StringBuilder();
156         sb.append("Result(column=\""); //$NON-NLS-1$
157         sb.append(escapeStringForKotlin(introspectedColumn.getActualColumnName()));
158         sb.append("\", property=\""); //$NON-NLS-1$
159         sb.append(introspectedColumn.getJavaProperty());
160         sb.append('\"');
161 
162         if (stringHasValue(introspectedColumn.getTypeHandler())) {
163             FullyQualifiedKotlinType fqjt =
164                     new FullyQualifiedKotlinType(introspectedColumn.getTypeHandler());
165             imports.add(introspectedColumn.getTypeHandler());
166             sb.append(", typeHandler="); //$NON-NLS-1$
167             sb.append(fqjt.getShortNameWithoutTypeArguments());
168             sb.append("::class"); //$NON-NLS-1$
169         }
170 
171         sb.append(", jdbcType=JdbcType."); //$NON-NLS-1$
172         sb.append(introspectedColumn.getJdbcTypeName());
173         if (idColumn) {
174             sb.append(", id=true"); //$NON-NLS-1$
175         }
176         sb.append(')');
177 
178         return sb.toString();
179     }
180 
181     public KotlinFunctionParts getGeneratedKeyAnnotation(GeneratedKey gk) {
182         KotlinFunctionParts.Builder builder = new KotlinFunctionParts.Builder();
183 
184         StringBuilder sb = new StringBuilder();
185         introspectedTable.getColumn(gk.getColumn()).ifPresent(introspectedColumn -> {
186             if (gk.isJdbcStandard()) {
187                 builder.withImport("org.apache.ibatis.annotations.Options"); //$NON-NLS-1$
188                 sb.append("@Options(useGeneratedKeys=true,keyProperty=\"row."); //$NON-NLS-1$
189                 sb.append(introspectedColumn.getJavaProperty());
190                 sb.append("\")"); //$NON-NLS-1$
191                 builder.withAnnotation(sb.toString());
192             } else {
193                 builder.withImport("org.apache.ibatis.annotations.SelectKey"); //$NON-NLS-1$
194                 FullyQualifiedKotlinType kt =
195                         JavaToKotlinTypeConverter.convert(introspectedColumn.getFullyQualifiedJavaType());
196 
197                 sb.append("@SelectKey(statement=[\""); //$NON-NLS-1$
198                 sb.append(gk.getRuntimeSqlStatement());
199                 sb.append("\"], keyProperty=\"row."); //$NON-NLS-1$
200                 sb.append(introspectedColumn.getJavaProperty());
201                 sb.append("\", before="); //$NON-NLS-1$
202                 sb.append(gk.isIdentity() ? "false" : "true"); //$NON-NLS-1$ //$NON-NLS-2$
203                 sb.append(", resultType="); //$NON-NLS-1$
204                 sb.append(kt.getShortNameWithoutTypeArguments());
205                 sb.append("::class)"); //$NON-NLS-1$
206                 builder.withAnnotation(sb.toString());
207             }
208         });
209 
210         return builder.build();
211     }
212 
213     public KotlinFunctionParts getSetEqualLines(List<IntrospectedColumn> columnList) {
214 
215         KotlinFunctionParts.Builder builder = new KotlinFunctionParts.Builder();
216 
217         List<IntrospectedColumn> columns = ListUtilities.removeIdentityAndGeneratedAlwaysColumns(columnList);
218         for (IntrospectedColumn column : columns) {
219             AbstractKotlinFunctionGenerator.FieldNameAndImport fieldNameAndImport =
220                     AbstractKotlinFunctionGenerator.calculateFieldNameAndImport(tableFieldName,
221                             supportObjectImport, column);
222             builder.withImport(fieldNameAndImport.importString());
223 
224             builder.withCodeLine("    set(" + fieldNameAndImport.fieldName() //$NON-NLS-1$
225                     + ") equalToOrNull row::" + column.getJavaProperty()); //$NON-NLS-1$
226         }
227 
228         return builder.build();
229     }
230 
231     public KotlinFunctionParts getSetEqualWhenPresentLines(List<IntrospectedColumn> columnList) {
232 
233         KotlinFunctionParts.Builder builder = new KotlinFunctionParts.Builder();
234 
235         List<IntrospectedColumn> columns = ListUtilities.removeIdentityAndGeneratedAlwaysColumns(columnList);
236         for (IntrospectedColumn column : columns) {
237             AbstractKotlinFunctionGenerator.FieldNameAndImport fieldNameAndImport =
238                     AbstractKotlinFunctionGenerator.calculateFieldNameAndImport(tableFieldName,
239                             supportObjectImport, column);
240             builder.withImport(fieldNameAndImport.importString());
241 
242             builder.withCodeLine("    set(" + fieldNameAndImport.fieldName() //$NON-NLS-1$
243                     + ") equalToWhenPresent row::" + column.getJavaProperty()); //$NON-NLS-1$
244         }
245 
246         return builder.build();
247     }
248 
249     public static class Builder {
250         private IntrospectedTable introspectedTable;
251         private String resultMapId;
252         private String supportObjectImport;
253         private String tableFieldName;
254 
255         public Builder withIntrospectedTable(IntrospectedTable introspectedTable) {
256             this.introspectedTable = introspectedTable;
257             return this;
258         }
259 
260         public Builder withResultMapId(String resultMapId) {
261             this.resultMapId = resultMapId;
262             return this;
263         }
264 
265         public Builder withDynamicSqlSupportClassGenerator(KotlinDynamicSqlSupportClassGenerator generator) {
266             supportObjectImport = generator.getSupportObjectImport();
267             return this;
268         }
269 
270         public Builder withTableFieldName(String tableFieldName) {
271             this.tableFieldName = tableFieldName;
272             return this;
273         }
274 
275         public KotlinFragmentGenerator build() {
276             return new KotlinFragmentGenerator(this);
277         }
278     }
279 }