mybatis 动态标签语句的解析(BoundSql)

本文详细介绍了 MyBatis 中 SQL 语句的解析流程,包括如何通过 XMLStatementBuilder 处理 select、insert、update 和 delete 节点,以及如何使用 XMLLanguageDriver 创建 SqlSource。此外还深入探讨了 DynamicSqlSource 如何解析动态 SQL 语句。
SqlSessionFactory初始化:[url]http://donald-draper.iteye.com/2331917[/url]
Mybatis加载解析Mapper(xml)文件第一讲:[url]http://donald-draper.iteye.com/blog/2333125[/url]
Mybatis加载解析Mapper(xml)文件第二讲:[url]http://donald-draper.iteye.com/blog/2333191[/url]
Mybatis 解析Mapper(class):[url]http://donald-draper.iteye.com/blog/2333293[/url]
Mybatis的Environment解析详解:[url]http://donald-draper.iteye.com/blog/2334133[/url]
解析Mapper xml文件中的select|insert|update|delete节点
public class XMLStatementBuilder extends BaseBuilder
{

private MapperBuilderAssistant builderAssistant;
private XNode context;
private String requiredDatabaseId;
public void parseStatementNode()
{
select|insert|update|delete节点的lang属性
String lang = context.getStringAttribute("lang");
LanguageDriver langDriver = getLanguageDriver(lang);
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
}
}

来看这一句
LanguageDriver langDriver = getLanguageDriver(lang);


//返回LanguageDriver
private LanguageDriver getLanguageDriver(String lang)
{
Class langClass;
if(lang == null)
{
//如果statement节点的lang属性为空,则返回默认的DefaultDriverClass
langClass = configuration.getLanguageRegistry().getDefaultDriverClass();
} else
{
langClass = resolveClass(lang);
configuration.getLanguageRegistry().register(langClass);
}
if(langClass == null)
langClass = configuration.getLanguageRegistry().getDefaultDriverClass();
return configuration.getLanguageRegistry().getDriver(langClass);
}

//configuration,返回LanguageDriverRegistry
 public LanguageDriverRegistry getLanguageRegistry()
{
return languageRegistry;
}

//LanguageDriverRegistry
  public class LanguageDriverRegistry
{
private final Map LANGUAGE_DRIVER_MAP = new HashMap();
private Class defaultDriverClass;
public void register(Class cls)
{
if(cls == null)
throw new IllegalArgumentException("null is not a valid Language Driver");
if(!org/apache/ibatis/scripting/LanguageDriver.isAssignableFrom(cls))
throw new ScriptingException((new StringBuilder()).append(cls.getName()).append(" does not implements ").append(org/apache/ibatis/scripting/LanguageDriver.getName()).toString());
LanguageDriver driver = (LanguageDriver)LANGUAGE_DRIVER_MAP.get(cls);
if(driver == null)
try
{
driver = (LanguageDriver)cls.newInstance();
LANGUAGE_DRIVER_MAP.put(cls, driver);
}
}
public Class getDefaultDriverClass()
{
return defaultDriverClass;
}
}

从默认的configuration的构造方法中,我们可以看,DefaultDriverClass为XMLLanguageDriver

languageRegistry.setDefaultDriverClass(org/apache/ibatis/scripting/xmltags/XMLLanguageDriver);


下面来看一下,LangDriver如何创建SqlSource,而LangDriver为XMLLanguageDriver
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);

//XMLLanguageDriver
public class XMLLanguageDriver
implements LanguageDriver
{
//根据configuration,和statement节点和参数类型来创建SqlSource
public SqlSource createSqlSource(Configuration configuration, XNode script, Class parameterType)
{
//构建statement节点解析器
XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script);
return builder.parseScriptNode();
}
}

