Mybatis中几个重要类

本文基于Mybatis3.2.0版本的代码。

1.org.apache.ibatis.mapping.MappedStatement

MappedStatement类在Mybatis框架中用于表示XML文件中一个sql语句节点,即一个节点,通过org.apache.ibatis.session.Configuration类中的getMappedStatement(String id)方法,可以检索到一个特定的MappedStatement。为了区分不同的Mapper文件中的sql节点,其中的String id方法参数,是以Mapper文件的namespace作为前缀,再加上该节点本身的id值。比如上面生成的两个MappedStatement对象在Mybatis框架中的唯一标识分别是mybatis.UserDao.find和mybatis.UserDao.find2。

打开MappedStatement对象的源码,看一下其中的私有属性。

01 public final class MappedStatement {
02  
03   private String resource;
04   private Configuration configuration;
05   private String id;
06   private Integer fetchSize;
07   private Integer timeout;
08   private StatementType statementType;
09   private ResultSetType resultSetType;
10   private SqlSource sqlSource;
11   private Cache cache;
12   private ParameterMap parameterMap;
13   private List resultMaps;
14   private boolean flushCacheRequired;
15   private boolean useCache;
16   private boolean resultOrdered;
17   private SqlCommandType sqlCommandType;
18   private KeyGenerator keyGenerator;
19   private String[] keyProperties;
20   private String[] keyColumns;
21   private boolean hasNestedResultMaps;
22   private String databaseId;
23   private Log statementLog;
24   private LanguageDriver lang;
25  
26   private MappedStatement() {
27     // constructor disabled
28   }
29   ..........
30

我们可以看到其中的属性基本上和xml元素的属性有对应关系,其中比较重要的有表示查询参数的ParameterMap对象,表示sql查询结果映射关系的ResultMap列表resultMaps,当然最重要的还是执行动态sql计算和获取的SqlSource对象。通过这些对象的通力合作,MappedStatement接受用户的查询参数对象,动态计算出要执行的sql语句,在数据库中执行sql语句后,再将取得的数据封装为JavaBean对象返回给用户。MappedStatement对象的这些功能,也体现出了Mybatis这个框架的核心价值,“根据用户提供的查询参数对象,动态执行sql语句,并将结果封装为Java对象”。

2.org.apache.ibatis.mapping.SqlSource

SqlSource是一个接口类,在MappedStatement对象中是作为一个属性出现的,它的代码如下:

01 package org.apache.ibatis.mapping;
02  
03 /**
04  *
05  * This bean represets the content of a mapped statement read from an XML file
06  * or an annotation. It creates the SQL that will be passed to the database out
07  * of the input parameter received from the user.
08  *
09  */
10 public interface SqlSource {
11  
12   BoundSql getBoundSql(Object parameterObject);
13  
14 }
SqlSource接口只有一个getBoundSql(Object parameterObject)方法,返回一个BoundSql对象。一个BoundSql对象,代表了一次sql语句的实际执行,而SqlSource对象的责任,就是根据传入的参数对象,动态计算出这个BoundSql,也就是说Mapper文件中的节点的计算,是由SqlSource对象完成的。SqlSource最常用的实现类是DynamicSqlSource,来看一看它的代码:
01 package org.apache.ibatis.scripting.xmltags;
02  
03 import java.util.Map;
04  
05 import org.apache.ibatis.builder.SqlSourceBuilder;
06 import org.apache.ibatis.mapping.BoundSql;
07 import org.apache.ibatis.mapping.SqlSource;
08 import org.apache.ibatis.session.Configuration;
09  
10 public class DynamicSqlSource implements SqlSource {
11  
12   private Configuration configuration;
13   private SqlNode rootSqlNode;
14  
15   public DynamicSqlSource(Configuration configuration, SqlNode rootSqlNode) {
16     this.configuration = configuration;
17     this.rootSqlNode = rootSqlNode;
18   }
19  
20   public BoundSql getBoundSql(Object parameterObject) {
21     DynamicContext context = new DynamicContext(configuration, parameterObject);
22     rootSqlNode.apply(context);
23     SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
24     Class parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
25     SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
26     BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
27     for (Map.Entry entry : context.getBindings().entrySet()) {
28       boundSql.setAdditionalParameter(entry.getKey(), entry.getValue());
29     }
30     return boundSql;
31   }
32  
33 }

其中的

1 rootSqlNode.apply(context);

这句调用语句,启动了一个非常精密的递归实现的动态计算sql语句的过程,计算过程使用Ognl来根据传入的参数对象计算表达式,生成该次调用过程中实际执行的sql语句。

3.org.apache.ibatis.scripting.xmltags.DynamicContext

DynamicContext类中,有对传入的parameterObject对象进行“map”化处理的部分,也就是说,你传入的pojo对象,会被当作一个键值对数据来源来进行处理,读取这个pojo对象的接口,还是Map对象。从DynamicContext的源码中,能看到很明显的线索。

001 import java.util.HashMap;
002 import java.util.Map;
003  
004 import ognl.OgnlException;
005 import ognl.OgnlRuntime;
006 import ognl.PropertyAccessor;
007  
008 import org.apache.ibatis.reflection.MetaObject;
009 import org.apache.ibatis.session.Configuration;
010  
011 public class DynamicContext {
012  
013   public static final String PARAMETER_OBJECT_KEY = "_parameter";
014   public static final String DATABASE_ID_KEY = "_databaseId";
015  
016   static {
017     OgnlRuntime.setPropertyAccessor(ContextMap.classnew ContextAccessor());
018   }
019  
020   private final ContextMap bindings;
021   private final StringBuilder sqlBuilder = new StringBuilder();
022   private int uniqueNumber = 0;
023  
024   public DynamicContext(Configuration configuration, Object parameterObject) {
025     if (parameterObject != null && !(parameterObject instanceof Map)) {
026       MetaObject metaObject = configuration.newMetaObject(parameterObject);
027       bindings = new ContextMap(metaObject);
028     else {
029       bindings = new ContextMap(null);
030     }
031     bindings.put(PARAMETER_OBJECT_KEY, parameterObject);
032     bindings.put(DATABASE_ID_KEY, configuration.getDatabaseId());
033   }
034  
035   public Map getBindings() {
036     return bindings;
037   }
038  
039   public void bind(String name, Object value) {
040     bindings.put(name, value);
041   }
042  
043   public void appendSql(String sql) {
044     sqlBuilder.append(sql);
045     sqlBuilder.append(" ");
046   }
047  
048   public String getSql() {
049     return sqlBuilder.toString().trim();
050   }
051  
052   public int getUniqueNumber() {
053     return uniqueNumber++;
054   }
055  
056   static class ContextMap extends HashMap {
057     private static final long serialVersionUID = 2977601501966151582L;
058  
059     private MetaObject parameterMetaObject;
060     public ContextMap(MetaObject parameterMetaObject) {

你可能感兴趣的:(mybatis原理)