- 浏览: 979885 次
文章分类
- 全部博客 (428)
- Hadoop (2)
- HBase (1)
- ELK (1)
- ActiveMQ (13)
- Kafka (5)
- Redis (14)
- Dubbo (1)
- Memcached (5)
- Netty (56)
- Mina (34)
- NIO (51)
- JUC (53)
- Spring (13)
- Mybatis (17)
- MySQL (21)
- JDBC (12)
- C3P0 (5)
- Tomcat (13)
- SLF4J-log4j (9)
- P6Spy (4)
- Quartz (12)
- Zabbix (7)
- JAVA (9)
- Linux (15)
- HTML (9)
- Lucene (0)
- JS (2)
- WebService (1)
- Maven (4)
- Oracle&MSSQL (14)
- iText (11)
- Development Tools (8)
- UTILS (4)
- LIFE (8)
最新评论
-
Donald_Draper:
Donald_Draper 写道刘落落cici 写道能给我发一 ...
DatagramChannelImpl 解析三(多播) -
Donald_Draper:
刘落落cici 写道能给我发一份这个类的源码吗Datagram ...
DatagramChannelImpl 解析三(多播) -
lyfyouyun:
请问楼主,执行消息发送的时候,报错:Transport sch ...
ActiveMQ连接工厂、连接详解 -
ezlhq:
关于 PollArrayWrapper 状态含义猜测:参考 S ...
WindowsSelectorImpl解析一(FdMap,PollArrayWrapper) -
flyfeifei66:
打算使用xmemcache作为memcache的客户端,由于x ...
Memcached分布式客户端(Xmemcached)
SqlSessionFactory初始化:http://donald-draper.iteye.com/2331917
Mybatis加载解析Mapper(xml)文件第一讲:http://donald-draper.iteye.com/blog/2333125
Mybatis加载解析Mapper(xml)文件第二讲:http://donald-draper.iteye.com/blog/2333191
Mybatis 解析Mapper(class):http://donald-draper.iteye.com/blog/2333293
Mybatis的Environment解析详解:http://donald-draper.iteye.com/blog/2334133
解析Mapper xml文件中的select|insert|update|delete节点
来看这一句
//返回LanguageDriver
//configuration,返回LanguageDriverRegistry
//LanguageDriverRegistry
从默认的configuration的构造方法中,我们可以看,DefaultDriverClass为XMLLanguageDriver
下面来看一下,LangDriver如何创建SqlSource,而LangDriver为XMLLanguageDriver
//XMLLanguageDriver
来看XMLScriptBuilder根据statement节点,创建SqlSource
//DynamicSqlSource
sql语句构建器
//SqlSourceBuilder
//创建sql参数解析器,
//解析sql
//GenericTokenParser
//StaticSqlSource
//sql包装类
//MappedStatement
//获取sql的包装类,关键在MappedStatement的这3个LanguageDriver,SqlSource,Configuration参数
总结:
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
附:
//LanguageDriver
//ParameterMappingTokenHandler
//MixedSqlNode
//SqlNode
//DynamicContext
Mybatis加载解析Mapper(xml)文件第一讲:http://donald-draper.iteye.com/blog/2333125
Mybatis加载解析Mapper(xml)文件第二讲:http://donald-draper.iteye.com/blog/2333191
Mybatis 解析Mapper(class):http://donald-draper.iteye.com/blog/2333293
Mybatis的Environment解析详解:http://donald-draper.iteye.com/blog/2334133
解析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参数
总结:
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
附:
//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; } } }
发表评论
-
Mybatis缓存实现
2016-12-07 10:36 978SqlSessionFactory初始化:http://don ... -
DefaultSqlSession第三讲-事务提交,回滚,关闭SqlSession,清除缓存
2016-11-20 11:07 5695上面两篇讲过query和update及flushStateme ... -
DefaultSqlSession第二讲-更新,刷新Statement
2016-11-20 11:06 627上一篇文章中,我们讲到DefaultSqlSession的查询 ... -
DefaultSqlSession第一讲query解析
2016-11-20 11:06 1619上一篇文章:我们说过DefaultSqlSession,Def ... -
Mybatis的SqlSession解析
2016-11-20 11:02 2542在前文中,Mybatis使用教程中,有下面一段代码: Sql ... -
Mybatis的Reflector解析
2016-11-18 12:53 2157Mybatis的MetaObject解析:http://don ... -
Mybatis的MetaObject解析
2016-11-18 11:40 9574SqlSessionFactory初始化:http://don ... -
Mybatis的Environment解析详解
2016-10-29 18:28 1990SqlSessionFactory初始化:http://don ... -
Mybatis 解析Mapper(class)
2016-10-26 11:44 3342SqlSessionFactory初始化:http://don ... -
Mybatis加载解析Mapper(xml)文件第二讲
2016-10-25 21:30 4780SqlSessionFactory初始化:http://don ... -
Mybatis加载解析Mapper(xml)文件第一讲
2016-10-25 16:51 6172SqlSessionFactory初始化:http://don ... -
SqlSessionFactory初始化
2016-10-20 15:38 2453mybatis 使用教程:http://donald-drap ... -
mybatis 使用教程
2016-10-14 09:03 906Myeclispe下mybatis generator的使 ... -
Spring+Mybatis多数据源的实现
2016-09-21 18:15 3093浅谈Spring事务隔离级别:http://www.cnblo ... -
mybaitis CDATA 防止字符被解析
2016-08-17 18:45 722在使用mybatis 时我们sql是写在xml 映射文件中,如 ... -
Myeclispe下mybatis generator的使用
2016-07-05 18:05 1372准备:下载附件包解压到myeclispe的dropins文件夹 ...
相关推荐
通过对`BoundSql`源码的研究,我们可以更加深入地理解MyBatis是如何处理SQL语句及其参数的。`BoundSql` 不仅是MyBatis执行数据库操作的重要组件,也是理解和掌握MyBatis框架的关键之一。希望本文能帮助读者更好地...
2. **SQL语句解析**: - `XMLStatementBuilder`解析SQL语句节点,构建`MappedStatement`对象,其中包含了SQL的ID、参数类型、返回类型、SQL源等信息。 - `SqlSourceBuilder`进一步解析动态SQL,将静态和动态部分...
MyBatis 在解析这些动态 SQL 时,会基于 `DynamicSqlSource` 类生成 `BoundSql` 对象,该对象包含了最终的 SQL 语句和参数信息,确保了 SQL 的正确执行。 总结来说,MyBatis 动态 SQL 提供了一种灵活的方式来根据...
本话题主要探讨如何编写一个MyBatis语句规范化拦截器,防止因条件错误导致的批量操作对数据库造成不可逆转的影响。 ### 1. 目的 规范化的SQL语句可以避免因程序员的疏忽或者错误导致的数据误删或误改。例如,未加...
MyBatis 提供了强大的动态 SQL 功能,通过 `<if>`, `<choose>`, `<when>`, `<otherwise>`, `<where>`, `<set>`, `<foreach>` 等标签,可以在 XML 文件中编写条件语句,实现 SQL 语句的动态生成,适应不同的查询需求...
首先,我们需要了解的是,Mybatis在对SQL语句进行预编译之前,会对SQL语句进行动态解析,解析为一个BoundSql对象。在动态SQL解析阶段,#{ }和${ }会有不同的表现。#{ }:解析为一个JDBC预编译语句(prepared ...
2. **创建BoundSql**:解析SQL语句,生成`BoundSql`对象。 3. **参数处理**:根据传入的参数值设置`BoundSql`中的参数。 4. **执行器处理**:调用`Executor`执行SQL语句。 5. **结果处理**:处理执行结果,将其转换...
它提供了 getBoundSql 方法,该方法将参数对象转换为 BoundSql 对象,并将 SQL 语句动态地拼接起来。 SqlNode 接口 SqlNode 接口是 Mybatis 中的一个重要接口,它定义了 SQL 语句节点的行为。SqlNode 接口有多个...
通过阅读源码,开发者可以深入理解MyBatis的工作原理,学习如何设计和实现一个高效的ORM框架,以及如何处理SQL语句的动态生成与执行。 描述中提到的"Eclipse中点击Open Source导入源码",意味着我们可以使用Eclipse...
// 获取BoundSql对象,其中包含了SQL语句和参数 BoundSql boundSql = mappedStatement.getBoundSql((Object) invocation.getArgs()[3]); // ... 分页逻辑 ... } } ``` 2. **识别分页查询**:我们需要判断当前...
在执行SQL前,MyBatis需要将XML中的SQL语句和结果集映射解析为`MappedStatement`对象,这一步由`Configuration`类完成。`MappedStatement`包含了完整的SQL语句、参数映射信息和结果集映射信息。通过`BoundSql`,...
除了上述基本的实现方式,还可以考虑结合MyBatis的`ExecutorType.SIMPLE`或`ExecutorType.BATCH`执行器,以及`boundSql`对象来优化分页性能。例如,可以避免在`SIMPLE`模式下对每个分页请求都进行数据库连接的创建和...
拦截器是MyBatis动态代理机制的一部分,它允许我们在执行SQL语句之前或之后插入一些额外的操作。在分页插件的场景下,我们可以利用拦截器在执行查询前计算页码和每页大小,然后动态修改原始的SQL语句,使其包含分页...
MyBatis通过`SqlSourceBuilder`和`BoundSql`等类解析XML中的动态元素,并在运行时构建出实际的SQL语句。而"工具"则可能意味着我们可以结合其他开发工具,如IDE插件或日志工具,来辅助调试和优化这些动态SQL。 至于...
- 它的主要作用在于处理动态传入的参数,并将其正确地绑定到SQL语句中,从而实现SQL语句的动态化。 2. **重要性**: - ParameterHandler对于提高SQL执行效率至关重要,因为它能够避免硬编码SQL语句中的参数,使得...
在Mybatis中,拦截器扮演着关键角色,它允许我们在特定的执行点(如SQL语句的准备、执行等)插入自定义的行为。`Interceptor`接口提供了`intercept`方法,这个方法会在目标方法执行前后被调用,使得我们可以对原方法...
- `org.apache.ibatis.builder.xml.XMLMapperBuilder`: 这是解析XML映射文件的关键类,其中的`parseNestedResultMap()`方法处理`<resultMap>`标签,包含了多对多映射的解析逻辑。 - `org.apache.ibatis.mapping....
在拦截器中,我们需要解析SQL,找出原始的查询语句并添加分页条件。通常,我们可以通过`RowBounds`参数来获取分页信息。`RowBounds`对象包含了起始行和每页记录数,我们可以利用这些信息构造新的SQL。 ```java ...
- **3.4.1版本**:此版本修复了一个关键的bug,即当`SqlParser`解析SQL失败时可能会返回不包含`count(*)`的SQL,从而导致查询失败的问题。 - **3.4.0版本**:增加了对`@SelectProvider`注解的支持,并进行了大量内部...