来看XMLScriptBuilder根据statement节点,创建SqlSource
public class XMLScriptBuilder extends BaseBuilder
{
private XNode context;//statement节点
private Map nodeHandlers = new HashMap() {
private static final long serialVersionUID = 7123056019193266281L;
final XMLScriptBuilder this$0;
{
this$0 = XMLScriptBuilder.this;
super();
//statement的节点的子节点where,forEach,if等处理器
put("trim", new TrimHandler());
put("where", new WhereHandler());
put("set", new SetHandler());
put("foreach", new ForEachHandler());
put("if", new IfHandler());
put("choose", new ChooseHandler());
put("when", new IfHandler());
put("otherwise", new OtherwiseHandler());
put("bind", new BindHandler());
}
};
//IF节点处理器
private class IfHandler
implements NodeHandler
{

public void handleNode(XNode nodeToHandle, List targetContents)
{
List contents = parseDynamicTags(nodeToHandle);
MixedSqlNode mixedSqlNode = new MixedSqlNode(contents);
String test = nodeToHandle.getStringAttribute("test");
IfSqlNode ifSqlNode = new IfSqlNode(mixedSqlNode, test);
targetContents.add(ifSqlNode);
}
}
private static interface NodeHandler
{

public abstract void handleNode(XNode xnode, List list);
}
//解析statement节点
public SqlSource parseScriptNode()
{
//解析statement节点的动态标签
List contents = parseDynamicTags(context);
MixedSqlNode rootSqlNode = new MixedSqlNode(contents);
//构建动态sql语句源,DynamicSqlSource
SqlSource sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
return sqlSource;
}
//解析statement节点的动态标签
private List parseDynamicTags(XNode node)
{
List contents = new ArrayList();
NodeList children = node.getNode().getChildNodes();
for(int i = 0; i < children.getLength(); i++)
{
XNode child = node.newXNode(children.item(i));
String nodeName = child.getNode().getNodeName();
if(child.getNode().getNodeType() == 4 || child.getNode().getNodeType() == 3)
{
String data = child.getStringBody("");
contents.add(new TextSqlNode(data));
continue;
}
if(child.getNode().getNodeType() != 1 || "selectKey".equals(nodeName))
continue;
NodeHandler handler = (NodeHandler)nodeHandlers.get(nodeName);
if(handler == null)
throw new BuilderException((new StringBuilder()).append("Unknown element <").append(nodeName).append("> in SQL statement.").toString());
handler.handleNode(child, contents);
}
return contents;
}
}

//DynamicSqlSource
public class DynamicSqlSource
implements SqlSource
{
private Configuration configuration;
private SqlNode rootSqlNode;

public DynamicSqlSource(Configuration configuration, SqlNode rootSqlNode)
{
this.configuration = configuration;
this.rootSqlNode = rootSqlNode;
}
public BoundSql getBoundSql(Object parameterObject)
{
DynamicContext context = new DynamicContext(configuration, parameterObject);
rootSqlNode.apply(context);
//创建SqlSourceBuilder
SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
Class parameterType = ((Class) (parameterObject != null ? parameterObject.getClass() : java/lang/Object));
//解析statement节点,并返回sqlSource
SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
java.util.Map.Entry entry;
for(Iterator i$ = context.getBindings().entrySet().iterator(); i$.hasNext(); boundSql.setAdditionalParameter((String)entry.getKey(), entry.getValue()))
entry = (java.util.Map.Entry)i$.next();

return boundSql;
}
}

sql语句构建器
//SqlSourceBuilder
public class SqlSourceBuilder extends BaseBuilder
{
private static class ParameterMappingTokenHandler extends BaseBuilder
public SqlSourceBuilder(Configuration configuration)
{
super(configuration);
}

public SqlSource parse(String originalSql, Class parameterType, Map additionalParameters)
{
ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters);
//创建sql语句解析器
GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);
//解析sql
String sql = parser.parse(originalSql);
//返回StaticSqlSource
return new StaticSqlSource(configuration, sql, handler.getParameterMappings());
}
private static final String parameterProperties = "javaType,jdbcType,mode,numericScale,resultMap,typeHandler,jdbcTypeName";
}


//创建sql参数解析器,
GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);

//解析sql
String sql = parser.parse(originalSql);

