深入理解java:4.3.1. 框架编程之MyBatis---SQL语句执行的完整流程

Mybatis的整个的执行流程。如下图所示:

原理详解:

MyBatis应用程序根据XML配置文件创建SqlSessionFactory,

SqlSessionFactory在根据配置,配置来源于两个地方,一处是配置文件,一处是Java代码的注解,获取一个SqlSession。

SqlSession包含了执行sql所需要的所有方法,可以通过SqlSession实例直接运行映射的sql语句,完成对数据的增删改查和事务提交等,用完之后关闭SqlSession。

MyBatis的优缺点

优点:

1、简单易学

mybatis本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。

2、灵活

mybatis不会对应用程序或者数据库的现有设计强加任何影响。 sql写在xml里,便于统一管理和优化。通过sql基本上可以实现我们不使用数据访问框架可以实现的所有功能,或许更多。

3、解除sql与程序代码的耦合

通过提供DAL层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。

4、提供映射标签,支持对象与数据库的orm字段关系映射

5、提供对象关系映射标签,支持对象关系组建维护

6、提供xml标签,支持编写动态sql。

缺点:

1、编写SQL语句时工作量很大,尤其是字段多、关联表多时,更是如此。

2、SQL语句依赖于数据库,导致数据库移植性差,不能更换数据库。

3、框架还是比较简陋,功能尚有缺失,虽然简化了数据绑定代码,但是整个底层数据库查询实际还是要自己写的,工作量也比较大,而且不太容易适应快速数据库修改。

4、二级缓存机制不佳

MyBatis源码分析-SQL语句执行的完整流程

MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架。

MyBatis 避免了几乎所有的 JDBC 代码手动设置参数以及获取结果集

MyBatis 可以对配置和原生Map使用简单的 XML 或注解,将接口和 Java 的 POJOs映射成数据库中的记录。

  

MyBatis框架主要完成的是以下2件事情:

  1. 根据JDBC规范建立与数据库的连接
  2. 通过反射打通Java对象与数据库参数交互之间相互转换的关系。

  MyBatis框架是一种典型的交互式框架,先准备好交互的必要条件,然后构建一个交互的环境,在交互环境中划分会话,在会话中与数据库进行交互数据。

1 MyBatis主要的类

  • Configuration        MyBatis所有的配置信息都维持在Configuration对象之中。
  • SqlSession            作为MyBatis工作的主要顶层API,表示和数据库交互的会话,完成必要数据库增删改查功能
  • Executor               MyBatis执行器,是MyBatis 调度的核心,负责SQL语句的生成和查询缓存的维护
  • StatementHandler 封装了JDBC Statement操作,负责对JDBC statement 的操作,如设置参数、将Statement结果集转换成List集合。
  • ParameterHandler  负责对用户传递的参数转换成JDBC Statement 所需要的参数,
  • ResultSetHandler   负责将JDBC返回的ResultSet结果集对象转换成List类型的集合;
  • TypeHandler          负责java数据类型和jdbc数据类型之间的映射和转换
  • MappedStatement  MappedStatement维护了一条<select|update|delete|insert>节点的封装,
  • SqlSource              负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封装到BoundSql对象中,并返回
  • BoundSql              表示动态生成的SQL语句以及相应的参数信息

  以上几个类在SQL操作中都会涉及,在SQL操作中重点关注下SQL参数什么时候写入结果集怎么转换为Java对象,这两个过程正好对应的类是PreparedStatementHandlerResultSetHandler类。

2 SQL执行流程

MyBatis主要设计目的还是为了让我们在执行SQL时,对输入输出的数据的管理更加方便,所以方便的让我们写出SQL方便的获取SQL的执行结果是MyBatis的核心竞争力。

下面就用一个例子来从源码角度看一下SQL的完整执行流程。

