Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
*/
package org.mybatis.generator.codegen;

import static org.mybatis.generator.internal.util.StringUtility.stringHasValue;

/**
* This class exists to that Java client generators can specify whether
* an XML generator is required to match the methods in the
Expand Down Expand Up @@ -49,4 +51,92 @@ public boolean requiresXMLGenerator() {
* XML is required by this generator
*/
public abstract AbstractXmlGenerator getMatchedXMLGenerator();

/**
* Processes the rootInterface string to replace generic type placeholders with the actual record type.
* <p>
* If the rootInterface contains a generic placeholder like {@code <T>}, {@code <E>}, etc., it will be
* replaced with the actual record type from the introspected table. For example:
* <ul>
* <li>{@code "BaseMapper<T>"} becomes {@code "BaseMapper<com.example.User>"}</li>
* <li>{@code "BaseMapper"} remains {@code "BaseMapper"} (no change for backward compatibility)</li>
* <li>{@code "BaseMapper<com.example.User>"} remains unchanged (already fully qualified)</li>
* </ul>
*
* @param rootInterface
* the rootInterface string from configuration, may contain generic placeholders
* @param recordType
* the actual record type (fully qualified) to use as replacement
* @return the processed rootInterface string with placeholders replaced, or the original string if no
* replacement is needed
*/
protected String processRootInterfaceWithGenerics(String rootInterface, String recordType) {
if (!stringHasValue(rootInterface) || !stringHasValue(recordType)) {
return rootInterface;
}

// Check if rootInterface contains generic brackets
int openBracketIndex = rootInterface.indexOf('<');
if (openBracketIndex == -1) {
// No generic brackets, return as-is for backward compatibility
return rootInterface;
}

int closeBracketIndex = rootInterface.lastIndexOf('>');
if (closeBracketIndex == -1 || closeBracketIndex <= openBracketIndex) {
// Malformed generic brackets, return as-is
return rootInterface;
}

String baseInterface = rootInterface.substring(0, openBracketIndex);
String genericContent = rootInterface.substring(openBracketIndex + 1, closeBracketIndex).trim();

// Check if the generic content is a placeholder (single letter or single letter with extends clause)
// Common Java generic type parameter names: T, E, K, V, N, U, R, S
if (isGenericPlaceholder(genericContent)) {
// Replace placeholder with actual record type
return baseInterface + "<" + recordType + ">";
}

// Already has a concrete type (fully qualified or not), return as-is
return rootInterface;
}