//GenericTokenParser
public class GenericTokenParser
{
private final String openToken;//参数开始标志
private final String closeToken;//参数解析标志
private final TokenHandler handler;//ParameterMappingTokenHandler,参数hanler
public GenericTokenParser(String openToken, String closeToken, TokenHandler handler)
{
this.openToken = openToken;
this.closeToken = closeToken;
this.handler = handler;
}
//解析statement中的sql语句
public String parse(String text)
{
StringBuilder builder = new StringBuilder();
if(text != null && text.length() > 0)
{
char src[] = text.toCharArray();
int offset = 0;
for(int start = text.indexOf(openToken, offset); start > -1; start = text.indexOf(openToken, offset))
{
if(start > 0 && src[start - 1] == '\\')
{
builder.append(src, offset, start - 1).append(openToken);
offset = start + openToken.length();
continue;
}
int end = text.indexOf(closeToken, start);
if(end == -1)
{
builder.append(src, offset, src.length - offset);
offset = src.length;
} else
{
builder.append(src, offset, start - offset);
offset = start + openToken.length();
String content = new String(src, offset, end - offset);
builder.append(handler.handleToken(content));
offset = end + closeToken.length();
}
}

if(offset < src.length)
builder.append(src, offset, src.length - offset);
}
return builder.toString();
}
}

//StaticSqlSource
public class StaticSqlSource
implements SqlSource
{

private String sql;
private List parameterMappings;
private Configuration configuration;
//构造静态的SqlSource根据configuration,sql,parameterMappings
public StaticSqlSource(Configuration configuration, String sql, List parameterMappings)
{
this.sql = sql;
this.parameterMappings = parameterMappings;
this.configuration = configuration;
}
//构建BoundSql
public BoundSql getBoundSql(Object parameterObject)
{
return new BoundSql(configuration, sql, parameterMappings, parameterObject);
}
}

//sql包装类
public class BoundSql
{
private String sql;//执行SQL,prepare
private List parameterMappings;//参数映射
private Object parameterObject;//参数
private Map additionalParameters;
private MetaObject metaParameters;
public BoundSql(Configuration configuration, String sql, List parameterMappings, Object parameterObject)
{
this.sql = sql;
this.parameterMappings = parameterMappings;
this.parameterObject = parameterObject;
additionalParameters = new HashMap();
metaParameters = configuration.newMetaObject(additionalParameters);
}
}

//MappedStatement
public final class MappedStatement
{
public static class Builder{}
private String resource;
private Configuration configuration;
private String id;
private Integer fetchSize;
private Integer timeout;
private StatementType statementType;//默认为Prepare,call - 》CallStatement
private ResultSetType resultSetType;
private SqlSource sqlSource;//sql语句产生器
private Cache cache;//缓存类型
private ParameterMap parameterMap;
private List resultMaps;
private boolean flushCacheRequired;
private boolean useCache;//是否用缓存
private boolean resultOrdered;
private SqlCommandType sqlCommandType;//命令类型,Select,delete,update,insert
private KeyGenerator keyGenerator;//主键生成器
private String keyProperties[];
private String keyColumns[];
private boolean hasNestedResultMaps;
private String databaseId;
private Log statementLog;
private LanguageDriver lang;//statement节点解析Drive,默认为XMLLanguageDriver
//
public BoundSql getBoundSql(Object parameterObject)
{
BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
List parameterMappings = boundSql.getParameterMappings();
if(parameterMappings == null || parameterMappings.size() <= 0)
boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
Iterator i$ = boundSql.getParameterMappings().iterator();
do
{
if(!i$.hasNext())
break;
ParameterMapping pm = (ParameterMapping)i$.next();
String rmId = pm.getResultMapId();
if(rmId != null)
{
ResultMap rm = configuration.getResultMap(rmId);
if(rm != null)
hasNestedResultMaps |= rm.hasNestedResultMaps();
}
} while(true);
return boundSql;
}
}

//获取sql的包装类,关键在MappedStatement的这3个LanguageDriver,SqlSource,Configuration参数
总结:
[color=blue]MappedStatement这Mapper文件中,每个select,insert,update,delete子节点的包装,从MappedStatement,获取sql的包装类BoundSql,主要根据这三个参数LanguageDriver,SqlSource,Configuration;在Configuration的构造函数,
我们可以看出LanguageDriver默认的,为XMLLanguageDriver,XMLLanguageDriver创建SqlSource时,由XMLScriptBuilder去解析具体的select|insert|update|delete节点信息,XMLScriptBuilder中有各种动态标签的处理器,如if,where,foreach等;
XMLScriptBuilder将具体动态标签语句,委托给DynamicSqlSource去处理,而DynamicSqlSource将具体的动态sql的解析,拼接委托
给SqlSourceBuilder,SqlSourceBuilder通过参数类型映射处理器ParameterMappingTokenHandler和参数解析器GenericTokenParser去除里;ParameterMappingTokenHandler主要处理参数与jdbc type的映射ParameterMappings,GenericTokenParser主要处理sql语句中参数的解析,拼接 sql,然后根据configuration, sql,ParameterMappings构造StaticSqlSource;
其实DynamicSqlSource获取BoundSql,解析动态标签及语句,转化为StaticSqlSource,实际上是,由StaticSqlSource,创建BoundSql[/color]