新建配置文件conf.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>

    <settings>
        <setting name="cacheEnabled" value="true"/>
        <setting name="lazyLoadingEnabled" value="false"/>
        <!--setting name="logImpl" value="STDOUT_LOGGING"/--> <!-- 日志 -->
    </settings>

    <typeAliases>
        <typeAlias type="com.luoxn28.dao.User" alias="User"/>
    </typeAliases>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC" /> <!-- 声明使用那种事务管理机制 JDBC/MANAGED -->
            <!-- 配置数据库连接信息 -->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver" />
                <property name="url" value="jdbc:mysql://192.168.1.150:3306/xxx" />
                <property name="username" value="xxx" />
                <property name="password" value="xxx" />
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper resource="userMapper.xml"/>
    </mappers>

</configuration>

首先建立数据表,这里就以user表为例 :

DROP TABLE IF EXISTS user;
CREATE TABLE user (
  id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
  name VARCHAR(32) NOT NULL,
  password VARCHAR(32) NOT NULL,
  sex int,
  email VARCHAR(32),
  phone VARCHAR(16),
  admin VARCHAR(16)
);

然后新建与数据表对应的类User:

/**
 * User - 用户类
 */
public class User {

    public static final int MAN  = 0;   // 男生
    public static final int WOMAN = 1;  // 女生
    public static final int OTHER = 2;  // 其他

    private int id;             // 用户id
    private String name;        // 用户名
    private String password;    // 用户密码
    private int sex;            // 用户性别
    private String email;       // 用户邮箱
    private String phone;       // 用户手机
    private String admin;       // 用户是否是管理员,"admin"表示是管理员,其他为普通用户

    public User() { }

    public User(String name, String password, int sex, String email, String phone) {
        this.name = name;
        this.password = password;
        this.sex = sex;
        this.email = email;
        this.phone = phone;
        this.admin = "";
    }
    public User(String name, String password, String sex, String email, String phone) {
        this.name = name;
        this.password = password;
        setSex(sex); // this.sex = sex;
        this.email = email;
        this.phone = phone;
        this.admin = "";
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public int getSex() {
        return sex;
    }

    public void setSex(int sex) {
        this.sex = sex;
    }

    public void setSex(String sexStr) {
        int sex = Integer.valueOf(sexStr);
        switch (Integer.valueOf(sexStr)) {
            case 0: {
                this.sex = MAN;
                break;
            }
            case 1: {
                this.sex = WOMAN;
                break;
            }
            default: {
                this.sex = OTHER;
                break;
            }
        }
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public String getAdmin() {
        return admin;
    }

    public void setAdmin(String admin) {
        this.admin = admin;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name=‘" + name + ‘\‘‘ +
                ", password=‘" + password + ‘\‘‘ +
                ", sex=" + sex +
                ", email=‘" + email + ‘\‘‘ +
                ", phone=‘" + phone + ‘\‘‘ +
                ", admin=‘" + admin + ‘\‘‘ +
                ‘}‘;
    }

}

再新建usre表的配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.luoxn28.dao.UserDao">

    <select id="getById" parameterType="int" resultType="User">
        SELECT * FROM user WHERE id=#{id}; <!-- #{xxx} xxx为类中的数据域名称 -->
    </select>

    <select id="getAll" resultType="com.luoxn28.dao.User">
        SELECT * FROM user;
    </select>
</mapper>

最后新建测试类:

/**
 * MyBatis测试类
 */
public class TestMain {

    public static void main(String[] args) throws IOException {
        String resouce = "conf.xml";
        InputStream is = Resources.getResourceAsStream(resouce);

        // 构建sqlSession工厂
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
                // 获取sqlSession
        SqlSession session = sqlSessionFactory.openSession();

        User user;

        try {
            /**
             * 第一种方式: 直接执行已映射的 SQL 语句
             */
            String statement = "com.luoxn28.dao.UserDao.getById";
            user = session.selectOne(statement, 1);
            System.out.println(user);
        }
        finally {
            session.close();
        }

        /**
         * 第二种方式: 执行更清晰和类型安全的代码
         */
//        UserDao userDao = session.getMapper(UserDao.class);
//        user = userDao.getById(1);
//        System.out.println(user);
    }

}

  由于我们分析的是SQL的执行流程,那就重点关注下 user = session.selectOne(statement, 1); 这行代码~ 注意,传进去的参数是1。

