MyBatis----03----MyBatis原理&&动态SQL

MyBatis----03

1. MyBatis原理

MyBatis底层使用的是动态代理帮我们创建实现接口的实现类。

测试代码,获得接口实现:

        //获得UserMapper接口的实现类
        UserMapper mapper = session.getMapper(UserMapper.class);
        //调用UserMapper方法
		List<User> list = mapper.findAll();

调用DefaultSqlSession中的getMapper方法:

@Override
public <T> T getMapper(Class<T> type) {
        return this.configuration.getMapper(type, this);
    }

调用Configuration中的getMapperfangfa:

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        return this.mapperRegistry.getMapper(type, sqlSession);
    }

调用MapperRegister总的getMapper方法:

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
        if (mapperProxyFactory == null) {
            throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
        } else {
            try {
            	//创建代理对象
                return mapperProxyFactory.newInstance(sqlSession);
            } catch (Exception var5) {
                throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
            }
        }
    }

调用MapperProxyFactory中的newInstance方法:

@Override
protected T newInstance(MapperProxy<T> mapperProxy) {
		//清晰的看到,使用动态代理技术,创建了接口的实现类
        return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), 
        new Class[]{this.mapperInterface}, 
        mapperProxy);
    }

Proxy.newProxyInstance方法的第三个参数:

//实现了InvocationHandler接口
public class MapperProxy<T> implements InvocationHandler, Serializable{}

查看invoke方法:

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws
Throwable {
	try {
		if (Object.class.equals(method.getDeclaringClass())) {
		//调用的是Object中继承方法(例如:toString) -> 直接执行
		return method.invoke(this, args);
		} else {
		//调用Mapper接口中声明的方法,需要进一步处理
		return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
		}
	} catch (Throwable t) {
	  throw ExceptionUtil.unwrapThrowable(t);
	 }
 }

调用MapperPtoxy中的invoke方法:

@Override
public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
		return mapperMethod.execute(sqlSession, args);
}

调用MapperMethod的execute方法:

public Object execute(SqlSession sqlSession, Object[] args) {
	Object result;
	//分析判断要执行的sql类型
	switch (command.getType()) {
		case INSERT: {
			Object param = method.convertArgsToSqlCommandParam(args);
			result = rowCountResult(sqlSession.insert(command.getName(), param));
			break;
		}
		case UPDATE: {
		Object param = method.convertArgsToSqlCommandParam(args);
		result = rowCountResult(sqlSession.update(command.getName(), param));
		break;
		}
		case DELETE: {
			Object param = method.convertArgsToSqlCommandParam(args);
			result = rowCountResult(sqlSession.delete(command.getName(), param));
			break;
		}
		//查询
		case SELECT:
			if (method.returnsVoid() && method.hasResultHandler()) {
			executeWithResultHandler(sqlSession, args);
			result = null;
			} else if (method.returnsMany()) {
			//查询返回结果是多个
			result = executeForMany(sqlSession, args);
			} else if (method.returnsMap()) {
				result = executeForMap(sqlSession, args);
			} else if (method.returnsCursor()) {
				result = executeForCursor(sqlSession, args);
			} else {
				Object param = method.convertArgsToSqlCommandParam(args);
				result = sqlSession.selectOne(command.getName(), param);
			if (method.returnsOptional()
				&& (result == null ||
		!method.getReturnType().equals(result.getClass()))) {
				result = Optional.ofNullable(result);
			}
		  }
		  break;
		case FLUSH:
			result = sqlSession.flushStatements();
			break;
		default:
			throw new BindingException("Unknown execution method for: " +
			command.getName());
		}
		if (result == null && method.getReturnType().isPrimitive() &&
		!method.returnsVoid()) {
			throw new BindingException("Mapper method '" + command.getName()
					+ " attempted to return null from a method with a primitive return type
		(" + method.getReturnType() + ").");
			}
			return result;
		}

执行MapperMethod中的executuForMany方法:

@Override
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
	List<E> result;
	Object param = method.convertArgsToSqlCommandParam(args);
	if (method.hasRowBounds()) {
		RowBounds rowBounds = method.extractRowBounds(args);
		//查询
		result = sqlSession.selectList(command.getName(), param, rowBounds);
	} else {
		//查询
		result = sqlSession.selectList(command.getName(), param);
	}
	// issue #510 Collections & arrays support
	if (!method.getReturnType().isAssignableFrom(result.getClass())) {
		if (method.getReturnType().isArray()) {
			return convertToArray(result);
		} else {
			return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
		}
	}
	return result;
}

