言接上篇,我说过的我认为的三部分
- 数据源
- sql语句
- 执行sql语句
上篇验证了数据源的一个连接,接下来就需要探究sql语句究竟是如何获得的。
熟悉mybatis的都知道sql的映射有两种方式,第一种是通过注解,第二种是通过xml文件。
实际上xml用的比较多,而且mybatis官方提倡使用xml文件
关于xml里的映射就是通过一个命名空间的绑定,通过全限名来进行一个绑定。类似于一个接口和接口的实现,然后注释就是在注释上进行一个接口的实现。两种是类似的。
但是我所说的如何获得sql语句是只框架本身。
我们得到了一个sqlseeionfactory 然后就要通过opensession获取到一个sqlsession
@Override
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
final Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
可以看到new DefaultSqlSession(configuration, executor, autoCommit); 把之前配置好的configuration和executor, autoCommit放进构造里,直接得到一个DefaultSqlSession 也就是我们会用到的sqlsession executor, autoCommit executor这个是什么东西,我们暂时不看 autoCommit 大概就是一个设置自动提交的东西把。
然后继续走,我们得到了一个sqlSeesion 按照官方文档给的教程。我们可以通过sqlsession执行selectone 或者是其他方法,这只是一种方式,更为推荐的是先getmapper(class),然后通过这个mapper去调用我们自己所定义好的接口,我们知道这个接口里的方法绑定着在mapper里写的sql语句。 所以这个getmapper方法他就是获得sql的关键 点进方法里我发现有个调用的顺序
首先是configuration.getMapper
@Override
public <T> T getMapper(Class<T> type) {
return configuration.getMapper(type, this);
}
然后是
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
然后进入到一个很关键的东西 我看到了proxy 这可是java的动态代理哎
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
显然 mapperProxyFactory.newInstance(sqlSession); 获得了一个mapperProxy的代理类实例 继续往下
@Override
public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
return mapperMethod.execute(sqlSession, args);
}
看到这个execute方法
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
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;
}
看到在select的区域依然调用了selectone
所以说selectone和自定义selecy方法在本质上是相同的。底层是一样的 回到了之前两种方法的调用上。
点进selectone
@Override
public <T> T selectOne(String statement, Object parameter) {
// Popular vote was to return null on 0 results and throw exception on too many.
List<T> list = this.selectList(statement, parameter);
if (list.size() == 1) {
return list.get(0);
} else if (list.size() > 1) {
throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
} else {
return null;
}
}
看到selectList深入深入再深入
private <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
try {
MappedStatement ms = configuration.getMappedStatement(statement);
return executor.query(ms, wrapCollection(parameter), rowBounds, handler);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
本质上是调用了一个executor.query() baseexecutor的query方法
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameter);
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
这是什么 出现了 cachekey,熟悉mybatis都知道是有缓存的这个命名 看到之后就会联想到缓存啊。
先不说这个,之前是有一个getBoundSql的啊 ,这个命名就感觉自己有了胜利的曙光
public BoundSql getBoundSql(Object parameterObject) {
BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings == null || parameterMappings.isEmpty()) {
boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
}
// check for nested result maps in parameter mappings (issue #30)
for (ParameterMapping pm : boundSql.getParameterMappings()) {
String rmId = pm.getResultMapId();
if (rmId != null) {
ResultMap rm = configuration.getResultMap(rmId);
if (rm != null) {
hasNestedResultMaps |= rm.hasNestedResultMaps();
}
}
}
到这就已经获取到sql语句了。
我的猜想二又得到了一个验证。
整体流程是什么样的呢。我稍作梳理
sqlsessionfactory.opensession(通过configuration,executor,autocommit构建一个defaultsqlsession)—>sqlsession.getmapper(configuration.getmapper–>mapperregister.getmapper(f方法实现是mapperproxyfactory–》newInstance 获得一个mapperProxy) ) ---->invoke里面调用execute方法—>出现了selectone —>selectList(调用了executor.query()–> getBoundSql 到这里就是完全得到了一个sql语句了(仅仅以select为用例,我觉得其他的也类似)
剩下的就是如何执行sql语句了。
这一个流程重要的是getboundsql