• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mybatis / generator / 2136

02 Apr 2026 07:17PM UTC coverage: 91.775% (+1.4%) from 90.382%
2136

Pull #1485

github

web-flow
Merge 516685a9a into 18f1f002d
Pull Request #1485: Code Cleanup and Coverage

2425 of 3124 branches covered (77.62%)

191 of 235 new or added lines in 54 files covered. (81.28%)

7 existing lines in 4 files now uncovered.

11884 of 12949 relevant lines covered (91.78%)

0.92 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

84.5
/core/mybatis-generator-core/src/main/java/org/mybatis/generator/api/FullyQualifiedTable.java
1
/*
2
 *    Copyright 2006-2026 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.api;
17

18
import static org.mybatis.generator.internal.util.StringUtility.composeFullyQualifiedTableName;
19
import static org.mybatis.generator.internal.util.StringUtility.mapStringValueOrElseGet;
20
import static org.mybatis.generator.internal.util.StringUtility.stringHasValue;
21
import static org.mybatis.generator.internal.util.StringUtility.stringValueOrElseGet;
22

23
import java.util.Objects;
24
import java.util.Optional;
25
import java.util.regex.Matcher;
26
import java.util.regex.Pattern;
27

28
import org.jspecify.annotations.Nullable;
29
import org.mybatis.generator.config.Context;
30
import org.mybatis.generator.config.DomainObjectRenamingRule;
31
import org.mybatis.generator.internal.util.JavaBeansUtil;
32

33
public class FullyQualifiedTable {
34
    private final @Nullable String introspectedCatalog;
35
    private final @Nullable String introspectedSchema;
36
    private final String introspectedTableName;
37
    private final @Nullable String runtimeCatalog;
38
    private final @Nullable String runtimeSchema;
39
    private final @Nullable String runtimeTableName;
40
    private @Nullable String configuredDomainObjectName;
41
    private @Nullable String domainObjectSubPackage;
42
    private final @Nullable String alias;
43
    private final boolean ignoreQualifiersAtRuntime;
44
    private final String beginningDelimiter;
45
    private final String endingDelimiter;
46
    private final @Nullable DomainObjectRenamingRule domainObjectRenamingRule;
47

48
    public FullyQualifiedTable(Builder builder) {
1✔
49
        introspectedCatalog = builder.introspectedCatalog;
1✔
50
        introspectedSchema = builder.introspectedSchema;
1✔
51
        introspectedTableName = Objects.requireNonNull(builder.introspectedTableName);
1✔
52
        ignoreQualifiersAtRuntime = builder.ignoreQualifiersAtRuntime;
1✔
53
        runtimeCatalog = builder.runtimeCatalog;
1✔
54
        runtimeSchema = builder.runtimeSchema;
1✔
55
        runtimeTableName = builder.runtimeTableName;
1✔
56
        domainObjectRenamingRule = builder.domainObjectRenamingRule;
1✔
57

58
        if (stringHasValue(builder.domainObjectName)) {
1✔
59
            int index = builder.domainObjectName.lastIndexOf('.');
1✔
60
            if (index == -1) {
1✔
61
                configuredDomainObjectName = builder.domainObjectName;
1✔
62
            } else {
63
                configuredDomainObjectName = builder.domainObjectName.substring(index + 1);
1✔
64
                domainObjectSubPackage = builder.domainObjectName.substring(0, index);
1✔
65
            }
66
        }
67

68
        if (builder.alias == null) {
1✔
69
            alias = null;
1✔
70
        } else {
71
            this.alias = builder.alias.trim();
1✔
72
        }
73

74
        if (builder.delimitIdentifiers && builder.context != null) {
1!
75
            beginningDelimiter =  builder.context.getBeginningDelimiter();
1✔
76
            endingDelimiter = builder.context.getEndingDelimiter();
1✔
77
        } else {
78
            beginningDelimiter = ""; //$NON-NLS-1$
1✔
79
            endingDelimiter = ""; //$NON-NLS-1$
1✔
80

81
        }
82
    }
1✔
83

84
    public Optional<String> getIntrospectedCatalog() {
85
        return Optional.ofNullable(introspectedCatalog);
1✔
86
    }
87

88
    public Optional<String> getIntrospectedSchema() {
89
        return Optional.ofNullable(introspectedSchema);
1✔
90
    }
91

92
    public String getIntrospectedTableName() {
93
        return introspectedTableName;
1✔
94
    }
95

96
    public String getFullyQualifiedTableNameAtRuntime() {
97
        StringBuilder localCatalog = new StringBuilder();
1✔
98
        if (!ignoreQualifiersAtRuntime) {
1!
99
            if (stringHasValue(runtimeCatalog)) {
1!
100
                localCatalog.append(runtimeCatalog);
×
101
            } else if (stringHasValue(introspectedCatalog)) {
1!
102
                localCatalog.append(introspectedCatalog);
×
103
            }
104
        }
105
        if (!localCatalog.isEmpty()) {
1!
106
            addDelimiters(localCatalog);
×
107
        }
108

109
        StringBuilder localSchema = new StringBuilder();
1✔
110
        if (!ignoreQualifiersAtRuntime) {
1!
111
            if (stringHasValue(runtimeSchema)) {
1!
112
                localSchema.append(runtimeSchema);
×
113
            } else if (stringHasValue(introspectedSchema)) {
1✔
114
                localSchema.append(introspectedSchema);
1✔
115
            }
116
        }
117
        if (!localSchema.isEmpty()) {
1✔
118
            addDelimiters(localSchema);
1✔
119
        }
120

121
        StringBuilder localTableName = new StringBuilder();
1✔
122
        if (stringHasValue(runtimeTableName)) {
1!
123
            localTableName.append(runtimeTableName);
×
124
        } else {
125
            localTableName.append(introspectedTableName);
1✔
126
        }
127
        addDelimiters(localTableName);
1✔
128

129
        return composeFullyQualifiedTableName(localCatalog.toString(), localSchema.toString(),
1✔
130
                localTableName.toString(), '.');
1✔
131
    }
132

133
    public String getAliasedFullyQualifiedTableNameAtRuntime() {
134
        StringBuilder sb = new StringBuilder();
1✔
135

136
        sb.append(getFullyQualifiedTableNameAtRuntime());
1✔
137

138
        if (stringHasValue(alias)) {
1✔
139
            sb.append(' ');
1✔
140
            sb.append(alias);
1✔
141
        }
142

143
        return sb.toString();
1✔
144
    }
145

146
    public String getDomainObjectName() {
147
        return stringValueOrElseGet(configuredDomainObjectName, this::calculateDomainObjectName);
1✔
148
    }
149

150
    private String calculateDomainObjectName() {
151
        String finalDomainObjectName = mapStringValueOrElseGet(runtimeTableName,
1✔
NEW
152
                s -> JavaBeansUtil.getCamelCaseString(s, true),
×
153
                () -> JavaBeansUtil.getCamelCaseString(introspectedTableName, true));
1✔
154

155
        if (domainObjectRenamingRule != null) {
1✔
156
            Pattern pattern = domainObjectRenamingRule.pattern();
1✔
157
            String replaceString = domainObjectRenamingRule.replaceString();
1✔
158
            Matcher matcher = pattern.matcher(finalDomainObjectName);
1✔
159
            finalDomainObjectName = JavaBeansUtil.getFirstCharacterUppercase(matcher.replaceAll(replaceString));
1✔
160
        }
161
        return finalDomainObjectName;
1✔
162
    }
163

164
    @Override
165
    public boolean equals(Object obj) {
166
        if (this == obj) {
×
167
            return true;
×
168
        }
169

170
        if (!(obj instanceof FullyQualifiedTable other)) {
×
171
            return false;
×
172
        }
173

174
        return Objects.equals(this.introspectedTableName, other.introspectedTableName)
×
175
                && Objects.equals(this.introspectedCatalog, other.introspectedCatalog)
×
176
                && Objects.equals(this.introspectedSchema, other.introspectedSchema);
×
177
    }
178

179
    @Override
180
    public int hashCode() {
181
        return Objects.hash(introspectedTableName, introspectedCatalog, introspectedCatalog);
1✔
182
    }
183

184
    @Override
185
    public String toString() {
186
        return composeFullyQualifiedTableName(
1✔
187
                introspectedCatalog, introspectedSchema, introspectedTableName,
188
                '.');
189
    }
190

191
    public Optional<String> getAlias() {
192
        return Optional.ofNullable(alias);
×
193
    }
194

195
    /**
196
     * Calculates a Java package fragment based on the table catalog and schema.
197
     * If qualifiers are ignored, then this method will return an empty string.
198
     *
199
     * <p>This method is used for determining the sub package for Java client and
200
     * SQL map (XML) objects.  It ignores any sub-package added to the
201
     * domain object name in the table configuration.
202
     *
203
     * @param isSubPackagesEnabled
204
     *            the is sub packages enabled
205
     * @return the subpackage for this table
206
     */