调用DefaultSqlSession的selectList方法:

@Override
public <E> List<E> selectList(String statement, Object parameter) {
	return this.selectList(statement, parameter, RowBounds.DEFAULT);
}
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds
rowBounds) {
	try {
		MappedStatement ms = configuration.getMappedStatement(statement);
		//调用执行器对象的query方法,进行查询
		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();
	}
}

如果没有缓存存在,会调用CachingExecutor中的query方法查询:

@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds
rowBounds, ResultHandler resultHandler) throws SQLException {
	//从XML中加载sql语句
	BoundSql boundSql = ms.getBoundSql(parameter);
	CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
	//执行查询
	return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}

调用CachingExecuter中的query方法:

@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, boundSql);
				@SuppressWarnings("unchecked")
				//获得缓存中是否存在查询结果
				List<E> list = (List<E>) tcm.getObject(cache, key);
				if (list == null) {
					//如果缓存中没有,就去jdbc中查询
					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);
	}

进入到baseExecutor中执行query方法:

@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++;
			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;
	}

BaseExecutor中的queryDataBase方法:

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;
}

调用SimpleExecutor中的doQuery方法:

@Override
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);
		stmt = prepareStatement(handler, ms.getStatementLog());
		//执行查询
		return handler.query(stmt, resultHandler);
	} finally {
	closeStatement(stmt);
	}
}

进入RountingStatementHandler中的query方法:

@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler)
throws SQLException {
	return delegate.query(statement, resultHandler);
}

进入PreparedStatementHandler中的query方法:

@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler)
throws SQLException {
	PreparedStatement ps = (PreparedStatement) statement;
	//jdbc中的PrepareStatement的execute方法 -> 执行sql
	ps.execute();
	//封装并返回结果
	return resultSetHandler.handleResultSets(ps);
}

进入DefaultResultSetHandler中的handlerResultSets封装查询结果:

@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
	ErrorContext.instance().activity("handling
results").object(mappedStatement.getId());

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

	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);
}

至此,MyBatis源码观看完毕,结论:

  • MyBatis底层就是使用了动态代理技术帮我们创建Mapper接口的实现类;
  • 查询数据库,MyBatis底层用的是JDBC;
  • 封装查询结果使用反射技术。

2. 如何获得保存后的主键值