  session是DefaultSqlSession类型的,因为sqlSessionFactory默认生成的SqlSession是DefaultSqlSession类型。selectOne()会调用selectList()。

// DefaultSqlSession类
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
        MappedStatement ms = configuration.getMappedStatement(statement);
        // CURD操作是交给Excetor去处理的
        return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
    } catch (Exception e) {
        throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
        ErrorContext.instance().reset();
    }
}

  在DefaultSqlSession.selectList中的各种CURD操作都是通多Executor进行的,这里executor的类型是CachingExecutor,接着跳转到其中的query方法中。

// CachingExecutor 类
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    BoundSql boundSql = ms.getBoundSql(parameterObject);  // 获取绑定的sql命令,比如"SELECT * FROM xxx"
    CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
    return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

  getBoundSql为了获取绑定的sql命令,在创建完cacheKey之后,就进入到CachingExecutor 类中的另一个query方法中。

// CachingExecutor 类
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
        throws SQLException {
    Cache cache = ms.getCache();
    if (cache != null) {
        flushCacheIfRequired(ms);
        if (ms.isUseCache() && resultHandler == null) {
            ensureNoOutParams(ms, parameterObject, boundSql);
            @SuppressWarnings("unchecked")
            List<E> list = (List<E>) tcm.getObject(cache, key);
            if (list == null) {
                list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
                tcm.putObject(cache, key, list); // issue #578 and #116
            }
            return list;
        }
    }
    return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

  这里真正执行query操作的是SimplyExecutor代理来完成的,接着就进入到了SimplyExecutor的父类BaseExecutor的query方法中。

// SimplyExecutor的父类BaseExecutor类
@SuppressWarnings("unchecked")
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
    if (closed) {
        throw new ExecutorException("Executor was closed.");
    }
    if (queryStack == 0 && ms.isFlushCacheRequired()) {
        clearLocalCache();
    }
    List<E> list;
    try {
        queryStack++;
        /**
         * localCache是一级缓存,如果找不到就调用queryFromDatabase从数据库中查找
         */
        list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
        if (list != null) {
            handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
        } else {
            list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
        }
    } finally {
        queryStack--;
    }
    if (queryStack == 0) {
        for (DeferredLoad deferredLoad : deferredLoads) {
            deferredLoad.load();
        }
        // issue #601
        deferredLoads.clear();
        if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
            // issue #482
            clearLocalCache();
        }
    }
    return list;
}

  因为是第一次SQL查询操作,所以会调用queryFromDatabase方法来执行查询。

// SimplyExecutor的父类BaseExecutor类
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    List<E> list;
    localCache.putObject(key, EXECUTION_PLACEHOLDER);
    try {
        list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
    } finally {
        localCache.removeObject(key);
    }
    localCache.putObject(key, list);
    if (ms.getStatementType() == StatementType.CALLABLE) {
        localOutputParameterCache.putObject(key, parameter);
    }
    return list;
}

  从数据库中查询数据,进入到SimplyExecutor中进行操作。

// SimplyExecutor类
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
        Configuration configuration = ms.getConfiguration();
        StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
        // 子流程1:SQL查询参数的设置
        stmt = prepareStatement(handler, ms.getStatementLog());
        // StatementHandler封装了Statement
        // 子流程2:SQL查询操作和结果集的封装
        return handler.<E>query(stmt);
    } finally {
        closeStatement(stmt);
    }
}

  注意,在prepareStatement方法中会进行SQL查询参数的设置,也就是咱们最开始传递进来的参数,其值为1。handler.<E>query(stmt)方法中会进行实际的SQL查询操作和结果集的封装(封装成Java对象)。当流程走到这里时,程序已经压栈有一定深度了,因为接下来程序分析会兵分两路,一方面深入到SQL查询及结果集的设置子流程1中,然后再深入到SQL查询操作和结果集的封装子流程2,因为还会回到这里,所以就来一张调用栈的特写吧:

子流程1:SQL查询参数的设置

