DynamicSqlSupportClassGenerator.java
/*
* Copyright 2006-2026 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mybatis.generator.runtime.dynamicsql.java;
import static org.mybatis.generator.internal.util.StringUtility.escapeStringForJava;
import static org.mybatis.generator.runtime.mybatis3.MyBatis3FormattingUtilities.getEscapedColumnName;
import java.util.List;
import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.dom.java.Field;
import org.mybatis.generator.api.dom.java.FullyQualifiedJavaType;
import org.mybatis.generator.api.dom.java.InnerClass;
import org.mybatis.generator.api.dom.java.JavaVisibility;
import org.mybatis.generator.api.dom.java.Method;
import org.mybatis.generator.api.dom.java.TopLevelClass;
import org.mybatis.generator.codegen.AbstractGenerator;
import org.mybatis.generator.config.PropertyRegistry;
import org.mybatis.generator.internal.util.JavaBeansUtil;
import org.mybatis.generator.internal.util.StringUtility;
import org.mybatis.generator.internal.util.messages.Messages;
public class DynamicSqlSupportClassGenerator extends AbstractGenerator {
private final boolean useSnakeCase;
private DynamicSqlSupportClassGenerator(Builder builder) {
super(builder);
useSnakeCase = introspectedTable
.findTableOrClientGeneratorPropertyAsBoolean(PropertyRegistry.ANY_USE_SNAKE_CASE_IDENTIFIERS);
}
public TopLevelClass generate() {
TopLevelClass topLevelClass = buildBasicClass();
Field tableField = calculateTableDefinition(topLevelClass);
topLevelClass.addImportedType(tableField.getType());
topLevelClass.addField(tableField);
InnerClass innerClass = buildInnerTableClass(topLevelClass);
topLevelClass.addInnerClass(innerClass);
List<IntrospectedColumn> columns = introspectedTable.getAllColumns();
for (IntrospectedColumn column : columns) {
handleColumn(topLevelClass, innerClass, column, tableField.getName());
}
return topLevelClass;
}
private TopLevelClass buildBasicClass() {
TopLevelClass topLevelClass = new TopLevelClass(introspectedTable.getMyBatisDynamicSqlSupportType());
topLevelClass.setVisibility(JavaVisibility.PUBLIC);
topLevelClass.setFinal(true);
topLevelClass.addImportedType(new FullyQualifiedJavaType("org.mybatis.dynamic.sql.SqlColumn")); //$NON-NLS-1$
topLevelClass.addImportedType(
new FullyQualifiedJavaType("org.mybatis.dynamic.sql.AliasableSqlTable")); //$NON-NLS-1$
topLevelClass.addImportedType(new FullyQualifiedJavaType("java.sql.JDBCType")); //$NON-NLS-1$
return topLevelClass;
}
private InnerClass buildInnerTableClass(TopLevelClass topLevelClass) {
FullyQualifiedJavaType fqjt =
new FullyQualifiedJavaType(introspectedTable.getMyBatisDynamicSQLTableObjectName());
InnerClass innerClass = new InnerClass(fqjt.getShortName());
innerClass.setVisibility(JavaVisibility.PUBLIC);
innerClass.setStatic(true);
innerClass.setFinal(true);
innerClass.setSuperClass(new FullyQualifiedJavaType("org.mybatis.dynamic.sql.AliasableSqlTable<" //$NON-NLS-1$
+ fqjt.getShortName() + ">")); //$NON-NLS-1$
Method method = new Method(fqjt.getShortName());
method.setVisibility(JavaVisibility.PUBLIC);
method.setConstructor(true);
method.addBodyLine("super(\"" //$NON-NLS-1$
+ escapeStringForJava(introspectedTable.getFullyQualifiedTableNameAtRuntime())
+ "\", " + fqjt.getShortName() + "::new" //$NON-NLS-1$ //$NON-NLS-2$
+ ");"); //$NON-NLS-1$
innerClass.addMethod(method);
commentGenerator.addClassAnnotation(innerClass, introspectedTable, topLevelClass.getImportedTypes());
return innerClass;
}
private Field calculateTableDefinition(TopLevelClass topLevelClass) {
FullyQualifiedJavaType fqjt =
new FullyQualifiedJavaType(introspectedTable.getMyBatisDynamicSQLTableObjectName());
String fieldName =
JavaBeansUtil.getValidPropertyName(introspectedTable.getMyBatisDynamicSQLTableObjectName());
if (useSnakeCase) {
fieldName = StringUtility.convertCamelCaseToSnakeCase(fieldName);
}
Field field = new Field(fieldName, fqjt);
commentGenerator.addFieldAnnotation(field, introspectedTable, topLevelClass.getImportedTypes());
field.setVisibility(JavaVisibility.PUBLIC);
field.setStatic(true);
field.setFinal(true);
String initializationString = String.format("new %s()", //$NON-NLS-1$
escapeStringForJava(introspectedTable.getMyBatisDynamicSQLTableObjectName()));
field.setInitializationString(initializationString);
return field;
}
private void handleColumn(TopLevelClass topLevelClass, InnerClass innerClass,
IntrospectedColumn column, String tableFieldName) {
topLevelClass.addImportedType(column.getFullyQualifiedJavaType());
FullyQualifiedJavaType javaType;
if (column.getFullyQualifiedJavaType().isPrimitive()) {
javaType = column.getFullyQualifiedJavaType().getPrimitiveTypeWrapper();
} else {
javaType = column.getFullyQualifiedJavaType();
}
FullyQualifiedJavaType fieldType = calculateFieldType(javaType);
String fieldName = column.getJavaProperty();
if (useSnakeCase) {
fieldName = StringUtility.convertCamelCaseToSnakeCase(fieldName);
}
if (fieldName.equals(tableFieldName)) {
// name collision, skip the shortcut field
warnings.add(
Messages.getString("Warning.29", //$NON-NLS-1$
fieldName, topLevelClass.getType().getFullyQualifiedName()));
} else {
// shortcut field
Field field = new Field(fieldName, fieldType);
field.setVisibility(JavaVisibility.PUBLIC);
field.setStatic(true);
field.setFinal(true);
field.setInitializationString(tableFieldName + "." + fieldName); //$NON-NLS-1$
commentGenerator.addFieldAnnotation(field, introspectedTable, column, topLevelClass.getImportedTypes());
topLevelClass.addField(field);
}
// inner class field
Field field = new Field(fieldName, fieldType);
field.setVisibility(JavaVisibility.PUBLIC);
field.setFinal(true);
field.setInitializationString(calculateInnerInitializationString(column, javaType));
innerClass.addField(field);
}
private FullyQualifiedJavaType calculateFieldType(FullyQualifiedJavaType javaType) {
return new FullyQualifiedJavaType(String.format("SqlColumn<%s>", javaType.getShortName())); //$NON-NLS-1$
}
private String calculateInnerInitializationString(IntrospectedColumn column, FullyQualifiedJavaType javaType) {
StringBuilder initializationString = new StringBuilder();
initializationString.append(String.format("column(\"%s\", JDBCType.%s", //$NON-NLS-1$ //$NON-NLS-2$
escapeStringForJava(getEscapedColumnName(column)),
column.getJdbcTypeName()));
column.getTypeHandler().ifPresentOrElse(
th -> initializationString.append(String.format(", \"%s\")", th)), //$NON-NLS-1$
() -> initializationString.append(')'));
if (StringUtility.isTrue(
column.getProperties().getProperty(PropertyRegistry.COLUMN_OVERRIDE_FORCE_JAVA_TYPE))) {
initializationString.append(".withJavaType("); //$NON-NLS-1$
initializationString.append(javaType.getShortName());
initializationString.append(".class)"); //$NON-NLS-1$
}
initializationString.append(".withJavaProperty(\""); //$NON-NLS-1$
initializationString.append(column.getJavaProperty());
initializationString.append("\")"); //$NON-NLS-1$
return initializationString.toString();
}
public static class Builder extends AbstractGeneratorBuilder<Builder> {
@Override
protected Builder getThis() {
return this;
}
public DynamicSqlSupportClassGenerator build() {
return new DynamicSqlSupportClassGenerator(this);
}
}
}