/**
* Checks if a generic content string represents a placeholder that should be replaced.
* Placeholders are typically single-letter type parameters like T, E, K, V, etc.,
* optionally with an "extends" clause like "T extends Entity".
*
* @param genericContent
* the content between angle brackets
* @return true if this looks like a placeholder that should be replaced
*/
private boolean isGenericPlaceholder(String genericContent) {
if (genericContent.isEmpty()) {
return false;
}

String trimmed = genericContent.trim();

// Simple case: just a single uppercase letter (T, E, K, V, etc.)
if (trimmed.length() == 1 && Character.isUpperCase(trimmed.charAt(0))) {
return true;
}

// Case with bounds: "T extends SomeClass" or "T super SomeClass"
// Pattern: single uppercase letter followed by " extends " or " super "
int extendsIndex = trimmed.indexOf(" extends ");
int superIndex = trimmed.indexOf(" super ");

if (extendsIndex > 0 || superIndex > 0) {
int boundIndex = extendsIndex > 0 ? extendsIndex : superIndex;
// First part should be a single uppercase letter
String typeParam = trimmed.substring(0, boundIndex).trim();
if (typeParam.length() == 1 && Character.isUpperCase(typeParam.charAt(0))) {
return true;
}
}

return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,10 @@ public List<CompilationUnit> getCompilationUnits() {
}

if (stringHasValue(rootInterface)) {
FullyQualifiedJavaType fqjt = new FullyQualifiedJavaType(rootInterface);
// Process rootInterface to replace generic placeholders with actual record type
String recordType = introspectedTable.getBaseRecordType();
String processedRootInterface = processRootInterfaceWithGenerics(rootInterface, recordType);
FullyQualifiedJavaType fqjt = new FullyQualifiedJavaType(processedRootInterface);
interfaze.addSuperInterface(fqjt);
interfaze.addImportedType(fqjt);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,10 @@ public List<CompilationUnit> getCompilationUnits() {
}

if (stringHasValue(rootInterface)) {
FullyQualifiedJavaType fqjt = new FullyQualifiedJavaType(rootInterface);
// Process rootInterface to replace generic placeholders with actual record type
String recordType = introspectedTable.getBaseRecordType();
String processedRootInterface = processRootInterfaceWithGenerics(rootInterface, recordType);
FullyQualifiedJavaType fqjt = new FullyQualifiedJavaType(processedRootInterface);
interfaze.addSuperInterface(fqjt);
interfaze.addImportedType(fqjt);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,10 @@ protected Interface createBasicInterface() {
}

if (stringHasValue(rootInterface)) {
FullyQualifiedJavaType fqjt = new FullyQualifiedJavaType(rootInterface);
// Process rootInterface to replace generic placeholders with actual record type
String processedRootInterface = processRootInterfaceWithGenerics(rootInterface,
recordType.getFullyQualifiedName());
FullyQualifiedJavaType fqjt = new FullyQualifiedJavaType(processedRootInterface);
interfaze.addSuperInterface(fqjt);
interfaze.addImportedType(fqjt);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,20 @@ specified with the <a href="property.html">&lt;property&gt;</a> child element:</
<p><b>Important:</b> MBG does not verify that the interface exists, or is a
valid Java interface.</p>
<p>If specified, the value of this property should be a fully qualified
interface name (like com.mycompany.MyRootInterface).</p></td>
interface name (like com.mycompany.MyRootInterface).</p>
<p><b>Generic Type Support:</b> If the root interface is a generic interface, you can
use a placeholder for the type parameter. The placeholder will be automatically
replaced with the actual record type for each table. For example:
<ul>
<li><code>com.mycompany.BaseMapper&lt;T&gt;</code> will become
<code>com.mycompany.BaseMapper&lt;com.example.User&gt;</code> for a User table</li>
<li><code>com.mycompany.BaseMapper&lt;T extends Entity&gt;</code> will become
<code>com.mycompany.BaseMapper&lt;com.example.User&gt;</code></li>
</ul>
Placeholders are typically single uppercase letters (T, E, K, V, etc.) optionally
followed by " extends ..." or " super ...". If you specify a fully qualified type
(like <code>BaseMapper&lt;com.example.User&gt;</code>), it will be used as-is without
replacement.</p></td>
</tr>
</table>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,20 @@ specified with the <a href="property.html">&lt;property&gt;</a> child element:</
<p><b>Important:</b> MBG does not verify that the interface exists, or is a
valid Java interface.</p>
<p>If specified, the value of this property should be a fully qualified
interface name (like com.mycompany.MyRootInterface).</p></td>
interface name (like com.mycompany.MyRootInterface).</p>
<p><b>Generic Type Support:</b> If the root interface is a generic interface, you can
use a placeholder for the type parameter. The placeholder will be automatically
replaced with the actual record type for this table. For example:
<ul>
<li><code>com.mycompany.BaseMapper&lt;T&gt;</code> will become
<code>com.mycompany.BaseMapper&lt;com.example.User&gt;</code></li>
<li><code>com.mycompany.BaseMapper&lt;T extends Entity&gt;</code> will become
<code>com.mycompany.BaseMapper&lt;com.example.User&gt;</code></li>
</ul>
Placeholders are typically single uppercase letters (T, E, K, V, etc.) optionally
followed by " extends ..." or " super ...". If you specify a fully qualified type
(like <code>BaseMapper&lt;com.example.User&gt;</code>), it will be used as-is without
replacement.</p></td>
</tr>
<tr>
<td valign="top">runtimeCatalog</td>
Expand Down