// SimplyExecutor类
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    // 获取一个Connection
    Connection connection = getConnection(statementLog);
    stmt = handler.prepare(connection, transaction.getTimeout());
    handler.parameterize(stmt); // 设置SQL查询中的参数值
    return stmt;
}

  通过getConnection方法来获取一个Connection,调用prepare方法来获取一个Statement(这里的handler类型是RoutingStatementHandler,RoutingStatementHandler的prepare方法调用的是PrepareStatementHandler的prepare方法,因为PrepareStatementHandler并没有覆盖其父类的prepare方法,其实最后调用的是BaseStatementHandler中的prepare方法。是不是绕晕了,那就再看一遍吧 :) )。调用parameterize方法来设置SQL的参数值(这里最后调用的是PrepareStatementHandler中的parameterize方法,而PrepareStatementHandler.parameterize方法调用的是DefaultParameterHandler中的setParameters方法)。

// PrepareStatementHandler类
@Override
public void parameterize(Statement statement) throws SQLException {
    parameterHandler.setParameters((PreparedStatement) statement);
}
// DefaultParameterHandler类
@Override
public void setParameters(PreparedStatement ps) {
    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings != null) {
        for (int i = 0; i < parameterMappings.size(); i++) {
            ParameterMapping parameterMapping = parameterMappings.get(i);
            if (parameterMapping.getMode() != ParameterMode.OUT) {
                Object value;
                String propertyName = parameterMapping.getProperty();
                if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
                    value = boundSql.getAdditionalParameter(propertyName);
                } else if (parameterObject == null) {
                    value = null;
                } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                    value = parameterObject;
                } else {
                    MetaObject metaObject = configuration.newMetaObject(parameterObject);
                    value = metaObject.getValue(propertyName);
                }
                TypeHandler typeHandler = parameterMapping.getTypeHandler();
                JdbcType jdbcType = parameterMapping.getJdbcType();
                if (value == null && jdbcType == null) {
                    jdbcType = configuration.getJdbcTypeForNull();
                }
                try {
                    typeHandler.setParameter(ps, i + 1, value, jdbcType);
                } catch (TypeException e) {
                    throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
                } catch (SQLException e) {
                    throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
                }
            }
        }
    }
}

  到这里为止,已经给Statement设置了最初传递进去的参数(值为1)了,那么接着分析流程2:

流程2:SQL查询及结果集的设置

// RoutingStatementHandler类
@Override
public <E> List<E> query(Statement statement) throws SQLException {
    return delegate.<E>query(statement);
}
// RoutingStatementHandler类
@Override
public <E> List<E> query(Statement statement) throws SQLException {
    // 这里就到了熟悉的PreparedStatement了
    PreparedStatement ps = (PreparedStatement) statement;
    // 执行SQL查询操作
    ps.execute();
    // 结果交给ResultHandler来处理
    return resultSetHandler.<E> handleResultSets(ps);
}
// DefaultResultSetHandler类(封装返回值,将查询结果封装成Object对象)
@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
    ErrorContext.instance().activity("handling results").object(mappedStatement.getId());

    final List<Object> multipleResults = new ArrayList<Object>();

    int resultSetCount = 0;
    ResultSetWrapper rsw = getFirstResultSet(stmt);

    List<ResultMap> resultMaps = mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size();
    validateResultMapsCount(rsw, resultMapCount);
    while (rsw != null && resultMapCount > resultSetCount) {
        ResultMap resultMap = resultMaps.get(resultSetCount);
        handleResultSet(rsw, resultMap, multipleResults, null);
        rsw = getNextResultSet(stmt);
        cleanUpAfterHandlingResultSet();
        resultSetCount++;
    }

    String[] resultSets = mappedStatement.getResultSets();
    if (resultSets != null) {
        while (rsw != null && resultSetCount < resultSets.length) {
            ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
            if (parentMapping != null) {
                String nestedResultMapId = parentMapping.getNestedResultMapId();
                ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
                handleResultSet(rsw, resultMap, null, parentMapping);
            }
            rsw = getNextResultSet(stmt);
            cleanUpAfterHandlingResultSet();
            resultSetCount++;
        }
    }

    return collapseSingleResultList(multipleResults);
}

  ResultSetWrapper是ResultSet的包装类,调用getFirstResultSet方法获取第一个ResultSet,同时获取数据库的MetaData数据,包括数据表列名、列的类型、类序号等,这些信息都存储在ResultSetWrapper类中了。然后调用handleResultSet方法来来进行结果集的封装。

