mybatis 数组_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维护了一条节点的封装,
  • SqlSource 负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封装到BoundSql对象中,并返回
  • BoundSql 表示动态生成的SQL语句以及相应的参数信息

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

1a42834adb2c51cd6accea797b20c479.png

2 SQL执行流程

 MyBatis主要设计目的还是为了让我们在执行SQL时对输入输出的数据的管理更加方便,所以方便的让我们写出SQL和方便的获取SQL的执行结果是MyBatis的核心竞争力。下面就用一个例子来从源码角度看一下SQL的完整执行流程。

新建配置文件conf.xml:

<?xml version="1.0" encoding="UTF-8"?>

首先建立数据表,这里就以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"?>        SELECT * FROM user WHERE id=#{id};             SELECT * FROM user;    

最后新建测试类:

/** * 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  List 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  List 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 类@Overridepublic  List 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 list = (List) tcm.getObject(cache, key);            if (list == null) {                list = delegate. query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);                tcm.putObject(cache, key, list); // issue #578 and #116            }            return list;        }    }    return delegate. query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);}

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

// SimplyExecutor的父类BaseExecutor类@SuppressWarnings("unchecked")@Overridepublic  List 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 list;    try {        queryStack++;        /**         * localCache是一级缓存,如果找不到就调用queryFromDatabase从数据库中查找         */        list = resultHandler == null ? (List) 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  List queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {    List 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  List 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.query(stmt);    } finally {        closeStatement(stmt);    }}

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

5339da0f29eb4a9649ff67fa647e3edf.png

子流程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类@Overridepublic void parameterize(Statement statement) throws SQLException {    parameterHandler.setParameters((PreparedStatement) statement);}
// DefaultParameterHandler类@Overridepublic void setParameters(PreparedStatement ps) {    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());    List 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类@Overridepublic  List query(Statement statement) throws SQLException {    return delegate.query(statement);}
// RoutingStatementHandler类@Overridepublic  List query(Statement statement) throws SQLException {    // 这里就到了熟悉的PreparedStatement了    PreparedStatement ps = (PreparedStatement) statement;    // 执行SQL查询操作    ps.execute();    // 结果交给ResultHandler来处理    return resultSetHandler. handleResultSets(ps);}
// DefaultResultSetHandler类(封装返回值,将查询结果封装成Object对象)@Overridepublic List handleResultSets(Statement stmt) throws SQLException {    ErrorContext.instance().activity("handling results").object(mappedStatement.getId());    final List multipleResults = new ArrayList();    int resultSetCount = 0;    ResultSetWrapper rsw = getFirstResultSet(stmt);    List 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 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 resultContext = new DefaultResultContext();    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 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方法中的快照:

ad9a8fd0d04fbacc18e15a9341201e20.png

喜欢这篇文章可以关注我 私信 “资料” 即可获得更多学习视频和资料:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值