2.1 使用useGenerateKeys属性

 <!--
        useGenerateKeys:声明需要返回生成的主键值
        keyProperty:返回主键值到对象的那个属性中
    -->
    <insert id="save2" parameterType="User" useGeneratedKeys="true" keyProperty="id">
    insert into t_user(u_name,u_password) values(#{name},#{password})
    </insert>

2.2 使用selectKey元素

    <insert id="save3" parameterType="User" >
    <!-- selectKey元素:需要返回指定sql语句的查询结果
            keyColumn:指定主键列列名
            keyProperty:指定主键对应的属性名
            order:指定select LAST_INSERT_ID();语句在insert语句之前还是之后执行
                    BEFORE:之前
                    AFTER:之后
            resultType:主键值类型
     -->
    <selectKey keyColumn="u_id" keyProperty="id" order="AFTER" resultType="int" >
        select LAST_INSERT_ID() ;
    </selectKey>
    insert into t_user(u_name,u_password) values(#{name},#{password})
    </insert>

3. 动态SQL

动态SQL使用场景:SQL语句内容不固定,而是根据运行实际传入的参数发生改变,
例如:做一个带有多个查询条件的列表页面,如果一个条件都没有:执行无条件的查询;如果使用了n个条件:执行带有n个条件的sql查询。

# 用户输入的条件是id:
SELECT * FROM t_user WHERE u_id = ?
#用户输入的条件是name:
SELECT * FROM t_user WHERE u_name LIKE ?
#用户输入的条件是什么都没有:
SELECT * FROM t_user 
#用户输入的条件是id和name:
SELECT * FROM t_user WHERE u_id = ? AND u_name LIKE ?

3.1 使用where元素配合if,可拼接多个条件

   <select id="findByIdAndName" resultMap="userMapper" parameterType="User">
        select * from t_user 
        <where>
            <!-- 如果id属性不为空,那么标签体中的条件 -->
            <if test="id!=null and id!=''">
                and u_id = #{id}
            </if>
            <if test="name!=null and name!=''">
                and u_name like #{name}
            </if>
        </where>
    </select>

3.2 使用where元素配合choose,只拼接一个条件

    <select id="findByIdAndName2" resultMap="userMapper" parameterType="User">
        select * from t_user
        <where>
            <choose>
                <!--判断条件是否成立 -->
                <when test="id!=null and id!=''">
                    and u_id = #{id}
                </when>
                <when test="name!=null and name!=''">
                    and u_name like #{name}
                </when>
                <!-- 上面的when都不成立,只拼接该元素(选填) -->
                <otherwise>
                    and 1=1
                </otherwise>
            </choose>
        </where>
    </select>

3.3 使用where元素配合foreach实现in|not in查询

3.3.1 针对数组

UserMapper;

//根据多个id查找User对象
    List<User> findByIdArray(@Param("ids") int[] id);

UserMapper.xml:

 <select id="findByIdArray" resultMap="userMapper">
        select * from t_user
        <where>
            <!-- collection:指定要遍历的(数组|集合)对象
                    item:为当前遍历的元素指定一个索引
                    open:条件左半部分
                    close:条件右半部分
                    separator:每个元素的分隔符
                    id in (1,2,3)
            -->
            <foreach collection="ids" item="id" open="u_id in(" separator="," close=")">
                #{id}
            </foreach>
        </where>
    </select>

3.3.2 针对集合(set与list同理)

UserMapper:

 List<User> findByIdList(@Param("ids") List<Integer> ids);

UserMapper.xml:

    <select id="findByIdList" resultMap="userMapper">
        select * from t_user
        <where>
            <!-- collection:指定要遍历的(数组|集合)对象
                    item:为当前遍历的元素指定一个索引
                    open:条件左半部分
                    close:条件右半部分
                    separator:每个元素的分隔符
                    id in (1,2,3)
            -->
            <foreach collection="ids" item="id" open="u_id in(" separator="," close=")">
                #{id}
            </foreach>
        </where>
    </select>

3.4 SQL片段

抽取经常重复出现的sql:

    <!-- 抽取xml中出现频率高的sql -->
    <sql id="baseQuery">
        select * from t_user
    </sql>

在其它配置中引用该sql:

    <select id="findByIdList" resultMap="userMapper">
        <!-- 引用抽取出来的sql -->
        <include refid="baseQuery"></include>
        <where>
            <!-- collection:指定要遍历的(数组|集合)对象
                    item:为当前遍历的元素指定一个索引
                    open:条件左半部分
                    close:条件右半部分
                    separator:每个元素的分隔符
                    id in (1,2,3)
            -->
            <foreach collection="ids" item="id" open="u_id in(" separator="," close=")">
                #{id}
            </foreach>
        </where>
    </select>

猜你喜欢

转载自blog.csdn.net/weixin_45245455/article/details/107976513