附:
//LanguageDriver
public interface LanguageDriver
{
public abstract ParameterHandler createParameterHandler(MappedStatement mappedstatement, Object obj, BoundSql boundsql);
public abstract SqlSource createSqlSource(Configuration configuration, XNode xnode, Class class1);
public abstract SqlSource createSqlSource(Configuration configuration, String s, Class class1);
}

//ParameterMappingTokenHandler
private static class ParameterMappingTokenHandler extends BaseBuilder
implements TokenHandler
{

public List getParameterMappings()
{
return parameterMappings;
}
//将参数类型添加到parameterMappings,返回占位符
public String handleToken(String content)
{
parameterMappings.add(buildParameterMapping(content));
return "?";
}
//获取参数的ParameterMapping
private ParameterMapping buildParameterMapping(String content)
{
org.apache.ibatis.mapping.ParameterMapping.Builder builder;
Class javaType;
String typeHandlerAlias;
label0:
{
Map propertiesMap = parseParameterMapping(content);
String property = (String)propertiesMap.get("property");
Class propertyType;
if(metaParameters.hasGetter(property))
propertyType = metaParameters.getGetterType(property);
else
if(typeHandlerRegistry.hasTypeHandler(parameterType))
propertyType = parameterType;
else
if(JdbcType.CURSOR.name().equals(propertiesMap.get("jdbcType")))
propertyType = java/sql/ResultSet;
else
if(property != null)
{
MetaClass metaClass = MetaClass.forClass(parameterType);
if(metaClass.hasGetter(property))
propertyType = metaClass.getGetterType(property);
else
propertyType = java/lang/Object;
} else
{
propertyType = java/lang/Object;
}
builder = new org.apache.ibatis.mapping.ParameterMapping.Builder(configuration, property, propertyType);
javaType = propertyType;
typeHandlerAlias = null;
String name;
label1:
do
{
for(Iterator i$ = propertiesMap.entrySet().iterator(); i$.hasNext();)
{
java.util.Map.Entry entry = (java.util.Map.Entry)i$.next();
name = (String)entry.getKey();
String value = (String)entry.getValue();
if("javaType".equals(name))
{
javaType = resolveClass(value);
builder.javaType(javaType);
} else
if("jdbcType".equals(name))
builder.jdbcType(resolveJdbcType(value));
else
if("mode".equals(name))
builder.mode(resolveParameterMode(value));
else
if("numericScale".equals(name))
builder.numericScale(Integer.valueOf(value));
else
if("resultMap".equals(name))
builder.resultMapId(value);
else
if("typeHandler".equals(name))
{
typeHandlerAlias = value;
} else
{
if(!"jdbcTypeName".equals(name))
continue label1;
builder.jdbcTypeName(value);
}
}

break label0;
} while("property".equals(name));
if("expression".equals(name))
throw new BuilderException("Expression based parameters are not supported yet");
else
throw new BuilderException((new StringBuilder()).append("An invalid property '").append(name).append("' was found in mapping #{").append(content).append("}. Valid properties are ").append("javaType,jdbcType,mode,numericScale,resultMap,typeHandler,jdbcTypeName").toString());
}
if(typeHandlerAlias != null)
builder.typeHandler(resolveTypeHandler(javaType, typeHandlerAlias));
return builder.build();
}

private Map parseParameterMapping(String content)
{
return ParameterExpressionParser.parse(content);
BuilderException ex;
ex;
throw ex;
ex;
throw new BuilderException((new StringBuilder()).append("Parsing error was found in mapping #{").append(content).append("}. Check syntax #{property|(expression), var1=value1, var2=value2, ...} ").toString(), ex);
}

private List parameterMappings;
private Class parameterType;
private MetaObject metaParameters;

public ParameterMappingTokenHandler(Configuration configuration, Class parameterType, Map additionalParameters)
{
super(configuration);
parameterMappings = new ArrayList();
this.parameterType = parameterType;
metaParameters = configuration.newMetaObject(additionalParameters);
}
}

