mybatis是一套ORM框架,也就是把字段映射爲對象的屬性。
大學學過,最基礎要操作一個數據庫步驟:
引入JDBC包——DriverManager註冊驅動——創建連接——創建Statement——CRUD操作——操作結果集——關閉連接。
爲了省去上面每次操作的繁瑣過程,而且相比hibernate更加靈活,方便維護。mybatis出現了。
首先pom.xml要引入mybatis。
框架的大致邏輯:
package com.stu.demo;
import com.stu.demo.pojo.User;
import org.apache.ibatis.annotations.Select;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
interface UserMapper{
@Select("select * from t_user where id = #{id} and grade = #{grade}")
List<User> selectUsers(Integer id, String grade);
}
public class Application {
public static void main(String[] args) {
// JDK動態代理,反射實現
UserMapper userMapper = (UserMapper) Proxy.newProxyInstance(Application.class.getClassLoader(), new Class<?>[]{UserMapper.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法名字:"+method.getName());
System.out.println("方法入參:" + Arrays.toString(args));
// sql參數和方法參數的對應鍵值關係
Map<String, Object> mapData = buildSQLData(method, args);
Select annotions = method.getAnnotation(Select.class);
if (annotions != null) {
String[] strs = annotions.value(); // 這個sql可以是多條
System.out.println("Mybatis種sql語句:" + Arrays.toString(strs));
// 根據鍵值關係賦值得到sql
String sql = bulidSQL(strs[0], mapData);
System.out.println("最終得到要在數據庫執行的sql語句:" + sql);
}
return null;
}
});
userMapper.selectUsers(1, "303班");
}
private static String bulidSQL(String str, Map<String, Object> mapData) {
// 頻繁字符操作
StringBuilder sb = new StringBuilder();
int length = str.length();
for (int i = 0; i < length; i++) {
char ch = str.charAt(i);
if (ch == '#') {
int nextIndex = i + 1;
char nextChar = str.charAt(nextIndex);
if ('{' != nextChar) {
throw new RuntimeException("sql異常:" + str);
}
// 找到當前}結束位置 argStr爲返回的參數key,比如id
StringBuilder argStr = new StringBuilder();
i = parseArgStr(argStr,str,nextIndex);
// 根據key找到對應的值。
Object argValue = mapData.get(argStr.toString());
if (argValue == null) {
throw new RuntimeException("argValue爲null");
}
sb.append(argValue);
continue;
}
sb.append(ch);
}
return sb.toString();
}
private static int parseArgStr(StringBuilder argStr, String str, int nextIndex) {
// 從nextIndex++循環
nextIndex++;
for (;nextIndex < str.length(); nextIndex++) {
char ch = str.charAt(nextIndex);
if (ch != '}') {
argStr.append(ch);
continue;
}
if (ch == '}') {
return nextIndex;
}
}
// 最後都沒有} 異常處理。
throw new RuntimeException("缺少}:"+ str);
}
/**
* sql參數和方法參數的對應鍵值關係
* @param method
* @param args
* @return
*/
public static Map<String, Object> buildSQLData(Method method, Object[] args) {
Map<String, Object> mapData = new HashMap<>();
// 參數獲得
Parameter[] parameters = method.getParameters();
// 將我們的參數賦值給sql上變量。
int index[] = {0}; // 因爲裏面用的時引用類型。
// 當數據量大時,並行運行。
// Arrays.asList(parameters).parallelStream().forEach
// 與之對應Map<String, Object> mapData = new ConcurrentHashMap<>();
Arrays.asList(parameters).forEach((parameter)->{
String name = parameter.getName();
System.out.println("SQL上參數的名字:" + name);
// 得到鍵值關係--sql參數和方法參數的對應鍵值關係
mapData.put(name, args[index[0]]);
index[0]++;
});
return mapData;
}
public static String parseIntoSQL(String sql, Map<String, Object> mapData){
return "";
}
}
運行結果:(很清晰的看到這個過程)
在框架種處理更簡單:通過反射直接拿到返回類型和參數類型。
UserMapper userMapper = (UserMapper) Proxy.newProxyInstance(Application.class.getClassLoader(), new Class<?>[]{UserMapper.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method.getReturnType());
System.out.println(method.getGenericReturnType());
}
return null;
}
});
結果:(找到了這兩個,get和set進去就行了)
Mybatis架構設計:
源碼包如下,ibatis包下的包:
annotations: 註解包
Arg、AutomapConstructor、CacheNamespace、CacheNamespaceRef、Case、ConstructorArgs、Delete、
DeleteProvider、Flush、Insert、InsertProvider、Lang、Many、MapKey、Mapper、One、Options、
Param、Property、Result、ResultMap、Results、ResultType、Select、SelectKey、SelectProvider、
TypeDiscriminator、Update、UpdateProvider
binding:綁定包
BindingException、MapperMethod、MapperProxy、MapperProxyFactory、MapperRegistry
builder:構建包
cache:緩衝存儲包
cursor:遊標包
datasource:數據源包
exceptions:異常包
executor:執行器包
io:io包
javassist:java助手
jdbc:連接包
lang:基礎類型包
logging:日誌包
mapping:映射包
ognl:語義解析包
parsing:解析包。用於解析mapper樹結構。
plugin:依賴包
reflection:反射包
scripting:腳本包
session:session包
transaction:事務包
type:各種類型包
源碼分析:
返回結果集ResultContext:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.apache.ibatis.session;
public interface ResultContext<T> {
T getResultObject();
int getResultCount();
boolean isStopped();
void stop();
}
返回處理類ResultHandler:
package org.apache.ibatis.session;
public interface ResultHandler<T> {
void handleResult(ResultContext<? extends T> var1);
}
SqlSessionFactoryBuilder 構建類:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.apache.ibatis.session;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.Properties;
import org.apache.ibatis.builder.xml.XMLConfigBuilder;
import org.apache.ibatis.exceptions.ExceptionFactory;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.session.defaults.DefaultSqlSessionFactory;
public class SqlSessionFactoryBuilder {
public SqlSessionFactoryBuilder() {
}
public SqlSessionFactory build(Reader reader) {
return this.build((Reader)reader, (String)null, (Properties)null);
}
public SqlSessionFactory build(Reader reader, String environment) {
return this.build((Reader)reader, environment, (Properties)null);
}
public SqlSessionFactory build(Reader reader, Properties properties) {
return this.build((Reader)reader, (String)null, properties);
}
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
SqlSessionFactory var5;
try {
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
var5 = this.build(parser.parse());
} catch (Exception var14) {
throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
} finally {
ErrorContext.instance().reset();
try {
reader.close();
} catch (IOException var13) {
}
}
return var5;
}
public SqlSessionFactory build(InputStream inputStream) {
return this.build((InputStream)inputStream, (String)null, (Properties)null);
}
public SqlSessionFactory build(InputStream inputStream, String environment) {
return this.build((InputStream)inputStream, environment, (Properties)null);
}
public SqlSessionFactory build(InputStream inputStream, Properties properties) {
return this.build((InputStream)inputStream, (String)null, properties);
}
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
SqlSessionFactory var5;
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
var5 = this.build(parser.parse());
} catch (Exception var14) {
throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException var13) {
}
}
return var5;
}
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
}
我們調用裏面的build方法,複用方式調用
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
SqlSessionFactory var5;
try {
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
var5 = this.build(parser.parse());
} catch (Exception var14) {
throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
} finally {
ErrorContext.instance().reset();
try {
reader.close();
} catch (IOException var13) {
}
}
return var5;
}
裏面XMLConfigBuilder對象:
XMLConfigBuilder類中:對XPathParser引用。
public XMLConfigBuilder(Reader reader, String environment, Properties props) {
this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props);
}
XPathParser類中:
public XPathParser(Reader reader, boolean validation, Properties variables, EntityResolver entityResolver) {
this.commonConstructor(validation, variables, entityResolver);
this.document = this.createDocument(new InputSource(reader));
}
commonConstructor得到Xpath實例:
XPathFactory factory = XPathFactory.newInstance();
this.xpath = factory.newXPath();
拿到返回的實例,創建結構樹this.createDocument(new InputSource(reader));
可以發現這裏面全是原生JDK代碼的引用。InputSource是A single input source for an XML entity.
最後得到xml解析後的Document 結構:這個如何去解析,參照最上的解析方式。
private Document createDocument(InputSource inputSource) {
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(this.validation);
factory.setNamespaceAware(false);
factory.setIgnoringComments(true);
factory.setIgnoringElementContentWhitespace(false);
factory.setCoalescing(false);
factory.setExpandEntityReferences(true);
DocumentBuilder builder = factory.newDocumentBuilder();
builder.setEntityResolver(this.entityResolver);
builder.setErrorHandler(new ErrorHandler() {
public void error(SAXParseException exception) throws SAXException {
throw exception;
}
public void fatalError(SAXParseException exception) throws SAXException {
throw exception;
}
public void warning(SAXParseException exception) throws SAXException {
}
});
return builder.parse(inputSource);
} catch (Exception var4) {
throw new BuilderException("Error creating document instance. Cause: " + var4, var4);
}
}
調用XMLConfigBuilder類的parse方法:
public Configuration parse() {
if (this.parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
} else {
this.parsed = true;
this.parseConfiguration(this.parser.evalNode("/configuration"));
return this.configuration;
}
}
這個調用this.parser.evalNode("/configuration"):
public XNode evalNode(Object root, String expression) {
Node node = (Node)this.evaluate(expression, root, XPathConstants.NODE);
return node == null ? null : new XNode(this, node, this.variables);
}
他在引用XNode類:
這裏有走到了JDK。拿到xpathParser,結點node,結點名getNodeName,變量等。
public XNode(XPathParser xpathParser, Node node, Properties variables) {
this.xpathParser = xpathParser;
this.node = node;
this.name = node.getNodeName();
this.variables = variables;
this.attributes = this.parseAttributes(node);
this.body = this.parseBody(node);
}
將返回的值設給parseConfiguration:
private void parseConfiguration(XNode root) {
try {
this.propertiesElement(root.evalNode("properties"));
Properties settings = this.settingsAsProperties(root.evalNode("settings"));
this.loadCustomVfs(settings);
this.loadCustomLogImpl(settings);
this.typeAliasesElement(root.evalNode("typeAliases"));
this.pluginElement(root.evalNode("plugins"));
this.objectFactoryElement(root.evalNode("objectFactory"));
this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
this.reflectorFactoryElement(root.evalNode("reflectorFactory"));
this.settingsElement(settings);
this.environmentsElement(root.evalNode("environments"));
this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
this.typeHandlerElement(root.evalNode("typeHandlers"));
this.mapperElement(root.evalNode("mappers"));
} catch (Exception var3) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
}
}
主要核心:迭代解析出,設置好transcational,dataSource在set進configuration
private void environmentsElement(XNode context) throws Exception {
if (context != null) {
if (this.environment == null) {
this.environment = context.getStringAttribute("default");
}
Iterator var2 = context.getChildren().iterator();
while(var2.hasNext()) {
XNode child = (XNode)var2.next();
String id = child.getStringAttribute("id");
if (this.isSpecifiedEnvironment(id)) {
TransactionFactory txFactory = this.transactionManagerElement(child.evalNode("transactionManager"));
DataSourceFactory dsFactory = this.dataSourceElement(child.evalNode("dataSource"));
DataSource dataSource = dsFactory.getDataSource();
Builder environmentBuilder = (new Builder(id)).transactionFactory(txFactory).dataSource(dataSource);
this.configuration.setEnvironment(environmentBuilder.build());
}
}
}
}
到此我們就得到了:Configuration config。
再調用build方法:
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
引用Configuration類:在這個類中需要MapperRegistry將mapper接口註冊進來:
可以看到MapperRegistry類中有個addMapper方法。他直接接受接口類。
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
if (this.hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
this.knownMappers.put(type, new MapperProxyFactory(type));
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(this.config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
this.knownMappers.remove(type);
}
}
}
}
這裏就出現了Mybatis的一個優化點:
場景:knownMappers集合種有很多mapper,問題如何高效有序加載執行。因爲我們配置都是
mybatis.mapper-locations=classpath:mapper/*.xml
經過掃描@Mapper註解,加載了上千個怎麼辦?
上面將返回的值設給parseConfiguration時,
this.mapperElement(root.evalNode("mappers"));
上面的方法種調用his.configuration.addMappers(resource):
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
Iterator var2 = parent.getChildren().iterator();
while(true) {
while(var2.hasNext()) {
XNode child = (XNode)var2.next();
String resource;
if ("package".equals(child.getName())) {
resource = child.getStringAttribute("name");
this.configuration.addMappers(resource);
} else {
resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
XMLMapperBuilder mapperParser;
InputStream inputStream;
if (resource != null && url == null && mapperClass == null) {
ErrorContext.instance().resource(resource);
inputStream = Resources.getResourceAsStream(resource);
mapperParser = new XMLMapperBuilder(inputStream, this.configuration, resource, this.configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url != null && mapperClass == null) {
ErrorContext.instance().resource(url);
inputStream = Resources.getUrlAsStream(url);
mapperParser = new XMLMapperBuilder(inputStream, this.configuration, url, this.configuration.getSqlFragments());
mapperParser.parse();
} else {
if (resource != null || url != null || mapperClass == null) {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
Class<?> mapperInterface = Resources.classForName(mapperClass);
this.configuration.addMapper(mapperInterface);
}
}
}
return;
}
}
}
上面如何來優化:
利用反射修改,進行小數據量和大數量分離,分別用單線程和多線程去處理,可以發現小數據單線程快,大數據多線程快,因此採取一個臨界值去分開處理。
另外我們還要考慮:
線程優化,考慮加鎖解鎖時間,集合擴容時間,線程切換,可以考慮自旋鎖。多線程不一定比單線程快。CAS樂觀鎖等。
回到上面addMapper方法種 parser.parse(),又在parse方法種調用bindMapperForNamespace,通過namespace反射到綁定的類,放入到configuration集合中,同時namespace放入addLoadedResource加載資源中:
private void bindMapperForNamespace() {
String namespace = this.builderAssistant.getCurrentNamespace();
if (namespace != null) {
Class boundType = null;
try {
boundType = Resources.classForName(namespace);
} catch (ClassNotFoundException var4) {
}
if (boundType != null && !this.configuration.hasMapper(boundType)) {
this.configuration.addLoadedResource("namespace:" + namespace);
this.configuration.addMapper(boundType);
}
}
}
上面有寫道返回new DefaultSqlSessionFactory(config):這個過程又做了什麼呢?
Mapper實例生成:
在binding包中有一個類叫MapperProxy類:裏面有反射實現
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
}
if (method.isDefault()) {
return this.invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable var5) {
throw ExceptionUtil.unwrapThrowable(var5);
}
MapperMethod mapperMethod = this.cachedMapperMethod(method);
return mapperMethod.execute(this.sqlSession, args);
}
private MapperMethod cachedMapperMethod(Method method) {
return (MapperMethod)this.methodCache.computeIfAbsent(method, (k) -> {
return new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration());
});
}
如上,MapperProxy的生產過程,自定義Mapper類的對象是通過動態代理生產的,調用自定義方法的時候實際上是調用了MapperMethod的execute方法
MapperMethod類中,execute實行交互傳入得到sqlSession,args。可以發現裏面CRUD都有了。
在實行CRUD之前:
會執行SqlCommand方法:
這裏將namespace和方法名拼接爲statement:查看configuration有無,有取返回,無則拿到所有的接口遍歷遞歸。
private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName, Class<?> declaringClass, Configuration configuration) {
String statementId = mapperInterface.getName() + "." + methodName;
if (configuration.hasStatement(statementId)) {
return configuration.getMappedStatement(statementId);
} else if (mapperInterface.equals(declaringClass)) {
return null;
} else {
Class[] var6 = mapperInterface.getInterfaces();
int var7 = var6.length;
for(int var8 = 0; var8 < var7; ++var8) {
Class<?> superInterface = var6[var8];
if (declaringClass.isAssignableFrom(superInterface)) {
MappedStatement ms = this.resolveMappedStatement(superInterface, methodName, declaringClass, configuration);
if (ms != null) {
return ms;
}
}
}
return null;
}
}
再執行MethodSignature方法拿到返回的類型。
拿着準備好的sqlSession,args反射執行execute方法:
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
Object param;
switch(this.command.getType()) {
case INSERT:
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
break;
case UPDATE:
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
break;
case DELETE:
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
break;
case SELECT:
if (this.method.returnsVoid() && this.method.hasResultHandler()) {
this.executeWithResultHandler(sqlSession, args);
result = null;
} else if (this.method.returnsMany()) {
result = this.executeForMany(sqlSession, args);
} else if (this.method.returnsMap()) {
result = this.executeForMap(sqlSession, args);
} else if (this.method.returnsCursor()) {
result = this.executeForCursor(sqlSession, args);
} else {
param = this.method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(this.command.getName(), param);
if (this.method.returnsOptional() && (result == null || !this.method.getReturnType().equals(result.getClass()))) {
result = Optional.ofNullable(result);
}
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + this.command.getName());
}
if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {
throw new BindingException("Mapper method '" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ").");
} else {
return result;
}
}
現在是查詢:走convertArgsToSqlCommandParam將參數sqlCommand。在執行selectOne(this.command.getName(), param):
再走到selectList: 得到ms ,它是信息的封裝,比如resource、configuration、sqlSource、parameter等等,你執行當前sql所需要的信息。
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
List var5;
try {
MappedStatement ms = this.configuration.getMappedStatement(statement);
var5 = this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception var9) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + var9, var9);
} finally {
ErrorContext.instance().reset();
}
return var5;
}
接下來就是核心了:
this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER)
BaseExecutor類中的query方法:
先查mybatis一級緩存,創建緩存key。進入複用方法,查詢queryStack。執行queryFromDatabase方法
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameter);
CacheKey key = this.createCacheKey(ms, parameter, rowBounds, boundSql);
return this.query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
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 (this.closed) {
throw new ExecutorException("Executor was closed.");
} else {
if (this.queryStack == 0 && ms.isFlushCacheRequired()) {
this.clearLocalCache();
}
List list;
try {
++this.queryStack;
list = resultHandler == null ? (List)this.localCache.getObject(key) : null;
if (list != null) {
this.handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
--this.queryStack;
}
if (this.queryStack == 0) {
Iterator var8 = this.deferredLoads.iterator();
while(var8.hasNext()) {
BaseExecutor.DeferredLoad deferredLoad = (BaseExecutor.DeferredLoad)var8.next();
deferredLoad.load();
}
this.deferredLoads.clear();
if (this.configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
this.clearLocalCache();
}
}
return list;
}
}
根據情況localCache增加移除:最終執行doQuery
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
this.localCache.putObject(key, ExecutionPlaceholder.EXECUTION_PLACEHOLDER);
List list;
try {
list = this.doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
this.localCache.removeObject(key);
}
this.localCache.putObject(key, list);
if (ms.getStatementType() == StatementType.CALLABLE) {
this.localOutputParameterCache.putObject(key, parameter);
}
return list;
}
BatchExecutor類中:
可發現這裏誰執行誰關閉,怎麼做到的,因爲一次ms,只有一次事務。
將configuration 交由StatementHandler 來處理。這裏的過程就是原生sql的創建到查詢的過程。
創建連接Connection --通過handler.prepare得到Statement –
public <E> List<E> doQuery(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
List var10;
try {
this.flushStatements();
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameterObject, rowBounds, resultHandler, boundSql);
Connection connection = this.getConnection(ms.getStatementLog());
stmt = handler.prepare(connection, this.transaction.getTimeout());
handler.parameterize(stmt);
var10 = handler.query(stmt, resultHandler);
} finally {
this.closeStatement(stmt);
}
return var10;
}
方法會走到eparePreparedStatementHandler類中instantiateStatement方法,以及並調用parameterize向下轉型。這個是不是就很眼熟了呢。
protected Statement instantiateStatement(Connection connection) throws SQLException {
String sql = this.boundSql.getSql();
if (this.mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
String[] keyColumnNames = this.mappedStatement.getKeyColumns();
return keyColumnNames == null ? connection.prepareStatement(sql, 1) : connection.prepareStatement(sql, keyColumnNames);
} else {
return this.mappedStatement.getResultSetType() == ResultSetType.DEFAULT ? connection.prepareStatement(sql) : connection.prepareStatement(sql, this.mappedStatement.getResultSetType().getValue(), 1007);
}
}
public void parameterize(Statement statement) throws SQLException {
this.parameterHandler.setParameters((PreparedStatement)statement);
}
在執行handler的query方法:
從boundSql中得到sql語句,execute執行sql
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
String sql = this.boundSql.getSql();
statement.execute(sql);
return this.resultSetHandler.handleResultSets(statement);
}
DefaultResultSetHandler類中拿到我們返回結果集。
我們可以發現該方法中ResultMap,這個是不是更眼熟了呢。這個就是xsm中的ResultMap。ResultMap類就是對動態映射的封裝。
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(this.mappedStatement.getId());
List<Object> multipleResults = new ArrayList();
int resultSetCount = 0;
ResultSetWrapper rsw = this.getFirstResultSet(stmt);
List<ResultMap> resultMaps = this.mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
this.validateResultMapsCount(rsw, resultMapCount);
while(rsw != null && resultMapCount > resultSetCount) {
ResultMap resultMap = (ResultMap)resultMaps.get(resultSetCount);
this.handleResultSet(rsw, resultMap, multipleResults, (ResultMapping)null);
rsw = this.getNextResultSet(stmt);
this.cleanUpAfterHandlingResultSet();
++resultSetCount;
}
String[] resultSets = this.mappedStatement.getResultSets();
if (resultSets != null) {
while(rsw != null && resultSetCount < resultSets.length) {
ResultMapping parentMapping = (ResultMapping)this.nextResultMaps.get(resultSets[resultSetCount]);
if (parentMapping != null) {
String nestedResultMapId = parentMapping.getNestedResultMapId();
ResultMap resultMap = this.configuration.getResultMap(nestedResultMapId);
this.handleResultSet(rsw, resultMap, (List)null, parentMapping);
}
rsw = this.getNextResultSet(stmt);
this.cleanUpAfterHandlingResultSet();
++resultSetCount;
}
}
return this.collapseSingleResultList(multipleResults);
}
通過映射,調用實體類的所有構造器,返回對象。到此結束。
構造主要體現在DefaultResultSetHandler類中如下方法:
private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix) throws SQLException {
Class<?> resultType = resultMap.getType();
MetaClass metaType = MetaClass.forClass(resultType, this.reflectorFactory);
List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings();
if (this.hasTypeHandlerForResultObject(rsw, resultType)) {
return this.createPrimitiveResultObject(rsw, resultMap, columnPrefix);
} else if (!constructorMappings.isEmpty()) {
return this.createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, constructorArgs, columnPrefix);
} else if (!resultType.isInterface() && !metaType.hasDefaultConstructor()) {
if (this.shouldApplyAutomaticMappings(resultMap, false)) {
return this.createByConstructorSignature(rsw, resultType, constructorArgTypes, constructorArgs);
} else {
throw new ExecutorException("Do not know how to create an instance of " + resultType);
}
} else {
return this.objectFactory.create(resultType);
}
}
private Object createByConstructorSignature(ResultSetWrapper rsw, Class<?> resultType, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) throws SQLException {
Constructor<?>[] constructors = resultType.getDeclaredConstructors();
Constructor<?> defaultConstructor = this.findDefaultConstructor(constructors);
if (defaultConstructor != null) {
return this.createUsingConstructor(rsw, resultType, constructorArgTypes, constructorArgs, defaultConstructor);
} else {
Constructor[] var7 = constructors;
int var8 = constructors.length;
for(int var9 = 0; var9 < var8; ++var9) {
Constructor<?> constructor = var7[var9];
if (this.allowedConstructorUsingTypeHandlers(constructor, rsw.getJdbcTypes())) {
return this.createUsingConstructor(rsw, resultType, constructorArgTypes, constructorArgs, constructor);
}
}
throw new ExecutorException("No constructor found in " + resultType.getName() + " matching " + rsw.getClassNames());
}
}
private Object createUsingConstructor(ResultSetWrapper rsw, Class<?> resultType, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, Constructor<?> constructor) throws SQLException {
boolean foundValues = false;
for(int i = 0; i < constructor.getParameterTypes().length; ++i) {
Class<?> parameterType = constructor.getParameterTypes()[i];
String columnName = (String)rsw.getColumnNames().get(i);
TypeHandler<?> typeHandler = rsw.getTypeHandler(parameterType, columnName);
Object value = typeHandler.getResult(rsw.getResultSet(), columnName);
constructorArgTypes.add(parameterType);
constructorArgs.add(value);
foundValues = value != null || foundValues;
}
return foundValues ? this.objectFactory.create(resultType, constructorArgTypes, constructorArgs) : null;
}