// DefaultResultSetHandler类
private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
    try {
        if (parentMapping != null) {
            handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
        } else {
            if (resultHandler == null) {
                DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
                handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
                multipleResults.add(defaultResultHandler.getResultList());
            } else {
                handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
            }
        }
    } finally {
        // issue #228 (close resultsets)
        closeResultSet(rsw.getResultSet());
    }
}

  这里调用handleRowValues方法来进行值的设置:

// DefaultResultSetHandler类
public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
    if (resultMap.hasNestedResultMaps()) {
        ensureNoRowBounds();
        checkResultHandler();
        handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
    } else {
        // 封装数据
        handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
    }
}
// DefaultResultSetHandler类
// 封装数据
private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
        throws SQLException {
    DefaultResultContext<Object> resultContext = new DefaultResultContext<Object>();
    skipRows(rsw.getResultSet(), rowBounds);
    while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {
        ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);
        Object rowValue = getRowValue(rsw, discriminatedResultMap);
        storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
    }
}
// DefaultResultSetHandler类
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
    final ResultLoaderMap lazyLoader = new ResultLoaderMap();
    // createResultObject为新创建的对象,数据表对应的类
    Object resultObject = createResultObject(rsw, resultMap, lazyLoader, null);
    if (resultObject != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
        final MetaObject metaObject = configuration.newMetaObject(resultObject);
        boolean foundValues = !resultMap.getConstructorResultMappings().isEmpty();
        if (shouldApplyAutomaticMappings(resultMap, false)) {
            // 这里把数据填充进去,metaObject中包含了resultObject信息
            foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;
        }
        foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
        foundValues = lazyLoader.size() > 0 || foundValues;
        resultObject = foundValues ? resultObject : null;
        return resultObject;
    }
    return resultObject;
}
// DefaultResultSetHandler类(把ResultSet中查询结果填充到JavaBean中)
private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
    List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
    boolean foundValues = false;
    if (autoMapping.size() > 0) {
                // 这里进行for循环调用,因为user表中总共有7项,所以也就调用7次
        for (UnMappedColumnAutoMapping mapping : autoMapping) {
            // 这里将esultSet中查询结果转换为对应的实际类型
            final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);
            if (value != null) {
                foundValues = true;
            }
            if (value != null || (configuration.isCallSettersOnNulls() && !mapping.primitive)) {
                // gcode issue #377, call setter on nulls (value is not ‘found‘)
                metaObject.setValue(mapping.property, value);
            }
        }
    }
    return foundValues;
}

  mapping.typeHandler.getResult会获取查询结果值的实际类型,比如我们user表中id字段为int类型,那么它就对应Java中的Integer类型,

然后通过调用statement.getInt("id")来获取其int值,其类型为Integer。metaObject.setValue方法会把获取到的Integer值设置到Java类中的对应字段。

// MetaObject类
public void setValue(String name, Object value) {
    PropertyTokenizer prop = new PropertyTokenizer(name);
    if (prop.hasNext()) {
        MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
        if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
            if (value == null && prop.getChildren() != null) {
                // don‘t instantiate child path if value is null
                return;
            } else {
                metaValue = objectWrapper.instantiatePropertyValue(name, prop, objectFactory);
            }
        }
        metaValue.setValue(prop.getChildren(), value);
    } else {
        objectWrapper.set(prop, value);
    }
}

  metaValue.setValue方法最后会调用到Java类中对应数据域的set方法,这样也就完成了SQL查询结果集的Java类封装过程。最后贴一张调用栈到达Java类的set方法中的快照:

参考:

  1、MyBatis源码

  2、《深入分析Java Web技术内幕》的iBatis章节

  3、《深入理解mybatis原理》 MyBatis的架构设计以及实例分析

  4、luoxn28/tuiku

  5、Java JDBC基础学习小结

时间: 2024-10-06 09:05:18

深入理解java:4.3.1. 框架编程之MyBatis---SQL语句执行的完整流程的相关文章

深入理解java:4.2. 框架编程之Spring框架的设计理念

