Mybatis-註解sql
Demo
主啟動類
public class MybatisHelloWorld {
public static void main(String[] args) throws Exception {
String resource = "org/mybatis/config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = sqlSessionFactory.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);
List<User> users = mapper.getUsers(1);
session.close();
}
}
userMapper.class
public interface UserMapper {
@Select({"select * from user where age=#{age}"})
List<User> getUsers(int age);
}
config.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
//控制檯輸出sql
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mappers>
<package name="org.mybatis.mapper"/>
</mappers>
</configuration>
Mybatis透過session來進行資料庫的操作,sqlSessionFactory封裝了session的建立,而SqlSessionFactoryBuilder又封裝了sqlSessionFactory的建立
從上面程式碼來看總共做了兩件事
- 讀取配置檔案,透過SqlSessionFactoryBuilder建立sqlSessionFactory繼而建立session
- 獲取mapper進行讀取資料庫
先來看如何將xml配置檔案封裝為物件的
解析配置檔案
new SqlSessionFactoryBuilder().build(inputStream);
這裡使用構造者模式來建立一個sqlSessionFactory,裡面使用過載
public SqlSessionFactory build(InputStream inputStream) {
return build(inputStream, null, null);
}
最終呼叫
SqlSessionFactoryBuilder.java
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
//建立一個xml解析類
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
//解析xml中配置,轉換為configuration類
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
mybatis是把一些配置類以及它自己需要使用的各種類封裝成一個大的config物件
org.apache.ibatis.session.Configuration 裡面有很多環境,mapper等等的資訊,內容太多就不貼上了
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
}
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
//建立了一個Configuration 物件
super(new Configuration());
ErrorContext.instance().resource("SQL Mapper Configuration");
this.configuration.setVariables(props);
this.parsed = false;
//這一行設定環境id
this.environment = environment;
this.parser = parser;
}
XMlConfigBuilder類關係圖
BaseBuilder.java
public BaseBuilder(Configuration configuration) {
this.configuration = configuration;
this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
}
解析主配置檔案.xml
SqlSessionFactoryBuilder.java
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
//....
return build(parser.parse());
//....
}
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
//讀取configuration節點下的node傳入
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
//讀取properties
propertiesElement(root.evalNode("properties"));
//讀取一些setting設定
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
//註冊別名
typeAliasesElement(root.evalNode("typeAliases"));
//外掛,進行增強-先略過
pluginElement(root.evalNode("plugins"));
//物件工廠,自定義例項化方法--略過
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectionFactoryElement(root.evalNode("reflectionFactory"));
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
//配置環境
environmentsElement(root.evalNode("environments"));
//資料廠商表示--略過
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
//配置mapper
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
解析的東西很多,我們只先看environments和mapper
environmentsElement
XMLConfigBuilder.java
private void environmentsElement(XNode context) throws Exception {
if (context != null) {
if (environment == null) {
environment = context.getStringAttribute("default");
}
for (XNode child : context.getChildren()) {
String id = child.getStringAttribute("id");
//可以配置多個環境,判斷是不是指定的環境
if (isSpecifiedEnvironment(id)) {
//獲取事物管理器,建立事物管理器工廠
TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
//獲取datasource工廠-UnpooledDataSourceFactory預設
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
DataSource dataSource = dsFactory.getDataSource();
Environment.Builder environmentBuilder = new Environment.Builder(id)
.transactionFactory(txFactory)
.dataSource(dataSource);
configuration.setEnvironment(environmentBuilder.build());
}
}
}
}
進入發現第一件是就是判斷環境,沒有指定就使用<environments default="development">
中default的環境id,在上面的XMLConfigBuilder的有參構造中this.environment = environment;
將環境配置設定給了XMLConfigBuilder的environment 點我跳轉到XMLConfigBuilder-有參構造
我們在使用時可以這樣,在配置檔案xml中,宣告多個環境
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
<environment id="myTest">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
主啟動類中,手動指明一個配置環境
public static void main(String[] args) throws Exception {
//....
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream,"myTest");
//....
}
回到程式碼第一步就是判斷使用者選擇的那個id的環境,之後建立事務管理器
XMLConfigBuilder.java
private void environmentsElement(XNode context) throws Exception {
//..
TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
//..
}
private TransactionFactory transactionManagerElement(XNode context) throws Exception {
if (context != null) {
//獲取我們在xml中宣告的事務管理型別,當前是JDBC
String type = context.getStringAttribute("type");
//獲取節點下的子節點,當前案例沒有子節點
Properties props = context.getChildrenAsProperties();
//這裡只是建立工廠類
TransactionFactory factory = (TransactionFactory) resolveClass(type).newInstance();
factory.setProperties(props);
return factory;
}
throw new BuilderException("Environment declaration requires a TransactionFactory.");
}
這裡呼叫resolveClass()
方法是父類BaseBuilder的方法
一直點進去最後如下
TypeAliasRegistry.java
public class TypeAliasRegistry {
private final Map<String, Class<?>> TYPE_ALIASES = new HashMap<String, Class<?>>();
//.....
public <T> Class<T> resolveAlias(String string) {
try {
if (string == null) {
return null;
}
// issue #748
String key = string.toLowerCase(Locale.ENGLISH);
Class<T> value;
if (TYPE_ALIASES.containsKey(key)) {
value = (Class<T>) TYPE_ALIASES.get(key);
} else {
value = (Class<T>) Resources.classForName(string);
}
return value;
} catch (ClassNotFoundException e) {
throw new TypeException("Could not resolve type alias '" + string + "'. Cause: " + e, e);
}
}
}
判斷這個TYPE_ALIASES map中是否存在JDBC這個key,如果不存在,則去載入
按理來說這裡應該是不存在的,因為你在TypeAliasRegistry中找不到任何一個地方對TYPE_ALIASES新增一個JDBC的key
但是實際它卻存在這個key,在Configuration類的無參構造時,對這個TypeAliasRegistry進行的新增
public Configuration() {
typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
//...
typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
//......
}
這個過程如下圖
回到程式碼因為我們這次案例的配置為<transactionManager type="JDBC"/>
所以不會存在子節點context.getChildrenAsProperties();
返回的結果0個配置項,transactionManagerElement
方法結束
之後去解析資料庫配置檔案
XMLConfigBulider.java
private void environmentsElement(XNode context) throws Exception {
//...
//獲取datasource工廠-UnpooledDataSourceFactory預設
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
DataSource dataSource = dsFactory.getDataSource();
//...
}
和解析環境基本一樣的程式碼,不過解析dataSource的時候,子節點就不為空了
會有四個屬性
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC"/>
<property name="username" value="root222"/>
<property name="password" value="root222"/>
</dataSource>
private DataSourceFactory dataSourceElement(XNode context) throws Exception {
if (context != null) {
String type = context.getStringAttribute("type");
Properties props = context.getChildrenAsProperties();
//type為POOLED的預設實現是PooledDataSourceFactory
DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance();
factory.setProperties(props);
return factory;
}
throw new BuilderException("Environment declaration requires a DataSourceFactory.");
}
進入factory.setProperties(props);
public class UnpooledDataSourceFactory implements DataSourceFactory {
private static final String DRIVER_PROPERTY_PREFIX = "driver.";
private static final int DRIVER_PROPERTY_PREFIX_LENGTH = DRIVER_PROPERTY_PREFIX.length();
protected DataSource dataSource;
//無參預設將dataSource設定為UnpooledDataSource
public UnpooledDataSourceFactory() {
this.dataSource = new UnpooledDataSource();
}
@Override
public void setProperties(Properties properties) {
Properties driverProperties = new Properties();
//將工廠物件進行包裝
MetaObject metaDataSource = SystemMetaObject.forObject(dataSource);
for (Object key : properties.keySet()) {
String propertyName = (String) key;
//如果存在driver
if (propertyName.startsWith(DRIVER_PROPERTY_PREFIX)) {
String value = properties.getProperty(propertyName);
driverProperties.setProperty(propertyName.substring(DRIVER_PROPERTY_PREFIX_LENGTH), value);
//如果當前屬性在類中有對應的可以寫入的屬性
} else if (metaDataSource.hasSetter(propertyName)) {
String value = (String) properties.get(propertyName);
Object convertedValue = convertValue(metaDataSource, propertyName, value);
metaDataSource.setValue(propertyName, convertedValue);
} else {
throw new DataSourceException("Unknown DataSource property: " + propertyName);
}
}
//如果屬性不為空,則設定給meatDataSource
if (driverProperties.size() > 0) {
metaDataSource.setValue("driverProperties", driverProperties);
}
}
//......
}
一頓設定後回到XMLConfigurationBuilder中的environmentsElement方法
最後將讀取出的配置封裝為Environment,賦值給BaseBuilder中的environment
private void environmentsElement(XNode context) throws Exception {
//.....
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
DataSource dataSource = dsFactory.getDataSource();
Environment.Builder environmentBuilder = new Environment.Builder(id)
.transactionFactory(txFactory)
.dataSource(dataSource);
configuration.setEnvironment(environmentBuilder.build());
//.....
}
mapperElement
回到XMLConfigBuilder中的parseConfiguration
private void parseConfiguration(XNode root) {
//.....
//配置mapper
mapperElement(root.evalNode("mappers"));
}
我們只看根據包掃描的,給Configuration中新增了mapper包名
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
//使用包,預設查詢指定包下位置
if ("package".equals(child.getName())) {
String mapperPackage = child.getStringAttribute("name");
configuration.addMappers(mapperPackage);
}
//.....
}
}
}
Configuration.java
public void addMappers(String packageName) {
mapperRegistry.addMappers(packageName);
}
MapperRegistry.java
public void addMappers(String packageName) {
addMappers(packageName, Object.class);
}
//根據包名去查詢該包下的類
public void addMappers(String packageName, Class<?> superType) {
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
for (Class<?> mapperClass : mapperSet) {
addMapper(mapperClass);
}
}
之後就是動態代理對應的mapper
MapperRegistry.java
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
//判斷是否已經存在
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
knownMappers.put(type, new MapperProxyFactory<T>(type));
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public Class<T> getMapperInterface() {
return mapperInterface;
}
public Map<Method, MapperMethod> getMethodCache() {
return methodCache;
}
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}
主要來看這段
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
MapperAnnotationBuilder.java
public class MapperAnnotationBuilder {
private final Set<Class<? extends Annotation>> sqlAnnotationTypes = new HashSet<Class<? extends Annotation>>();
private final Set<Class<? extends Annotation>> sqlProviderAnnotationTypes = new HashSet<Class<? extends Annotation>>();
//....
/**
* 在構造時新增mybatis的註解
*/
public MapperAnnotationBuilder(Configuration configuration, Class<?> type) {
String resource = type.getName().replace('.', '/') + ".java (best guess)";
this.assistant = new MapperBuilderAssistant(configuration, resource);
this.configuration = configuration;
this.type = type;
sqlAnnotationTypes.add(Select.class);
sqlAnnotationTypes.add(Insert.class);
sqlAnnotationTypes.add(Update.class);
sqlAnnotationTypes.add(Delete.class);
sqlProviderAnnotationTypes.add(SelectProvider.class);
sqlProviderAnnotationTypes.add(InsertProvider.class);
sqlProviderAnnotationTypes.add(UpdateProvider.class);
sqlProviderAnnotationTypes.add(DeleteProvider.class);
}
public void parse() {
String resource = type.toString();
if (!configuration.isResourceLoaded(resource)) {
loadXmlResource();
configuration.addLoadedResource(resource);
assistant.setCurrentNamespace(type.getName());
parseCache();
parseCacheRef();
Method[] methods = type.getMethods();
for (Method method : methods) {
try {
// issue #237
if (!method.isBridge()) {
parseStatement(method);
}
} catch (IncompleteElementException e) {
configuration.addIncompleteMethod(new MethodResolver(this, method));
}
}
}
parsePendingMethods();
}
}
void parseStatement(Method method) {
Class<?> parameterTypeClass = getParameterType(method);
LanguageDriver languageDriver = getLanguageDriver(method);
SqlSource sqlSource = getSqlSourceFromAnnotations(method, parameterTypeClass, languageDriver);
//...
}
首先第一步是獲取引數型別-程式碼如下,如果mapper的入引數量大於1,則返回的就是ParamMap.class
private Class<?> getParameterType(Method method) {
Class<?> parameterType = null;
Class<?>[] parameterTypes = method.getParameterTypes();
for (int i = 0; i < parameterTypes.length; i++) {
if (!RowBounds.class.isAssignableFrom(parameterTypes[i]) && !ResultHandler.class.isAssignableFrom(parameterTypes[i])) {
if (parameterType == null) {
parameterType = parameterTypes[i];
} else {
// issue #135
parameterType = ParamMap.class;
}
}
}
return parameterType;
}
之後獲取語言解析,沒有指定就去找預設-預設的是XMLLanguageDriver.class 還是在Configuration類無參構造時新增進去的點我跳轉到Configuration-無參構造
private LanguageDriver getLanguageDriver(Method method) {
Lang lang = method.getAnnotation(Lang.class);
Class<?> langClass = null;
if (lang != null) {
langClass = lang.value();
}
return assistant.getLanguageDriver(langClass);
}
獲取註解上的內容,以及封裝sql就在這個方法
private SqlSource getSqlSourceFromAnnotations(Method method, Class<?> parameterType, LanguageDriver languageDriver) {
try {
//獲取是否存在@Select,@Insert....
Class<? extends Annotation> sqlAnnotationType = getSqlAnnotationType(method);
//獲取是否存在@SelectProvider,@InsertProvider...
Class<? extends Annotation> sqlProviderAnnotationType = getSqlProviderAnnotationType(method);
if (sqlAnnotationType != null) {
if (sqlProviderAnnotationType != null) {
throw new BindingException("You cannot supply both a static SQL and SqlProvider to method named " + method.getName());
}
Annotation sqlAnnotation = method.getAnnotation(sqlAnnotationType);
//獲取註解上的值
final String[] strings = (String[]) sqlAnnotation.getClass().getMethod("value").invoke(sqlAnnotation);
//返回sqlSource
//這個時候還沒有進行填充值
return buildSqlSourceFromStrings(strings, parameterType, languageDriver);
} else if (sqlProviderAnnotationType != null) {
Annotation sqlProviderAnnotation = method.getAnnotation(sqlProviderAnnotationType);
return new ProviderSqlSource(assistant.getConfiguration(), sqlProviderAnnotation);
}
return null;
} catch (Exception e) {
throw new BuilderException("Could not find value method on SQL annotation. Cause: " + e, e);
}
}
此時的strings
值還是 select * from user where age = #{age} 需要給替換為 select * from user where age =?
private SqlSource buildSqlSourceFromStrings(String[] strings, Class<?> parameterTypeClass, LanguageDriver languageDriver) {
final StringBuilder sql = new StringBuilder();
for (String fragment : strings) {
sql.append(fragment);
sql.append(" ");
}
return languageDriver.createSqlSource(configuration, sql.toString().trim(), parameterTypeClass);
}
預設的語言驅動是XMLLanguageDriver
XMLLanguageDriver.java
首先判斷註解上的內容是否存在指令碼,在mybatis官網,動態SQL下的script有使用案例,使得在註解中可以像在xml中使用<if <where 等標籤
@Override
public SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType) {
// issue #3
if (script.startsWith("<script>")) {
XPathParser parser = new XPathParser(script, false, configuration.getVariables(), new XMLMapperEntityResolver());
return createSqlSource(configuration, parser.evalNode("/script"), parameterType);
} else {
// issue #127
script = PropertyParser.parse(script, configuration.getVariables());
TextSqlNode textSqlNode = new TextSqlNode(script);
//判斷是否為動態的sql就取決於使用的是${} 還是#{} 當使用${}時就是動態sql
if (textSqlNode.isDynamic()) {
return new DynamicSqlSource(configuration, textSqlNode);
} else {
return new RawSqlSource(configuration, script, parameterType);
}
}
}
之後在RewSqlSource中對sql進行解析
RewSqlSource.java
public RawSqlSource(Configuration configuration, String sql, Class<?> parameterType) {
SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
Class<?> clazz = parameterType == null ? Object.class : parameterType;
sqlSource = sqlSourceParser.parse(sql, clazz, new HashMap<String, Object>());
}
解析完最終結果如下
回到上個方法
void parseStatement(Method method) {
Class<?> parameterTypeClass = getParameterType(method);
LanguageDriver languageDriver = getLanguageDriver(method);
SqlSource sqlSource = getSqlSourceFromAnnotations(method, parameterTypeClass, languageDriver);
//從這裡繼續
if (sqlSource != null) {
Options options = method.getAnnotation(Options.class);
//mappedStatementId=類全限定+方法名
final String mappedStatementId = type.getName() + "." + method.getName();
//設定獲取資料的大小
Integer fetchSize = null;
//設定此次查詢超時時間
Integer timeout = null;
//https://blog.csdn.net/king101125s/article/details/104167493
StatementType statementType = StatementType.PREPARED;
//resultSet結果型別 FORWARD_ONLY 游標只能向前移動
ResultSetType resultSetType = ResultSetType.FORWARD_ONLY;
//設定sql型別,當前案例是SELECT
SqlCommandType sqlCommandType = getSqlCommandType(method);
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
boolean flushCache = !isSelect;
boolean useCache = isSelect;
KeyGenerator keyGenerator;
String keyProperty = "id";
String keyColumn = null;
if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) {
// first check for SelectKey annotation - that overrides everything else
SelectKey selectKey = method.getAnnotation(SelectKey.class);
if (selectKey != null) {
keyGenerator = handleSelectKeyAnnotation(selectKey, mappedStatementId, getParameterType(method), languageDriver);
keyProperty = selectKey.keyProperty();
} else if (options == null) {
keyGenerator = configuration.isUseGeneratedKeys() ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
} else {
keyGenerator = options.useGeneratedKeys() ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
keyProperty = options.keyProperty();
keyColumn = options.keyColumn();
}
} else {
keyGenerator = new NoKeyGenerator();
}
if (options != null) {
flushCache = options.flushCache();
useCache = options.useCache();
fetchSize = options.fetchSize() > -1 || options.fetchSize() == Integer.MIN_VALUE ? options.fetchSize() : null; //issue #348
timeout = options.timeout() > -1 ? options.timeout() : null;
statementType = options.statementType();
resultSetType = options.resultSetType();
}
String resultMapId = null;
ResultMap resultMapAnnotation = method.getAnnotation(ResultMap.class);
if (resultMapAnnotation != null) {
String[] resultMaps = resultMapAnnotation.value();
StringBuilder sb = new StringBuilder();
for (String resultMap : resultMaps) {
if (sb.length() > 0) {
sb.append(",");
}
sb.append(resultMap);
}
resultMapId = sb.toString();
} else if (isSelect) {
resultMapId = parseResultMap(method);
}
//將引數傳給小助手
assistant.addMappedStatement(
mappedStatementId,
sqlSource,
statementType,
sqlCommandType,
fetchSize,
timeout,
// ParameterMapID
null,
parameterTypeClass,
resultMapId,
getReturnType(method),
resultSetType,
flushCache,
useCache,
// TODO issue #577
false,
keyGenerator,
keyProperty,
keyColumn,
// DatabaseID
null,
languageDriver,
// ResultSets
null);
}
}
public MappedStatement addMappedStatement(
String id,
SqlSource sqlSource,
StatementType statementType,
SqlCommandType sqlCommandType,
Integer fetchSize,
Integer timeout,
String parameterMap,
Class<?> parameterType,
String resultMap,
Class<?> resultType,
ResultSetType resultSetType,
boolean flushCache,
boolean useCache,
boolean resultOrdered,
KeyGenerator keyGenerator,
String keyProperty,
String keyColumn,
String databaseId,
LanguageDriver lang,
String resultSets) {
if (unresolvedCacheRef) {
throw new IncompleteElementException("Cache-ref not yet resolved");
}
id = applyCurrentNamespace(id, false);
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType)
.resource(resource)
.fetchSize(fetchSize)
.timeout(timeout)
.statementType(statementType)
.keyGenerator(keyGenerator)
.keyProperty(keyProperty)
.keyColumn(keyColumn)
.databaseId(databaseId)
.lang(lang)
.resultOrdered(resultOrdered)
.resulSets(resultSets)
.resultMaps(getStatementResultMaps(resultMap, resultType, id))
.resultSetType(resultSetType)
.flushCacheRequired(valueOrDefault(flushCache, !isSelect))
.useCache(valueOrDefault(useCache, isSelect))
.cache(currentCache);
ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);
if (statementParameterMap != null) {
statementBuilder.parameterMap(statementParameterMap);
}
MappedStatement statement = statementBuilder.build();
//這裡將這個MappedStatement放入configuration
configuration.addMappedStatement(statement);
return statement;
}
將各種引數最後封裝為一個MappedStatement,放入configuration中,這樣一個addMapper的方法就結束了
之後回到SqlSessionFactory的build中,執行過載的build
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
//回到這裡
return build(parser.parse());
}
//預設為DefaultSqlSessionFactory
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
DefaultSqlSessionFactory.java
public class DefaultSqlSessionFactory implements SqlSessionFactory {
private final Configuration configuration;
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
//.....
}
至此new SqlSessionFactoryBuilder().build(inputStream)
這段程式碼的解析環境和載入mapper就分析完了
sqlSessionFactory
回到主程式碼
public static void main(String[] args) throws Exception {
String resource = "org/mybatis/config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = sqlSessionFactory.openSession();
//..
}
開始分析sqlSessionFactory.openSession();
點進入發現有兩個實現類,在上面的build中已經明確了建立的就是DefaultSqlSessionFactory
DefaultSqlSessionFactory.java
@Override
public SqlSession openSession() {
//引數:執行器,預設為simple,每次關閉statement SimpleExecutor
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
configuration.getDefaultExecutorType()
在Configuration類中獲取的是本身的一個屬性,型別是一個ExecutorType的列舉,預設為SIMPE
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
//獲取配置類中的事務,datasource封裝類
final Environment environment = configuration.getEnvironment();
//透過配置的環境中獲取事務工廠
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
//建立物件/datasource/隔離等級/是否自動提交
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();
}
}
獲取configuration中的事務工廠之後建立一個執行器
Configuration.java
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
這裡快取是預設開啟的,所以最後返回的是一個CachingExecutor
包含著一個SimpleExecutor
最後返回一個預設的DefaultSqlSession
DefaultSqlSession.java
public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
this.configuration = configuration;
this.executor = executor;
this.dirty = false;
this.autoCommit = autoCommit;
}
到這裡session也建立完成了之後就是獲取mapper執行查詢了
session.getMapper
DefaultSqlSession.java
@Override
public <T> T getMapper(Class<T> type) {
return configuration.<T>getMapper(type, this);
}
Configuration.java
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
MapperRegistry.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.java
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
MapperProxy.java
動態代理來實現mapper的方法呼叫
public class MapperProxy<T> implements InvocationHandler, Serializable {
private static final long serialVersionUID = -6424540398559729838L;
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache;
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
try {
return method.invoke(this, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
private MapperMethod cachedMapperMethod(Method method) {
MapperMethod mapperMethod = methodCache.get(method);
if (mapperMethod == null) {
mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
methodCache.put(method, mapperMethod);
}
return mapperMethod;
}
}
當我們執行userMapper.getUsers()的時候,透過動態代理進入invoke方法,之後獲取快取的方法,進入cachedMapperMethod
先找是否已經建立過這個方法的封裝類了,如果沒有則去建立
MapperMethod.java
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
this.command = new SqlCommand(config, mapperInterface, method);
this.method = new MethodSignature(config, method);
}
MapperMethod.java-SqlCommand.java 靜態內部類
public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
String statementName = mapperInterface.getName() + "." + method.getName();
MappedStatement ms = null;
//當程式走到這裡的時候就會為true
if (configuration.hasStatement(statementName)) {
ms = configuration.getMappedStatement(statementName);
}
///......
if (ms == null) {
//.....
} else {
name = ms.getId();
type = ms.getSqlCommandType();
if (type == SqlCommandType.UNKNOWN) {
throw new BindingException("Unknown execution method for: " + name);
}
}
}
Configuration.java
public boolean hasStatement(String statementName) {
return hasStatement(statementName, true);
}
public boolean hasStatement(String statementName, boolean validateIncompleteStatements) {
if (validateIncompleteStatements) {
buildAllStatements();
}
//主要這行
return mappedStatements.containsKey(statementName);
}
可能忘了它什麼時候新增進去的了,在那個小助手中的addMappedStatement方法,最後的時候進行的新增點我跳轉到addMappedStatement
那麼這裡就直接走到最後的if了,將name和type賦值給SqlCommand,方法結束
之後還有建立MethodSignature
MapperMethod.java-MethodSignature.java 靜態內部類
public MethodSignature(Configuration configuration, Method method) {
this.returnType = method.getReturnType();
this.returnsVoid = void.class.equals(this.returnType);
this.returnsMany = (configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray());
this.mapKey = getMapKey(method);
this.returnsMap = (this.mapKey != null);
//是否存在@Param註解
this.hasNamedParameters = hasNamedParams(method);
this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
this.params = Collections.unmodifiableSortedMap(getParams(method, this.hasNamedParameters));
}
走完這一系列後回到MapperProxy.java中的invoke方法,最後執行mapperMethod.execute(sqlSession, args)
MapperMethod.java
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
if(){
//.......
} else if (SqlCommandType.SELECT == command.getType()) {
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 {
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
}
} else if (SqlCommandType.FLUSH == command.getType()) {
result = sqlSession.flushStatements();
} else {
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;
}
根據command的型別,當前這個案例是SELECT,進入後判斷返回型別,當前是返回一個集合,returnsMany屬性為ture,進入到result = executeForMany(sqlSession, args);
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
List<E> result;
//封裝入參
Object param = method.convertArgsToSqlCommandParam(args);
//這個為false,沒有設定過邏輯分頁
if (method.hasRowBounds()) {
RowBounds rowBounds = method.extractRowBounds(args);
result = sqlSession.<E>selectList(command.getName(), param, rowBounds);
} else {
result = sqlSession.<E>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;
}
public Object convertArgsToSqlCommandParam(Object[] args) {
final int paramCount = params.size();
if (args == null || paramCount == 0) {
return null;
} else if (!hasNamedParameters && paramCount == 1) {
return args[params.keySet().iterator().next()];
} else {
final Map<String, Object> param = new ParamMap<Object>();
int i = 0;
for (Map.Entry<Integer, String> entry : params.entrySet()) {
param.put(entry.getValue(), args[entry.getKey().intValue()]);
// issue #71, add param names as param1, param2...but ensure backward compatibility
final String genericParamName = "param" + String.valueOf(i + 1);
if (!param.containsKey(genericParamName)) {
param.put(genericParamName, args[entry.getKey()]);
}
i++;
}
return param;
}
}
封裝mapper入參,沒有入參返回null,一個入參返回陣列,多個入參返回ParamMap
最終執行到result = sqlSession.<E>selectList(command.getName(), param);
DefaultSqlSession.java
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);
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();
}
}
warpCollection方法對入參同一封裝了一遍
DefaultSqlSession.java
private Object wrapCollection(final Object object) {
//如果是集合
if (object instanceof Collection) {
StrictMap<Object> map = new StrictMap<Object>();
map.put("collection", object);
if (object instanceof List) {
map.put("list", object);
}
return map;
//如果是陣列
} else if (object != null && object.getClass().isArray()) {
StrictMap<Object> map = new StrictMap<Object>();
map.put("array", object);
return map;
}
return object;
}
之後進入executor.query()
@Override
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);
}
第一步先獲取該方法對應的sql,入參型別,入參引數
ms.getBoundSql(parameter);
之後是建立快取key
@Override
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
if (closed) {
throw new ExecutorException("Executor was closed.");
}
CacheKey cacheKey = new CacheKey();
cacheKey.update(ms.getId());
cacheKey.update(Integer.valueOf(rowBounds.getOffset()));
cacheKey.update(Integer.valueOf(rowBounds.getLimit()));
cacheKey.update(boundSql.getSql());
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
// mimic DefaultParameterHandler logic
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)) {
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);
}
cacheKey.update(value);
}
}
if (configuration.getEnvironment() != null) {
// issue #176
cacheKey.update(configuration.getEnvironment().getId());
}
return cacheKey;
}
建立CacheKey作為快取key的封裝類,根據以下引數進行生成key
- MappedStatement的id
- rowBounds.getOffset() 跳過條數
- rowBounds.getLimit() 限制條數
- boundSql.getSql() 要執行的sql語句
之後遍歷
如果是陣列就將每個元素取出然後執行doUpdate,否則直接執行
CacheKey.java
private void doUpdate(Object object) {
int baseHashCode = object == null ? 1 : object.hashCode();
count++;
checksum += baseHashCode;
baseHashCode *= count;
hashcode = multiplier * hashcode + baseHashCode;
updateList.add(object);
}
最後走到query
CachingExecutor.java
@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, parameterObject, boundSql);
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
因為是首次進入,沒有快取,直接到BaseExecutor.query方法
BaseExecutor.java
@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;
}
進入後判斷如果queryStack==0並且當前MappedStatement宣告瞭需要清除快取,則去清除快取
@Options(flushCache= Options.FlushCachePolicy.TRUE)
<select id="getUser" resultType="user" flushCache="true" >
將queryStack++,根據CacheKey獲取對應快取,如果沒有則去查詢資料庫
queryStack的作用
BaseExecutor.java
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;
}
進入doQuery
SimpleExecutor.java
@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.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
建立statement執行類
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
switch (ms.getStatementType()) {
case STATEMENT:
delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case PREPARED:
delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case CALLABLE:
delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
default:
throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
}
}
這裡使用的是#{} MappedStatement的getStatementType返回為PREPARED 最後返回PreparedStatementHandler
之後解析prepareStatement
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection);
handler.parameterize(stmt);
return stmt;
}
獲取連線之後解析statement
@Override
public Statement prepare(Connection connection) throws SQLException {
Statement statement = null;
statement = instantiateStatement(connection);
//設定超時
setStatementTimeout(statement);
//設定獲取資料大小
setFetchSize(statement);
return statement;
//....
}
PreparedStatementHandler.java
@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
String sql = boundSql.getSql();
if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
String[] keyColumnNames = mappedStatement.getKeyColumns();
if (keyColumnNames == null) {
return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
} else {
return connection.prepareStatement(sql, keyColumnNames);
}
} else if (mappedStatement.getResultSetType() != null) {
//將會走到這裡返回preparedStatement
return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
} else {
return connection.prepareStatement(sql);
}
}
之後執行handler.parameterize(stmt);
DefaultParameterHandler.java
@Override
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
List<ParameterMapping> 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;
//判斷入參型別是否有對應的解析類
//去找TYPE_HANDLER_MAP中是否存在對應的型別,當前demo的入參型別是int
//在TypeHandlerRegistry類的無參構造中已經將常用的基本資料型別和引用資料型別放入到了TYPE_HANDLER_MAP中
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
//這裡獲取的是IntegerTypeHandler
TypeHandler typeHandler = parameterMapping.getTypeHandler();
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) {
jdbcType = configuration.getJdbcTypeForNull();
}
try {
//IntegerTypeHandler中的並沒有重寫setParameter
//進入BaseTypeHandler的setParameter
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);
}
}
}
}
}
BaseTypeHandler.java
@Override
public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
if (parameter == null) {
if (jdbcType == null) {
throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters.");
}
try {
ps.setNull(i, jdbcType.TYPE_CODE);
} catch (SQLException e) {
throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . " +
"Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. " +
"Cause: " + e, e);
}
} else {
try {
//進入這裡
setNonNullParameter(ps, i, parameter, jdbcType);
} catch (Exception e) {
throw new TypeException("Error setting non null for parameter #" + i + " with JdbcType " + jdbcType + " . " +
"Try setting a different JdbcType for this parameter or a different configuration property. " +
"Cause: " + e, e);
}
}
}
public class IntegerTypeHandler extends BaseTypeHandler<Integer> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, Integer parameter, JdbcType jdbcType)
throws SQLException {
ps.setInt(i, parameter);
}
}
最後回到SimpleExecutor類中的prepareStatement方法,返回PreparedStatement
@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());
//獲取返回的stmt,進行查詢
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
PreparedStatementHandler.java
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.<E> handleResultSets(ps);
}
DefaultResultSetHandler.java
@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
final List<Object> multipleResults = new ArrayList<Object>();
int resultSetCount = 0;
//先將結果進行封裝
ResultSetWrapper rsw = getFirstResultSet(stmt);
//.....
}
private ResultSetWrapper getFirstResultSet(Statement stmt) throws SQLException {
ResultSet rs = stmt.getResultSet();
while (rs == null) {
// move forward to get the first resultset in case the driver
// doesn't return the resultset as the first result (HSQLDB 2.1)
if (stmt.getMoreResults()) {
rs = stmt.getResultSet();
} else {
if (stmt.getUpdateCount() == -1) {
// no more results. Must be no resultset
break;
}
}
}
return rs != null ? new ResultSetWrapper(rs, configuration) : null;
}
ResultSetWrapper.java
將結果對應的查詢列名,資料庫列型別,類名
public ResultSetWrapper(ResultSet rs, Configuration configuration) throws SQLException {
super();
this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
this.resultSet = rs;
final ResultSetMetaData metaData = rs.getMetaData();
final int columnCount = metaData.getColumnCount();
for (int i = 1; i <= columnCount; i++) {
columnNames.add(configuration.isUseColumnLabel() ? metaData.getColumnLabel(i) : metaData.getColumnName(i));
jdbcTypes.add(JdbcType.forCode(metaData.getColumnType(i)));
classNames.add(metaData.getColumnClassName(i));
}
}
回到DefaultResultSetHandler的handleResultSets方法
@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
final List<Object> multipleResults = new ArrayList<Object>();
int resultSetCount = 0;
ResultSetWrapper rsw = getFirstResultSet(stmt);
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
//檢查查詢結果是否指定對應的類或resultMap
validateResultMapsCount(rsw, resultMapCount);
while (rsw != null && resultMapCount > resultSetCount) {
ResultMap resultMap = resultMaps.get(resultSetCount);
//處理結果,當前demo是封裝為User
handleResultSet(rsw, resultMap, multipleResults, null);
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
//.......
}
private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> 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());
}
}
private void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
//如果設定了resultMap,我們沒有設定,走else
if (resultMap.hasNestedResultMaps()) {
ensureNoRowBounds();
checkResultHandler();
handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
} else {
handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
}
}
private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
throws SQLException {
DefaultResultContext<Object> resultContext = new DefaultResultContext<Object>();
//根據rowBounds跳過行數
skipRows(rsw.getResultSet(), rowBounds);
//判斷是否應該處理更多 && 移動resultSet游標並且返回是否還有更多資料
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());
}
}
判斷當前結果上下文是否已經關閉,並且判斷當前結果集合總數是否小於rowBounds規定的限制
private boolean shouldProcessMoreRows(ResultContext<?> context, RowBounds rowBounds) throws SQLException {
return !context.isStopped() && context.getResultCount() < rowBounds.getLimit();
}
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
final ResultLoaderMap lazyLoader = new ResultLoaderMap();
//建立實體物件
Object resultObject = createResultObject(rsw, resultMap, lazyLoader, null);
if (resultObject != null && !typeHandlerRegistry.hasTypeHandler(resultMap.getType())) {
final MetaObject metaObject = configuration.newMetaObject(resultObject);
boolean foundValues = !resultMap.getConstructorResultMappings().isEmpty();
if (shouldApplyAutomaticMappings(resultMap, false)) {
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;
}
private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
final List<Class<?>> constructorArgTypes = new ArrayList<Class<?>>();
final List<Object> constructorArgs = new ArrayList<Object>();
//建立物件
final Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);
//.....
return resultObject;
}
private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix)
throws SQLException {
final Class<?> resultType = resultMap.getType();
//獲取類的後設資料 例如類存在幾個屬性,幾個getter 幾個setter等等類的基本資訊
final MetaClass metaType = MetaClass.forClass(resultType, reflectorFactory);
//判斷使用者是否指明瞭使用@ConstructorArgs來填充引數,當前demo沒有,所以這個集合為空
final List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings();
//判斷該類是否存在對應的型別處理器
if (typeHandlerRegistry.hasTypeHandler(resultType)) {
return createPrimitiveResultObject(rsw, resultMap, columnPrefix);
//判斷是否需要透過有參構造方式填充
} else if (!constructorMappings.isEmpty()) {
return createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, constructorArgs, columnPrefix);
//判斷如果當前類hi介面,或者存在預設構造
} else if (resultType.isInterface() || metaType.hasDefaultConstructor()) {
//裡面最終使用class.newInstance()返回物件
return objectFactory.create(resultType);
} else if (shouldApplyAutomaticMappings(resultMap, false)) {
return createByConstructorSignature(rsw, resultType, constructorArgTypes, constructorArgs, columnPrefix);
}
throw new ExecutorException("Do not know how to create an instance of " + resultType);
}
走完這些就會回到DefaultResultSetHandler中的 getRowValue方法,其中還有些方法沒有對類進行實際修改就省略了
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
final ResultLoaderMap lazyLoader = new ResultLoaderMap();
//回到這裡
Object resultObject = createResultObject(rsw, resultMap, lazyLoader, null);
//是否有對應的型別處理器
if (resultObject != null && !typeHandlerRegistry.hasTypeHandler(resultMap.getType())) {
final MetaObject metaObject = configuration.newMetaObject(resultObject);
//獲取是否透過構造填充引數,當前demo為空取反false
boolean foundValues = !resultMap.getConstructorResultMappings().isEmpty();
//判斷是否使用了自動對映
//https://mybatis.org/mybatis-3/zh/configuration.html#settings setting設定autoMappingBehavior
if (shouldApplyAutomaticMappings(resultMap, false)) {
//應用自動對映
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;
}
private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
//建立自動對映的列
List<UnMappedColumAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
boolean foundValues = false;
if (autoMapping.size() > 0) {
for (UnMappedColumAutoMapping mapping : autoMapping) {
final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);
// issue #377, call setter on nulls
//如果獲取的值不為空,或者允許設定空值
if (value != null || configuration.isCallSettersOnNulls()) {
//如果value不為空 或者值不是基本資料型別
if (value != null || !mapping.primitive) {
//反射設定值
metaObject.setValue(mapping.property, value);
}
foundValues = true;
}
}
}
return foundValues;
}
private List<UnMappedColumAutoMapping> createAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
final String mapKey = resultMap.getId() + ":" + columnPrefix;
List<UnMappedColumAutoMapping> autoMapping = autoMappingsCache.get(mapKey);
if (autoMapping == null) {
autoMapping = new ArrayList<UnMappedColumAutoMapping>();
//獲取沒有對映的欄位名稱
final List<String> unmappedColumnNames = rsw.getUnmappedColumnNames(resultMap, columnPrefix);
for (String columnName : unmappedColumnNames) {
String propertyName = columnName;
//...
//根據名稱獲取對應實體類的屬性名 isMapUnderscoreToCamelCase 是否開啟下劃線轉到駝峰
final String property = metaObject.findProperty(propertyName, configuration.isMapUnderscoreToCamelCase());
if (property != null && metaObject.hasSetter(property)) {
//獲取對應屬性的set方法的型別
final Class<?> propertyType = metaObject.getSetterType(property);
//尋找是否有對應的型別轉換
if (typeHandlerRegistry.hasTypeHandler(propertyType)) {
final TypeHandler<?> typeHandler = rsw.getTypeHandler(propertyType, columnName);
autoMapping.add(new UnMappedColumAutoMapping(columnName, property, typeHandler, propertyType.isPrimitive()));
}
}
}
//放入快取
autoMappingsCache.put(mapKey, autoMapping);
}
return autoMapping;
}
之後就是獲取ResultSet中的值填充到實體類中
BaseTypeHandler.java
@Override
public T getResult(ResultSet rs, String columnName) throws SQLException {
T result;
try {
result = getNullableResult(rs, columnName);
} catch (Exception e) {
throw new ResultMapException("Error attempting to get column '" + columnName + "' from result set. Cause: " + e, e);
}
if (rs.wasNull()) {
return null;
} else {
return result;
}
}
IntegerTypeHandler.java
@Override
public Integer getNullableResult(ResultSet rs, String columnName)
throws SQLException {
return rs.getInt(columnName);
}
這個時候物件已經填充完一個了
之後呼叫storeObject
DefaultResultSetHandler.java
private void storeObject(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue, ResultMapping parentMapping, ResultSet rs) throws SQLException {
if (parentMapping != null) {
linkToParents(rs, parentMapping, rowValue);
} else {
//進入這裡
callResultHandler(resultHandler, resultContext, rowValue);
}
}
private void callResultHandler(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue) {
//將物件儲存到resultContext
resultContext.nextResultObject(rowValue);
((ResultHandler<Object>)resultHandler).handleResult(resultContext);
}
DefaultResultContext.java
public void nextResultObject(T resultObject) {
resultCount++;
this.resultObject = resultObject;
}
之後執行handleResult
public class DefaultResultHandler implements ResultHandler<Object> {
private final List<Object> list;
//....
@Override
public void handleResult(ResultContext<? extends Object> context) {
list.add(context.getResultObject());
}
public List<Object> getResultList() {
return list;
}
}
至此一個物件的建立/填充就完成了,之後回到DefaultResultSetHandler的handleRowValuesForSimpleResultMap方法繼續下一個物件的迴圈
private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
throws SQLException {
DefaultResultContext<Object> resultContext = new DefaultResultContext<Object>();
skipRows(rsw.getResultSet(), rowBounds);
//一直把result的資料迴圈完
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的handleResultSets方法
@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
final List<Object> multipleResults = new ArrayList<Object>();
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);
//判斷是否存在下一個resultSet 一般一個statement只會返回一個resultSet
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
//......
return collapseSingleResultList(multipleResults);
}
private List<Object> collapseSingleResultList(List<Object> multipleResults) {
return multipleResults.size() == 1 ? (List<Object>) multipleResults.get(0) : multipleResults;
}
最後,返回這個list,整個方法結束