//MixedSqlNode
public class MixedSqlNode
implements SqlNode
{
private List contents;
public MixedSqlNode(List contents)
{
this.contents = contents;
}
public boolean apply(DynamicContext context)
{
SqlNode sqlNode;
for(Iterator i$ = contents.iterator(); i$.hasNext(); sqlNode.apply(context))
sqlNode = (SqlNode)i$.next();

return true;
}
}

//SqlNode
public interface SqlNode
{
public abstract boolean apply(DynamicContext dynamiccontext);
}

//DynamicContext
public class DynamicContext
{
public static final String PARAMETER_OBJECT_KEY = "_parameter";
public static final String DATABASE_ID_KEY = "_databaseId";
private final ContextMap bindings;
private final StringBuilder sqlBuilder = new StringBuilder();
private int uniqueNumber;

static
{
OgnlRuntime.setPropertyAccessor(org/apache/ibatis/scripting/xmltags/DynamicContext$ContextMap, new ContextAccessor());
}
public String getSql()
{
return sqlBuilder.toString().trim();
}
static class ContextMap extends HashMap
{

public Object get(Object key)
{
String strKey = (String)key;
if(super.containsKey(strKey))
return super.get(strKey);
if(parameterMetaObject != null)
{
Object object = parameterMetaObject.getValue(strKey);
if(object != null)
super.put(strKey, object);
return object;
} else
{
return null;
}
}
private static final long serialVersionUID = 2977601501966151582L;
private MetaObject parameterMetaObject;

public ContextMap(MetaObject parameterMetaObject)
{
this.parameterMetaObject = parameterMetaObject;
}
}
}
<think> 我们正在讨论MyBatis如何解析SQL语句中的表名。根据用户的问题,他想要查找使用MyBatis解析SQL语句中表名称的方法。 在MyBatis中,SQL语句通常写在映射文件(XML)或注解中,MyBatis本身并不提供直接解析SQL语句获取表名的API。但是,我们可以通过其他方式来实现这一需求。 常见的思路: 1. 使用MyBatis的插件机制(Interceptor)拦截SQL语句,然后对SQL进行解析。 2. 借助第三方SQL解析工具(如jsqlparser)来解析SQL,提取表名。 由于MyBatis本身不提供SQL解析功能,所以通常采用第二种方式,即使用jsqlparser这样的库。 步骤: a. 添加jsqlparser依赖 b. 编写一个MyBatis插件,在插件中拦截执行的SQL,然后使用jsqlparser解析SQL并提取表名。 下面给出一个简单的实现示例: 1. 添加Maven依赖: ```xml <dependency> <groupId>com.github.jsqlparser</groupId> <artifactId>jsqlparser</artifactId> <version>4.2</version> </dependency> ``` 2. 编写MyBatis插件: ```java @Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})}) public class TableNameInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { StatementHandler statementHandler = (StatementHandler) invocation.getTarget(); BoundSql boundSql = statementHandler.getBoundSql(); String sql = boundSql.getSql(); // 使用jsqlparser解析SQL Statement statement = CCJSqlParserUtil.parse(sql); Select selectStatement = (Select) statement; PlainSelect plainSelect = (PlainSelect) selectStatement.getSelectBody(); FromItem fromItem = plainSelect.getFromItem(); // 获取表名(简单情况,不考虑join等) String tableName = fromItem.toString(); System.out.println("Table Name: " + tableName); // 继续执行 return invocation.proceed(); } @Override public Object plugin(Object target) { return Plugin.wrap(target, this); } @Override public void setProperties(Properties properties) { } } ``` 注意:上述代码仅处理了简单的SELECT语句,实际SQL可能很复杂(如JOIN、子查询等),需要更完善的解析。 3. 在MyBatis配置文件中注册插件: ```xml <plugins> <plugin interceptor="com.example.TableNameInterceptor"/> </plugins> ``` 这样,每次执行SQL时,插件就会拦截并解析出表名(这里只是打印,实际可根据需要处理)。 但是,这种方法只能拦截执行的SQL,对于映射文件中的SQL语句,如果希望在应用启动时就解析,可以考虑在初始化时读取XML文件中的SQL语句进行解析。 另一种方式:解析MyBatis的映射文件(XML)或注解中的SQL语句。我们可以通过MyBatis提供的解析XML的API来获取SQL语句,然后再用jsqlparser解析表名。 例如,我们可以使用`XMLMapperBuilder`等工具来解析映射文件,获取每条SQL语句的内容,然后进行解析。但这通常是在MyBatis初始化过程中完成的,需要编写额外的代码。 总结: 要解析MyBatis中SQL语句的表名,推荐使用MyBatis插件机制配合jsqlparser库,在SQL执行时拦截并解析。如果需要在启动时解析所有SQL,则需要读取映射文件并解析其中的SQL语句。 注意:SQL语句的复杂性(如动态SQL)可能会使解析变得困难,因为动态SQL在运行前可能没有完整的SQL语句。因此,拦截执行的SQL(已经是完整的SQL)更为可靠。 参考文献: [1] MyBatis官方文档 - 插件: https://mybatis.org/mybatis-3/zh/configuration.html#plugins [2] jsqlparser项目地址: https://github.com/JSQLParser/JSqlParser </think> ### MyBatis解析SQL语句中表名称的方法 在MyBatis解析SQL语句的表名称,可以通过以下技术方案实现: #### 1. **使用MyBatis插件拦截SQL** 最可靠的方式是通过自定义`Interceptor`插件拦截运行时SQL: ```java @Intercepts(@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})) public class TableNameInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { StatementHandler handler = (StatementHandler) invocation.getTarget(); BoundSql boundSql = handler.getBoundSql(); String sql = boundSql.getSql(); // 获取原始SQL // 使用SQL解析解析表名 List<String> tableNames = SQLParserUtils.parseTableNames(sql); System.out.println("Detected tables: " + tableNames); return invocation.proceed(); } // 注册到SqlSessionFactory @Bean public ConfigurationCustomizer configurationCustomizer() { return configuration -> configuration.addInterceptor(new TableNameInterceptor()); } } ``` #### 2. **使用SQL解析库(推荐JSqlParser)** 添加依赖: ```xml <dependency> <groupId>com.github.jsqlparser</groupId> <artifactId>jsqlparser</artifactId> <version>4.6</version> </dependency> ``` 解析工具类示例: ```java public class SQLParserUtils { public static List<String> parseTableNames(String sql) { List<String> tables = new ArrayList<>(); try { Statement stmt = CCJSqlParserUtil.parse(sql); if (stmt instanceof Select) { Select select = (Select) stmt; TablesNamesFinder finder = new TablesNamesFinder(); tables = finder.getTableList(select); } // 处理INSERT/UPDATE/DELETE语句 } catch (JSQLParserException e) { throw new RuntimeException("SQL解析失败", e); } return tables; } } ``` #### 3. **解析映射文件中的SQL** 直接解析XML/注解中的SQL语句(需注意动态SQL): ```java // 加载MyBatis配置文件 InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); // 获取所有映射语句 Collection<String> statementNames = sqlSessionFactory.getConfiguration().getMappedStatementNames(); for (String name : statementNames) { MappedStatement ms = sqlSessionFactory.getConfiguration().getMappedStatement(name); String sql = ms.getBoundSql(null).getSql(); List<String> tables = SQLParserUtils.parseTableNames(sql); } ``` #### 关键注意事项 1. **动态SQL处理** MyBatis动态标签(如`<if>`、`<foreach>`)需生成完整SQL后才能解析表名 $$ \text{运行时SQL} = \text{静态SQL} + \text{动态参数渲染} $$ 2. **复杂语句解析** 支持嵌套查询、JOIN、子查询等场景: ```sql SELECT * FROM main_table m JOIN (SELECT * FROM sub_table) s ON m.id = s.id ``` 3. **SQL方言兼容** 需处理不同数据库的语法差异,如`[dbo].[table]`(SQL Server)、`table`(MySQL) > **性能提示**:生产环境建议缓存解析结果,避免每次执行都解析SQL[^1] [^1]: MyBatis拦截器机制参考官方文档: https://mybatis.org/mybatis-3/zh/configuration.html#plugins ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值