什么是Spring呢? Spring是为了解决企业应用开发的复杂性而创建的一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架. Spring优点 简单了解Spring之后,我们看一下Spring的优点. 1.方便解耦,简化开发. 2.AOP编程的支持. 3.声明式事务的支持. 4.方便程序的测试.. 5.方便集成各种优秀框架. 6.降低Java EE API的使用难度. Spring的骨骼架构 Spring总共有十几个组件,但是真正核心组件只有三个:Core.Context和Beans

深入理解java:4.1. 框架编程之Spring MVC

Spring MVC是当前最优秀的MVC框架,自从Spring 2.5版本发布后,由于支持注解配置,易用性有了大幅度的提高. Spring 3.0更加完善,实现了对Struts 2的超越.现在越来越多的开发团队选择了Spring MVC. Struts2也是非常优秀的MVC构架,优点非常多比如良好的结构,拦截器的思想,丰富的功能. 但这里想说的是缺点,Struts2由于采用了值栈.OGNL表达式.struts2标签库等,会导致应用的性能下降,应避免使用这些功能. Spring3 MVC的优点:

shell编程之case分支语句

shell编程之case分支语句 case分支语句和if的多分支语句很相似. if多分支语句一般用在有(区间范围)的地方 :例如:0-100之间. if需要判断多个不同的条件. case的分支语句用在 如果里面匹配到固定值:或某个变量存在多中取值,需要对其中的每一个取值分别执行不同的命令序列. case只是判断一个变量的不同取值. case的模式: 执行流程: 首先使用"变量值"与模式1进行比较,如果取值相同就执行模式1的命令序列1,直到遇见" ;; "后跳转到es

java大型企业级互联网框架平台 SpringMVC Mybatis Shiro Bootstrap

开发快报: 页面打印功能,websocket 强制下线功能,玩转websocket技术  [金牌]获取[下载地址]   QQ: 313596790A 代码生成器(开发利器);     增删改查的处理类,service层,mybatis的xml,SQL( mysql   和oracle)脚本,   jsp页面 都生成   就不用写搬砖的代码了,生成的放到项目里,可以直接运行B 阿里巴巴数据库连接池druid;  数据库连接池  阿里巴巴的 druid.Druid在监控.可扩展性.稳定性和性能方面都

Java注解项目实战即模拟Hibenernate生成sql语句

整理了近期学习java注解的部分代码 ,借助java注解模拟Hibenernate ORM模型建立对象与sql语句的映射 Table 注解的创建 package com.imooc.test; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.

JDBC之java数据库的连接与简单的sql语句执行

import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.Statement; public class Test { /** * @param args */ public static void main(String[] args) { /*这部分是用java驱动连接oracle数据库,驱动的jar可以自己在网上下载,也可以给我留言,发给你*/

在hibernate框架中配置显示sql语句

使用Hibernate的框架开发时,可在Hibernate.cfg.xml中加上 <property name="hibernate.show_sql">true</property><!-- 配置显示sql语句 -->  <property name="format_sql">true</property><!-- 让输出的sql语句格式化 --> 就可以在控制台显示对应的sql语句了,对于开

深入理解java合集之框架总览

综述 Java集合就是一个容器.面向对象语言对事物的体现都是以对象的形式存在,所以为了方便对多个对象的操作,就对对象进行存储,集合就是存储对象最常用的一种方式.集合只用于存储对象,集合长度是可变的,集合可以存储不同类型的对象.如果往集合里存放基本数据类型,在存取过程中会有个自动装箱和拆箱. 因为容器中数据结构不同,容器有很多种.不断地将共性功能向上抽取,形成了集合体系,称之为集合框架. 集合框架的顶层就称之为Collection接口.所有的集合类都位于java.util包下,查阅API可以得到如

MyBatis框架在控制台打印Sql语句-遁地龙卷风

(-1)写在前面 我用的是MyBatis 3.2.4,Maven Project (0)mybatis-config.xml <settings> <setting name="logImpl" value="LOG4J"/> </settings> (1)log4j.properties 内容 log4j.rootLogger=DEBUG, Console log4j.appender.Console=org.apache.lo