207
    public String getSubPackageForClientOrSqlMap(boolean isSubPackagesEnabled) {
208
        StringBuilder sb = new StringBuilder();
1✔
209
        if (!ignoreQualifiersAtRuntime && isSubPackagesEnabled) {
1!
210
            if (stringHasValue(runtimeCatalog)) {
1!
211
                sb.append('.');
×
212
                sb.append(runtimeCatalog.toLowerCase());
×
213
            } else if (stringHasValue(introspectedCatalog)) {
1!
214
                sb.append('.');
×
215
                sb.append(introspectedCatalog.toLowerCase());
×
216
            }
217

218
            if (stringHasValue(runtimeSchema)) {
1!
219
                sb.append('.');
×
220
                sb.append(runtimeSchema.toLowerCase());
×
221
            } else if (stringHasValue(introspectedSchema)) {
1✔
222
                sb.append('.');
1✔
223
                sb.append(introspectedSchema.toLowerCase());
1✔
224
            }
225
        }
226

227
        // TODO - strip characters that are not valid in package names
228
        return sb.toString();
1✔
229
    }
230

231
    /**
232
     * Calculates a Java package fragment based on the table catalog and schema.
233
     * If qualifiers are ignored, then this method will return an empty string.
234
     *
235
     * <p>This method is used for determining the sub package for Java model objects only.
236
     * It takes into account the possibility that a sub-package was added to the
237
     * domain object name in the table configuration.
238
     *
239
     * @param isSubPackagesEnabled
240
     *            the is sub packages enabled
241
     * @return the subpackage for this table
242
     */
