目录
- MyBatis----03
- 1. MyBatis原理
- 测试代码,获得接口实现:
- 调用DefaultSqlSession中的getMapper方法:
- 调用Configuration中的getMapperfangfa:
- 调用MapperRegister总的getMapper方法:
- 调用MapperProxyFactory中的newInstance方法:
- Proxy.newProxyInstance方法的第三个参数:
- 查看invoke方法:
- 调用MapperPtoxy中的invoke方法:
- 调用MapperMethod的execute方法:
- 执行MapperMethod中的executuForMany方法:
- 调用DefaultSqlSession的selectList方法:
- 如果没有缓存存在,会调用CachingExecutor中的query方法查询:
- 调用CachingExecuter中的query方法:
- 进入到baseExecutor中执行query方法:
- BaseExecutor中的queryDataBase方法:
- 调用SimpleExecutor中的doQuery方法:
- 进入RountingStatementHandler中的query方法:
- 进入PreparedStatementHandler中的query方法:
- 进入DefaultResultSetHandler中的handlerResultSets封装查询结果:
- 至此,MyBatis源码观看完毕,结论:
- 2. 如何获得保存后的主键值
- 3. 动态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>