243
    public String getSubPackageForModel(boolean isSubPackagesEnabled) {
244
        StringBuilder sb = new StringBuilder();
1✔
245
        sb.append(getSubPackageForClientOrSqlMap(isSubPackagesEnabled));
1✔
246

247
        if (stringHasValue(domainObjectSubPackage)) {
1✔
248
            sb.append('.');
1✔
249
            sb.append(domainObjectSubPackage);
1✔
250
        }
251

252
        return sb.toString();
1✔
253
    }
254

255
    private void addDelimiters(StringBuilder sb) {
256
        if (stringHasValue(beginningDelimiter)) {
1✔
257
            sb.insert(0, beginningDelimiter);
1✔
258
        }
259

260
        if (stringHasValue(endingDelimiter)) {
1✔
261
            sb.append(endingDelimiter);
1✔
262
        }
263
    }
1✔
264

265
    public Optional<String> getDomainObjectSubPackage() {
266
        return Optional.ofNullable(domainObjectSubPackage);
1✔
267
    }
268

269
    public static class Builder {
1✔
270
        private @Nullable String introspectedCatalog;
271
        private @Nullable String introspectedSchema;
272
        private @Nullable String introspectedTableName;
273
        private @Nullable String runtimeCatalog;
274
        private @Nullable String runtimeSchema;
275
        private @Nullable String runtimeTableName;
276
        private @Nullable String domainObjectName;
277
        private @Nullable String alias;
278
        private boolean ignoreQualifiersAtRuntime;
279
        private boolean delimitIdentifiers;
280
        private @Nullable DomainObjectRenamingRule domainObjectRenamingRule;
281
        private @Nullable Context context;
282

283
        /**
284
         * Sets the actual catalog of the table as returned from DatabaseMetaData.
285
         *
286
         * <p>This value should only be set if the user configured a catalog. Otherwise, the
287
         * DatabaseMetaData is reporting some database default that we don't want in the generated code.
288
         *
289
         * @param introspectedCatalog the introspected catalog
290
         * @return this builder
291
         */
292
        public Builder withIntrospectedCatalog(@Nullable String introspectedCatalog) {
293
            this.introspectedCatalog = introspectedCatalog;
1✔
294
            return this;
1✔
295
        }
296

297
        /**
298
         * Sets the actual schema of the table as returned from DatabaseMetaData.
299
         *
300
         * <p>This value should only be set if the user configured a schema. Otherwise, the
301
         * DatabaseMetaData is reporting some database default that we don't want in the generated code.
302
         *
303
         * @param introspectedSchema the introspected schema
304
         * @return this builder
305
         */
306
        public Builder withIntrospectedSchema(@Nullable String introspectedSchema) {
307
            this.introspectedSchema = introspectedSchema;
1✔
308
            return this;
1✔
309
        }
310

311
        /**
312
         * Sets the actual table name as returned from DatabaseMetaData.
313
         *
314
         * @param introspectedTableName the introspected table name
315
         * @return this builder
316
         */
317
        public Builder withIntrospectedTableName(String introspectedTableName) {
318
            this.introspectedTableName = introspectedTableName;
1✔
319
            return this;
1✔
320
        }
321

322
        /**
323
         * Sets the runtime catalog.
324
         *
325
         * <p>This is used to "rename" the catalog in the generated SQL. This is useful, for example, when
326
         *    generating code against one catalog that should run with a different catalog.
327
         *
328
         * @param runtimeCatalog the runtime catalog
329
         * @return this builder
330
         */
331
        public Builder withRuntimeCatalog(@Nullable String runtimeCatalog) {
332
            this.runtimeCatalog = runtimeCatalog;
1✔
333
            return this;
1✔
334
        }
335

336
        /**
337
         * Sets the runtime schema.
338
         *
339
         * <p>This is used to "rename" the schema in the generated SQL. This is useful, for example, when
340
         *    generating code against one schema that should run with a different schema.
341
         *
342
         * @param runtimeSchema the runtime schema
343
         * @return this builder
344
         */
345
        public Builder withRuntimeSchema(@Nullable String runtimeSchema) {
346
            this.runtimeSchema = runtimeSchema;
1✔
347
            return this;
1✔
348
        }
349

350
        /**
351
         * Sets the runtime table name.
352
         *
353
         * <p>This is used to "rename" the table in the generated SQL. This is useful, for example, when generating
354
         *    code to run with an Oracle synonym. The user would have to specify the actual table name and schema
355
         *    for generation, but would want to use the synonym name in the generated SQL
356
         *
357
         * @param runtimeTableName the runtime table name
358
         * @return this builder
359
         */
360
        public Builder withRuntimeTableName(@Nullable String runtimeTableName) {
361
            this.runtimeTableName = runtimeTableName;
1✔
362
            return this;
1✔
363
        }
364

365
        /**
366
         * Set the configured domain object name for this table.
367
         *
368
         * <p>If nothing is configured, we'll build the domain object named based on the tableName or runtimeTableName.
369
         *
370
         * @param domainObjectName the domain object name
371
         * @return this builder
372
         */
373
        public Builder withDomainObjectName(@Nullable String domainObjectName) {
374
            this.domainObjectName = domainObjectName;
1✔
375
            return this;
1✔
376
        }
377

378
        /**
379
         * Sets a configured alias for the table. This alias will be added to the table name in the SQL.
380
         *
381
         * @param alias the alias
382
         * @return this builder
383
         */
384
        public Builder withAlias(@Nullable String alias) {
385
            this.alias = alias;
1✔
386
            return this;
1✔
387
        }
388

389
        /**
390
         * If true, then the catalog and schema qualifiers will be ignored when composing fully qualified names
391
         * in the generated SQL.
392
         *
393
         * <p>This is used, for example, when the user needs to specify a specific schema for
394
         * generating code but does not want the schema in the generated SQL
395
         *
396
         * @param ignoreQualifiersAtRuntime whether to ignore qualifiers at runtime
397
         * @return this builder
398
         */
399
        public Builder withIgnoreQualifiersAtRuntime(boolean ignoreQualifiersAtRuntime) {
400
            this.ignoreQualifiersAtRuntime = ignoreQualifiersAtRuntime;
1✔
401
            return this;
1✔
402
        }
403

404
        /**
405
         * If true, then the table identifiers will be delimited at runtime.
406
         *
407
         * <p>The delimiter characters are obtained from the Context.
408
         *
409
         * @param delimitIdentifiers whether to delimit identifiers at runtime
410
         * @return this builder
411
         */
412
        public Builder withDelimitIdentifiers(boolean delimitIdentifiers) {
413
            this.delimitIdentifiers = delimitIdentifiers;
1✔
414
            return this;
1✔
415
        }
416

417
        /**
418
         * Sets a domain object renaming rule.
419
         *
420
         * <p>This is ignored is a domain object name is configured.
421
         *
422
         * <p>If a domain object name is not configured, we'll build the domain object named based on the tableName
423
         * or runtimeTableName, and then we use the domain object renaming rule to generate the final domain object
424
         * name.
425
         *
426
         * @param domainObjectRenamingRule the domain object renaming rule
427
         * @return this builder
428
         */
429
        public Builder withDomainObjectRenamingRule(@Nullable DomainObjectRenamingRule domainObjectRenamingRule) {
430
            this.domainObjectRenamingRule = domainObjectRenamingRule;
1✔
431
            return this;
1✔
432
        }
433

434
        public Builder withContext(Context context) {
435
            this.context = context;
1✔
436
            return this;
1✔
437
        }
438

439
        public FullyQualifiedTable build() {
440
            return new FullyQualifiedTable(this);
1✔
441
        }
442
    }
443
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc