mybatis的总体执行流程如下:
读取mybatis配置文件,构建configuration对象,将mapper.xml配置文件与Mapper接口绑定,并将文件中select|insert|update|delete
配置,封装成一个个MappedStatement
对象,用于执行接口方法时读取配置信息。
创建数据库会话,用于执行数据库的增删改查
创建Mapper接口的代理对象MapperProxy
,这里使用的是JDK动态代理
对Mapper接口参数进行解析,这里分为两个阶段:形参解析和实参解析
解析动态SQL,主要是根据条件,进行SQL拼接
SQL解析后含有占位符,这里会设置SQL参数
SQL的执行,调用的是Statement的execute方法(和JDBC一样)
结果解析:根据返回值类型和参数映射,进行解析返回List
根据mybatis配置文件解析(inputStream就是读取配置文件的输入流):
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
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.
}
}
}
重点就是parser.parse()
,也就是调用XMLConfigBuilder#parse
:
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
重点:parseConfiguration(parser.evalNode("/configuration"))
这里的parser属性是XPathParser对象,在构造XMLConfigBuilder
对象时创建的:
public XMLConfigBuilder(Reader reader, String environment, Properties props) {
this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props);
}
可以看到这里解析分成了两步:
解析根节点configuration
:parser.evalNode("/configuration")
再根据根节点,解析整个配置文件:parseConfiguration(XNode root)
解析根节点,主要是利用XPathParse
进行解析,这里不再深入。主要还是看配置各个节点的解析:
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
// 解析properties节点,存储在Configuration.variables属性中
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
loadCustomLogImpl(settings);
// 解析别名,存储在Configuration.typeAliasRegistry.typeAliases属性中(含内置别名)
typeAliasesElement(root.evalNode("typeAliases"));
// 解析插件,存储在Configuration.interceptorChain.interceptors属性中
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
// 解析环境配置,主要是数据库连接属性以及事务管理器,存储在Configuration.environment属性中
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
// 解析类型处理器,负责处理jdbc类型和Java类型之间的转换,存储在Configuration.typeHandlerRegistry.allTypeHandlersMap属性中(含内置类型处理器)
typeHandlerElement(root.evalNode("typeHandlers"));
// 解析mapper配置:
// (1)注册mapper接口及其代理对象工厂,存储在Configuration.mapperRegistry.knownMappers属性中
// (2)解析mapper.xml文件,将select|insert|update|delete标签及其内容,解析成MappedStatement对象,存储在Configuration.mappedStatements属性中
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
// 解析别名,存储在Configuration.typeAliasRegistry.typeAliases属性中(含内置别名)
typeAliasesElement(root.evalNode("typeAliases"));
别名主要是为了书写的简便,不用写类型的全限定类名,在开发中,经常会给实体类增加别名,直接写类名即可,比如没有注册类型别名,需要这样写:
<select id="getUser" resultType="com.qxf.pojo.User">
select u.id,u.username,u.password,u.is_valid
from t_user u
where u.username = #{username} and u.password = #{password}
select>
可以看到resultType
需要写全限定类名com.qxf.pojo.User
。
但是如果设置了类型别名:
<typeAliases>
<package name="com.qxf.pojo">package>
typeAliases>
就可以直接使用类名:
<select id="getUser" resultType="User">
select u.id,u.username,u.password,u.is_valid
from t_user u
where u.username = #{username} and u.password = #{password}
select>
别名大小写不敏感,因为注册的时候,都会转换为小写TypeAliasRegistry#registerAlias:
public void registerAlias(String alias, Class<?> value) {
if (alias == null) {
throw new TypeException("The parameter alias cannot be null");
}
// 别名转换为小写
String key = alias.toLowerCase(Locale.ENGLISH);
if (typeAliases.containsKey(key) && typeAliases.get(key) != null && !typeAliases.get(key).equals(value)) {
throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + typeAliases.get(key).getName() + "'.");
}
typeAliases.put(key, value);
}
除了我们注册的类型别名,mybatis帮我们内置了如下别名:
public TypeAliasRegistry() {
registerAlias("string", String.class);
registerAlias("byte", Byte.class);
registerAlias("long", Long.class);
registerAlias("short", Short.class);
registerAlias("int", Integer.class);
registerAlias("integer", Integer.class);
registerAlias("double", Double.class);
registerAlias("float", Float.class);
registerAlias("boolean", Boolean.class);
registerAlias("byte[]", Byte[].class);
registerAlias("long[]", Long[].class);
registerAlias("short[]", Short[].class);
registerAlias("int[]", Integer[].class);
registerAlias("integer[]", Integer[].class);
registerAlias("double[]", Double[].class);
registerAlias("float[]", Float[].class);
registerAlias("boolean[]", Boolean[].class);
registerAlias("_byte", byte.class);
registerAlias("_long", long.class);
registerAlias("_short", short.class);
registerAlias("_int", int.class);
registerAlias("_integer", int.class);
registerAlias("_double", double.class);
registerAlias("_float", float.class);
registerAlias("_boolean", boolean.class);
registerAlias("_byte[]", byte[].class);
registerAlias("_long[]", long[].class);
registerAlias("_short[]", short[].class);
registerAlias("_int[]", int[].class);
registerAlias("_integer[]", int[].class);
registerAlias("_double[]", double[].class);
registerAlias("_float[]", float[].class);
registerAlias("_boolean[]", boolean[].class);
registerAlias("date", Date.class);
registerAlias("decimal", BigDecimal.class);
registerAlias("bigdecimal", BigDecimal.class);
registerAlias("biginteger", BigInteger.class);
registerAlias("object", Object.class);
registerAlias("date[]", Date[].class);
registerAlias("decimal[]", BigDecimal[].class);
registerAlias("bigdecimal[]", BigDecimal[].class);
registerAlias("biginteger[]", BigInteger[].class);
registerAlias("object[]", Object[].class);
registerAlias("map", Map.class);
registerAlias("hashmap", HashMap.class);
registerAlias("list", List.class);
registerAlias("arraylist", ArrayList.class);
registerAlias("collection", Collection.class);
registerAlias("iterator", Iterator.class);
registerAlias("ResultSet", ResultSet.class);
}
除了这些内置别名外,在创建Configuration对象时,也会注册一些类型别名:
public Configuration() {
typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
typeAliasRegistry.registerAlias("LRU", LruCache.class);
typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
typeAliasRegistry.registerAlias("WEAK", WeakCache.class);
typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);
typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);
typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);
typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);
}
// 解析插件,存储在Configuration.interceptorChain.interceptors属性中
pluginElement(root.evalNode("plugins"));
插件的注册和管理是通过InterceptorChain进行的,它管理了所有的插件。
public class InterceptorChain {
private final List<Interceptor> interceptors = new ArrayList<>();
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
public void addInterceptor(Interceptor interceptor) {
interceptors.add(interceptor);
}
public List<Interceptor> getInterceptors() {
return Collections.unmodifiableList(interceptors);
}
}
MyBatis的插件类型主要分为四种,分别是:
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 = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
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 = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
// 执行插件功能,增强ParameterHandlerer
parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
return parameterHandler;
}
public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
ResultHandler resultHandler, BoundSql boundSql) {
ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
// 执行插件功能,增强ResultSetHandler
resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
return resultSetHandler;
}
这些插件可以对MyBatis的四大核心对象进行拦截,以增强核心对象的功能。
MyBatis的常见插件包括:
todo
// 解析环境配置,主要是数据库连接属性以及事务管理器,存储在Configuration.environment属性中
environmentsElement(root.evalNode("environments"));
主要是解析配置文件的environments
节点:
<environments default="dev">
<environment id="dev">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis_test?serverTimezone=UTC&useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="19930919"/>
dataSource>
environment>
environments>
可以看到,这里可以配置多个环境,每个环境可以配置自己的事务管理器和数据源配置
private void environmentsElement(XNode context) throws Exception {
if (context != null) {
if (environment == null) {
// 读取默认环境id
environment = context.getStringAttribute("default");
}
for (XNode child : context.getChildren()) {
String id = child.getStringAttribute("id");
// 激活默认环境:default的值等于environment节点id属性值
if (isSpecifiedEnvironment(id)) {
// 根据配置的事务管理器类型:这里配置的type="JDBC",根据Configuration对象创建的注册的别名:
// typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class),可以拿到JdbcTransactionFactory
TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
// 同理,根据数据源类型,这里配置的type="POOLED",根据Configuration对象创建的注册的别名:
// typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class),可以拿到PooledDataSourceFactory
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());
}
}
}
}
这里用到的别名,可以参考Configuration对象的构造函数注册的别名。
// 解析类型处理器,负责处理jdbc类型和Java类型之间的转换,存储在Configuration.typeHandlerRegistry.allTypeHandlersMap属性中(含内置类型处理器)
typeHandlerElement(root.evalNode("typeHandlers"));
这里主要解析配置的自定义类型处理器,目前没有自定义。
注册逻辑:
private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {
if (javaType != null) {
Map<JdbcType, TypeHandler<?>> map = typeHandlerMap.get(javaType);
if (map == null || map == NULL_TYPE_HANDLER_MAP) {
map = new HashMap<>();
typeHandlerMap.put(javaType, map);
}
map.put(jdbcType, handler);
}
allTypeHandlersMap.put(handler.getClass(), handler);
}
先看内置的类型处理器:
public TypeHandlerRegistry() {
register(Boolean.class, new BooleanTypeHandler());
register(boolean.class, new BooleanTypeHandler());
register(JdbcType.BOOLEAN, new BooleanTypeHandler());
register(JdbcType.BIT, new BooleanTypeHandler());
register(Byte.class, new ByteTypeHandler());
register(byte.class, new ByteTypeHandler());
register(JdbcType.TINYINT, new ByteTypeHandler());
register(Short.class, new ShortTypeHandler());
register(short.class, new ShortTypeHandler());
register(JdbcType.SMALLINT, new ShortTypeHandler());
register(Integer.class, new IntegerTypeHandler());
register(int.class, new IntegerTypeHandler());
register(JdbcType.INTEGER, new IntegerTypeHandler());
register(Long.class, new LongTypeHandler());
register(long.class, new LongTypeHandler());
register(Float.class, new FloatTypeHandler());
register(float.class, new FloatTypeHandler());
register(JdbcType.FLOAT, new FloatTypeHandler());
register(Double.class, new DoubleTypeHandler());
register(double.class, new DoubleTypeHandler());
register(JdbcType.DOUBLE, new DoubleTypeHandler());
register(Reader.class, new ClobReaderTypeHandler());
register(String.class, new StringTypeHandler());
register(String.class, JdbcType.CHAR, new StringTypeHandler());
register(String.class, JdbcType.CLOB, new ClobTypeHandler());
register(String.class, JdbcType.VARCHAR, new StringTypeHandler());
register(String.class, JdbcType.LONGVARCHAR, new StringTypeHandler());
register(String.class, JdbcType.NVARCHAR, new NStringTypeHandler());
register(String.class, JdbcType.NCHAR, new NStringTypeHandler());
register(String.class, JdbcType.NCLOB, new NClobTypeHandler());
register(JdbcType.CHAR, new StringTypeHandler());
register(JdbcType.VARCHAR, new StringTypeHandler());
register(JdbcType.CLOB, new ClobTypeHandler());
register(JdbcType.LONGVARCHAR, new StringTypeHandler());
register(JdbcType.NVARCHAR, new NStringTypeHandler());
register(JdbcType.NCHAR, new NStringTypeHandler());
register(JdbcType.NCLOB, new NClobTypeHandler());
register(Object.class, JdbcType.ARRAY, new ArrayTypeHandler());
register(JdbcType.ARRAY, new ArrayTypeHandler());
register(BigInteger.class, new BigIntegerTypeHandler());
register(JdbcType.BIGINT, new LongTypeHandler());
register(BigDecimal.class, new BigDecimalTypeHandler());
register(JdbcType.REAL, new BigDecimalTypeHandler());
register(JdbcType.DECIMAL, new BigDecimalTypeHandler());
register(JdbcType.NUMERIC, new BigDecimalTypeHandler());
register(InputStream.class, new BlobInputStreamTypeHandler());
register(Byte[].class, new ByteObjectArrayTypeHandler());
register(Byte[].class, JdbcType.BLOB, new BlobByteObjectArrayTypeHandler());
register(Byte[].class, JdbcType.LONGVARBINARY, new BlobByteObjectArrayTypeHandler());
register(byte[].class, new ByteArrayTypeHandler());
register(byte[].class, JdbcType.BLOB, new BlobTypeHandler());
register(byte[].class, JdbcType.LONGVARBINARY, new BlobTypeHandler());
register(JdbcType.LONGVARBINARY, new BlobTypeHandler());
register(JdbcType.BLOB, new BlobTypeHandler());
register(Object.class, unknownTypeHandler);
register(Object.class, JdbcType.OTHER, unknownTypeHandler);
register(JdbcType.OTHER, unknownTypeHandler);
register(Date.class, new DateTypeHandler());
register(Date.class, JdbcType.DATE, new DateOnlyTypeHandler());
register(Date.class, JdbcType.TIME, new TimeOnlyTypeHandler());
register(JdbcType.TIMESTAMP, new DateTypeHandler());
register(JdbcType.DATE, new DateOnlyTypeHandler());
register(JdbcType.TIME, new TimeOnlyTypeHandler());
register(java.sql.Date.class, new SqlDateTypeHandler());
register(java.sql.Time.class, new SqlTimeTypeHandler());
register(java.sql.Timestamp.class, new SqlTimestampTypeHandler());
register(String.class, JdbcType.SQLXML, new SqlxmlTypeHandler());
register(Instant.class, new InstantTypeHandler());
register(LocalDateTime.class, new LocalDateTimeTypeHandler());
register(LocalDate.class, new LocalDateTypeHandler());
register(LocalTime.class, new LocalTimeTypeHandler());
register(OffsetDateTime.class, new OffsetDateTimeTypeHandler());
register(OffsetTime.class, new OffsetTimeTypeHandler());
register(ZonedDateTime.class, new ZonedDateTimeTypeHandler());
register(Month.class, new MonthTypeHandler());
register(Year.class, new YearTypeHandler());
register(YearMonth.class, new YearMonthTypeHandler());
register(JapaneseDate.class, new JapaneseDateTypeHandler());
// issue #273
register(Character.class, new CharacterTypeHandler());
register(char.class, new CharacterTypeHandler());
}
类型处理器,都实现了TypeHandler
接口:
public interface TypeHandler<T> {
/**
* @description 设置参数
* @author qiuxinfa
* @time 2023/11/29 23:50
* @param ps
* @param i SQL中占位符的下标
* @param parameter Java参数
* @param jdbcType jdbc参数
* @throws SQLException
*/
void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
/**
* @description 从查询结果集中,根据列名获取值
* @author qiuxinfa
* @time 2023/11/29 23:52
* @param rs 结果集
* @param columnName 列名
* @return T 返回值
* @throws SQLException
*/
T getResult(ResultSet rs, String columnName) throws SQLException;
/**
* @description 从查询结果集中,根据下标获取值
* @author qiuxinfa
* @time 2023/11/29 23:52
* @param rs 结果集
* @param columnIndex 下标
* @return T 返回值
* @throws SQLException
*/
T getResult(ResultSet rs, int columnIndex) throws SQLException;
T getResult(CallableStatement cs, int columnIndex) throws SQLException;
}
mapperElement(root.evalNode("mappers"));
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
// mapper的4种配置方式
for (XNode child : parent.getChildren()) {
// 1. 配置包名
if ("package".equals(child.getName())) {
String mapperPackage = child.getStringAttribute("name");
// 根据包名,批量注册mapper接口及其代理对象工厂
configuration.addMappers(mapperPackage);
} else {
// 2. 配置资源的位置
String resource = child.getStringAttribute("resource");
// 3. 配置资源的url
String url = child.getStringAttribute("url");
// 4. 配置接口类名:和package类似,只不过package是批量配置
String mapperClass = child.getStringAttribute("class");
if (resource != null && url == null && mapperClass == null) {
ErrorContext.instance().resource(resource);
InputStream inputStream = Resources.getResourceAsStream(resource);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
// 解析mapper.xml文件
mapperParser.parse();
} else if (resource == null && url != null && mapperClass == null) {
ErrorContext.instance().resource(url);
InputStream inputStream = Resources.getUrlAsStream(url);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
// 解析mapper.xml文件
mapperParser.parse();
} else if (resource == null && url == null && mapperClass != null) {
Class<?> mapperInterface = Resources.classForName(mapperClass);
// 注册mapper接口及其代理对象工厂
configuration.addMapper(mapperInterface);
} else {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
}
}
}
}
配置包名,调用的是Configuration对象的addMappers方法:
// 根据包名,批量注册mapper接口及其代理对象工厂
configuration.addMappers(mapperPackage);
最终会委托给MapperRegistry#addMappers:
public void addMappers(String packageName) {
addMappers(packageName, Object.class);
}
调用重载方法:
public void addMappers(String packageName, Class<?> superType) {
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
// 将package包及其子包的类,存放在Set> matches属性种
resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
// 拿到package包及其子包下的类
Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
// 遍历所有的类,执行注册逻辑
for (Class<?> mapperClass : mapperSet) {
addMapper(mapperClass);
}
}
注册逻辑:
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 {
// 注册mapper接口及创建mapper接口代理对象的工厂
knownMappers.put(type, new MapperProxyFactory<>(type));
// It's important that the type is added before the parser is run
// otherwise the binding may automatically be attempted by the
// mapper parser. If the type is already known, it won't try.
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
// 解析接口对应的xml映射文件
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
到此,创建mapper接口代理对象的工厂,已经注册到了MapperRegistry.knownMappers
现在看下解析mapper.xml文件的过程:
// 解析接口对应的xml映射文件
parser.parse();
调用的是MapperAnnotationBuilder#parse:
public void parse() {
String resource = type.toString();
// 判断mapper.xml资源是否加载过了
if (!configuration.isResourceLoaded(resource)) {
// 加载接口对应的xml资源
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.incompleteMethods
configuration.addIncompleteMethod(new MethodResolver(this, method));
}
}
}
// 对解析出现异常的方法,重新解析一次
parsePendingMethods();
}
// 加载接口对应的xml资源
loadXmlResource();
private void loadXmlResource() {
// Spring may not know the real resource name so we check a flag
// to prevent loading again a resource twice
// this flag is set at XMLMapperBuilder#bindMapperForNamespace
if (!configuration.isResourceLoaded("namespace:" + type.getName())) {
// 根据接口,找到xml资源路径
String xmlResource = type.getName().replace('.', '/') + ".xml";
// #1347
InputStream inputStream = type.getResourceAsStream("/" + xmlResource);
if (inputStream == null) {
// Search XML mapper that is not in the module but in the classpath.
try {
inputStream = Resources.getResourceAsStream(type.getClassLoader(), xmlResource);
} catch (IOException e2) {
// ignore, resource is not required
}
}
if (inputStream != null) {
XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName());
// 解析xml
xmlParser.parse();
}
}
}
重点来到XMLMapperBuilder#parse
public void parse() {
// 避免重复解析
if (!configuration.isResourceLoaded(resource)) {
// 解析mapper.xml文件内容,转换为对象存储
configurationElement(parser.evalNode("/mapper"));
configuration.addLoadedResource(resource);
// 通过mapper.xml文件的namespace拿到接口的全限定类名,注册mapper接口及其代理对象工厂,存储在Configuration.mapperRegistry.knownMappers属性中
// 对于配置接口或者配置包名的方式,在此之前,已经注册了,这里主要是针对先解析mapper.xml的方式,需要执行注册
bindMapperForNamespace();
}
// 对解析异常的进行重试
parsePendingResultMaps();
parsePendingCacheRefs();
parsePendingStatements();
}
重点看下解析逻辑:
// 解析mapper.xml文件内容,转换为对象存储
configurationElement(parser.evalNode("/mapper"));
首先解析mapper.xml文件的mapper标签节点,再以此为根节点,解析整个mapper.xml:
private void configurationElement(XNode context) {
try {
String namespace = context.getStringAttribute("namespace");
if (namespace == null || namespace.equals("")) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
builderAssistant.setCurrentNamespace(namespace);
cacheRefElement(context.evalNode("cache-ref"));
cacheElement(context.evalNode("cache"));
// 解析parameterMap,存在configuration的parameterMaps属性中
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
// 解析resultMap,存在configuration的resultMaps属性中
resultMapElements(context.evalNodes("/mapper/resultMap"));
// 解析sql片段,存在XMLMapperBuilder的sqlFragments属性中
sqlElement(context.evalNodes("/mapper/sql"));
// 解析select|insert|update|delete为XMLStatementBuilder对象,存在configuration的mappedStatements属性中
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} catch (Exception e) {
throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
}
}
解析resultMap
// 解析resultMap,存在configuration的resultMaps属性中
resultMapElements(context.evalNodes("/mapper/resultMap"));
这里主要看:
private void resultMapElements(List<XNode> list) throws Exception {
// 遍历解析每个ResultMap节点
for (XNode resultMapNode : list) {
try {
resultMapElement(resultMapNode);
} catch (IncompleteElementException e) {
// ignore, it will be retried
}
}
}
解析过程:
private ResultMap resultMapElement(XNode resultMapNode) throws Exception {
return resultMapElement(resultMapNode, Collections.emptyList(), null);
}
调用的是重载的方法:
private ResultMap resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings, Class<?> enclosingType) throws Exception {
ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier());
String type = resultMapNode.getStringAttribute("type",
resultMapNode.getStringAttribute("ofType",
resultMapNode.getStringAttribute("resultType",
resultMapNode.getStringAttribute("javaType"))));
Class<?> typeClass = resolveClass(type);
if (typeClass == null) {
typeClass = inheritEnclosingType(resultMapNode, enclosingType);
}
Discriminator discriminator = null;
List<ResultMapping> resultMappings = new ArrayList<>();
resultMappings.addAll(additionalResultMappings);
// 拿到resultMap的所有子节点,遍历解析实体属性和数据库字段的映射关系ResultMapping
List<XNode> resultChildren = resultMapNode.getChildren();
for (XNode resultChild : resultChildren) {
if ("constructor".equals(resultChild.getName())) {
processConstructorElement(resultChild, typeClass, resultMappings);
} else if ("discriminator".equals(resultChild.getName())) {
discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings);
} else {
List<ResultFlag> flags = new ArrayList<>();
if ("id".equals(resultChild.getName())) {
flags.add(ResultFlag.ID);
}
// 将解析的ResultMapping添加到resultMappings
resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags));
}
}
String id = resultMapNode.getStringAttribute("id",
resultMapNode.getValueBasedIdentifier());
String extend = resultMapNode.getStringAttribute("extends");
Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping");
// 根据resultMappings等信息,构建ResultMap
ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping);
try {
return resultMapResolver.resolve();
} catch (IncompleteElementException e) {
configuration.addIncompleteResultMap(resultMapResolver);
throw e;
}
}
这里的重点是解析ResultMapping
,其实是解析我们配置的映射关系
<resultMap id="userMap" type="User">
<id property="id" column="id">id>
<result property="username" column="username">result>
<result property="password" column="password">result>
<result property="isValid" column="is_valid">result>
<collection property="blogs" javaType="list" ofType="Blog">
<id property="id" column="blog_id">id>
<result property="title" column="title">result>
<result property="userId" column="user_id">result>
collection>
resultMap>
private ResultMapping buildResultMappingFromContext(XNode context, Class<?> resultType, List<ResultFlag> flags) throws Exception {
String property;
if (flags.contains(ResultFlag.CONSTRUCTOR)) {
property = context.getStringAttribute("name");
} else {
property = context.getStringAttribute("property");
}
String column = context.getStringAttribute("column");
String javaType = context.getStringAttribute("javaType");
String jdbcType = context.getStringAttribute("jdbcType");
String nestedSelect = context.getStringAttribute("select");
String nestedResultMap = context.getStringAttribute("resultMap",
processNestedResultMappings(context, Collections.emptyList(), resultType));
String notNullColumn = context.getStringAttribute("notNullColumn");
String columnPrefix = context.getStringAttribute("columnPrefix");
String typeHandler = context.getStringAttribute("typeHandler");
String resultSet = context.getStringAttribute("resultSet");
String foreignColumn = context.getStringAttribute("foreignColumn");
boolean lazy = "lazy".equals(context.getStringAttribute("fetchType", configuration.isLazyLoadingEnabled() ? "lazy" : "eager"));
Class<?> javaTypeClass = resolveClass(javaType);
Class<? extends TypeHandler<?>> typeHandlerClass = resolveClass(typeHandler);
JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
return builderAssistant.buildResultMapping(resultType, property, column, javaTypeClass, jdbcTypeEnum, nestedSelect, nestedResultMap, notNullColumn, columnPrefix, typeHandlerClass, flags, resultSet, foreignColumn, lazy);
}
这里就是读取配置的属性信息,最后构建ResultMapping。
MapperBuilderAssistant#addResultMap构建ResultMapp:
public ResultMap addResultMap(
String id,
Class<?> type,
String extend,
Discriminator discriminator,
List<ResultMapping> resultMappings,
Boolean autoMapping) {
id = applyCurrentNamespace(id, false);
extend = applyCurrentNamespace(extend, true);
if (extend != null) {
if (!configuration.hasResultMap(extend)) {
throw new IncompleteElementException("Could not find a parent resultmap with id '" + extend + "'");
}
ResultMap resultMap = configuration.getResultMap(extend);
List<ResultMapping> extendedResultMappings = new ArrayList<>(resultMap.getResultMappings());
extendedResultMappings.removeAll(resultMappings);
// Remove parent constructor if this resultMap declares a constructor.
boolean declaresConstructor = false;
for (ResultMapping resultMapping : resultMappings) {
if (resultMapping.getFlags().contains(ResultFlag.CONSTRUCTOR)) {
declaresConstructor = true;
break;
}
}
if (declaresConstructor) {
extendedResultMappings.removeIf(resultMapping -> resultMapping.getFlags().contains(ResultFlag.CONSTRUCTOR));
}
resultMappings.addAll(extendedResultMappings);
}
// 根据resultMappings信息,解析mappedColumns、mappedProperties、idResultMappings、constructorResultMappings、propertyResultMappings属性值
ResultMap resultMap = new ResultMap.Builder(configuration, id, type, resultMappings, autoMapping)
.discriminator(discriminator)
.build();
// 将ResultMap存放到Configuration对象中
configuration.addResultMap(resultMap);
return resultMap;
}
解析sql片段
// 解析sql片段,存在XMLMapperBuilder的sqlFragments属性中
sqlElement(context.evalNodes("/mapper/sql"));
遍历所有的sql节点:
private void sqlElement(List<XNode> list) {
if (configuration.getDatabaseId() != null) {
sqlElement(list, configuration.getDatabaseId());
}
sqlElement(list, null);
}
调用重载的方法:
private void sqlElement(List<XNode> list, String requiredDatabaseId) {
for (XNode context : list) {
String databaseId = context.getStringAttribute("databaseId");
String id = context.getStringAttribute("id");
// id = currentNamespace + "." + id;
id = builderAssistant.applyCurrentNamespace(id, false);
if (databaseIdMatchesCurrent(id, databaseId, requiredDatabaseId)) {
// 存放在sqlFragments属性中
sqlFragments.put(id, context);
}
}
}
MappedStatement的构建,主要看这段逻辑:
// 解析select|insert|update|delete为XMLStatementBuilder对象,存在configuration的mappedStatements属性中
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
拿到所有的elect|insert|update|delete节点进行解析:
private void buildStatementFromContext(List<XNode> list) {
if (configuration.getDatabaseId() != null) {
buildStatementFromContext(list, configuration.getDatabaseId());
}
buildStatementFromContext(list, null);
}
调用重载方法:
private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
for (XNode context : list) {
final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
try {
// 遍历所有的节点,调用XMLStatementBuilder#parseStatementNode解析
statementParser.parseStatementNode();
} catch (IncompleteElementException e) {
// 解析异常的,放入的未完成的集合中,进行重试
configuration.addIncompleteStatement(statementParser);
}
}
}
看XMLStatementBuilder#parseStatementNode是如何解析的:
public void parseStatementNode() {
// 拿到节点id,跟接口方法名一一对应
String id = context.getStringAttribute("id");
String databaseId = context.getStringAttribute("databaseId");
if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
return;
}
// 拿到节点名称,据此判断是select|insert|update|delete中的哪种操作
String nodeName = context.getNode().getNodeName();
SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
boolean useCache = context.getBooleanAttribute("useCache", isSelect);
boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);
// Include Fragments before parsing
// 解析包含的sql片段
XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
includeParser.applyIncludes(context.getNode());
// 拿到参数类型
String parameterType = context.getStringAttribute("parameterType");
Class<?> parameterTypeClass = resolveClass(parameterType);
String lang = context.getStringAttribute("lang");
LanguageDriver langDriver = getLanguageDriver(lang);
// Parse selectKey after includes and remove them.
processSelectKeyNodes(id, parameterTypeClass, langDriver);
// Parse the SQL (pre: and were parsed and removed)
KeyGenerator keyGenerator;
String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
// 最终keyStatementId = currentNamespace + "." + id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
if (configuration.hasKeyGenerator(keyStatementId)) {
keyGenerator = configuration.getKeyGenerator(keyStatementId);
} else {
keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
}
// 拿到sql主体
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
// 尝试获取statementType属性,默认是PREPARED,即PreparedStatement
StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
Integer fetchSize = context.getIntAttribute("fetchSize");
Integer timeout = context.getIntAttribute("timeout");
String parameterMap = context.getStringAttribute("parameterMap");
String resultType = context.getStringAttribute("resultType");
Class<?> resultTypeClass = resolveClass(resultType);
String resultMap = context.getStringAttribute("resultMap");
String resultSetType = context.getStringAttribute("resultSetType");
ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
if (resultSetTypeEnum == null) {
resultSetTypeEnum = configuration.getDefaultResultSetType();
}
String keyProperty = context.getStringAttribute("keyProperty");
String keyColumn = context.getStringAttribute("keyColumn");
String resultSets = context.getStringAttribute("resultSets");
// 根据解析的属性,添加MappedStatement到Configuration中
builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
resultSetTypeEnum, flushCache, useCache, resultOrdered,
keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
核心当然是最后一行代码MapperBuilderAssistant#addMappedStatement:
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 = currentNamespace + "." + id;
id = applyCurrentNamespace(id, false);
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
// 根据解析的参数,构建MappedStatement
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)
.resultSets(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的构建
MappedStatement statement = statementBuilder.build();
// 添加到Configuration对象中
configuration.addMappedStatement(statement);
return statement;
}
至此,配置的解析基本完成。
与配置包名类似,只不过是先解析mapper.xml文件,再解析mapper接口,刚好与配置包名的解析步骤相反。
没用过这种方式。
与配置包名的逻辑类似。
Configuration对象作为构造参数,直接new了一个DefaultSqlSessionFactory
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
// SqlSession工厂创建SqlSession,executor默认为SimpleExecutor,被包装成CachingExecutor
SqlSession sqlSession = factory.openSession();
调用的是DefaultSqlSessionFactory#openSession
public SqlSession openSession() {
// 默认ExecutorType.SIMPLE
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
接着调用重载方法,开启数据库会话:
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
// 获取配置的环境信息,拿到事务工厂和数据源配置
final Environment environment = configuration.getEnvironment();
// 根据配置的 ,拿到JdbcTransactionFactory
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
// 根据数据源、事务隔离级别、是否自动提交,创建事务JdbcTransaction
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
// 根据事务和执行器类型创建执行器,默认的Executor是SimpleExecutor,但是会被CachingExecutor包装,并且调用插件方法增强
final Executor executor = configuration.newExecutor(tx, execType);
// 根据configuration和Executor创建DefaultSqlSession
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();
}
}
获取mapper接口的代理对象:
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
调用的是上一环节创建的DefaultSqlSession#getMapper:
public <T> T getMapper(Class<T> type) {
return configuration.getMapper(type, this);
}
委托给了Configuration#getMapper:
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
又委托给了MapperRegistry#getMapper:
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
// 从knownMappers里面获取(解析mapper接口时存入的)
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:
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
可以看到,这里利用的是JDK动态代理,而MapperProxy
实现了InvocationHandler
接口,所有代理对象都会调用其invoke方法。
执行mapper接口方法:
User user = mapper.getUserAndBlogByUserId("123");
根据第3部分(创建Mapper接口的代理对象)可以知道,当我们调用mapper接口的方法时,实际调用的是MapperProxy#invoke:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (method.isDefault()) {
if (privateLookupInMethod == null) {
return invokeDefaultMethodJava8(proxy, method, args);
} else {
return invokeDefaultMethodJava9(proxy, method, args);
}
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
// 1. 根据接口方法,创建MapperMethod
final MapperMethod mapperMethod = cachedMapperMethod(method);
// 2. 执行MapperMethod#execute,并返回结果
return mapperMethod.execute(sqlSession, args);
}
创建MapperMethod
private MapperMethod cachedMapperMethod(Method method) {
return methodCache.computeIfAbsent(method,
k -> new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
}
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
// 构建SqlCommand:包含statementId和sql命令类型(select|insert|update|delete)
this.command = new SqlCommand(config, mapperInterface, method);
// 构建MethodSignature:方法返回值类型和参数解析器
this.method = new MethodSignature(config, mapperInterface, method);
}
重点看下MethodSignature的构造方法:
public MethodSignature(Configuration configuration, Class<?> mapperInterface, Method method) {
// 解析返回值类型
Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface);
if (resolvedReturnType instanceof Class<?>) {
this.returnType = (Class<?>) resolvedReturnType;
} else if (resolvedReturnType instanceof ParameterizedType) {
this.returnType = (Class<?>) ((ParameterizedType) resolvedReturnType).getRawType();
} else {
this.returnType = method.getReturnType();
}
this.returnsVoid = void.class.equals(this.returnType);
this.returnsMany = configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray();
this.returnsCursor = Cursor.class.equals(this.returnType);
this.returnsOptional = Optional.class.equals(this.returnType);
this.mapKey = getMapKey(method);
this.returnsMap = this.mapKey != null;
this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
// 参数名称解析器
this.paramNameResolver = new ParamNameResolver(configuration, method);
}
在创建MapperMethod时,需要创建MethodSignature用来解析返回值类型和参数解析,而参数解析利用的是ParamNameResolver,所以在执行:
// 2. 执行MapperMethod#execute,并返回结果
return mapperMethod.execute(sqlSession, args);
在执行MapperMethod#execute时,要先解析参数:
Object param = method.convertArgsToSqlCommandParam(args);
public Object convertArgsToSqlCommandParam(Object[] args) {
return paramNameResolver.getNamedParams(args);
}
而解析则是委托给了ParamNameResolver#getNamedParams:
public Object getNamedParams(Object[] args) {
// names属性存储:key为参数的下标(0开始),value是参数的名称
final int paramCount = names.size();
if (args == null || paramCount == 0) {
return null;
} else if (!hasParamAnnotation && paramCount == 1) {
// 1. 如果没有标注@Param注解,并且只有一个参数,就直接返回实参的第一个参数
return args[names.firstKey()];
} else {
final Map<String, Object> param = new ParamMap<>();
int i = 0;
// 2. 解析实参param:key为参数名称,value为参数值
for (Map.Entry<Integer, String> entry : names.entrySet()) {
param.put(entry.getValue(), args[entry.getKey()]);
// 同时也会放入通用的参数:param1->value1,param2->value2
// add generic param names (param1, param2, ...)
final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);
// ensure not to overwrite parameter named with @Param
if (!names.containsValue(genericParamName)) {
param.put(genericParamName, args[entry.getKey()]);
}
i++;
}
return param;
}
}
这里解析的实参,也就是为实际参数赋值,那么形式参数是在哪里解析的呢?
实际上,在创建ParamNameResolver对象时,就会解析形参。而创建ParamNameResolver对象的时机是:
调用mapper接口方法
调用MapperProxy#invoke
创建MapperMethod对象(接口方法的实际执行对象)
创建MethodSignature(解析方法返回值类型和创建参数解析器)
创建ParamNameResolver
public ParamNameResolver(Configuration config, Method method) {
final Class<?>[] paramTypes = method.getParameterTypes();
// 因为方法可以有多个参数,每个参数可以有多个注解,paramAnnotations二维数组代表的含义:annotations[i][j],第i个参数,第j个注解
final Annotation[][] paramAnnotations = method.getParameterAnnotations();
final SortedMap<Integer, String> map = new TreeMap<>();
// 参数的数量
int paramCount = paramAnnotations.length;
// get names from @Param annotations
for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {
// 如果是RowBounds和ResultHandler类型的参数,跳过
if (isSpecialParameter(paramTypes[paramIndex])) {
// skip special parameters
continue;
}
String name = null;
// 遍历每个参数的所有注解,拿到@Param注解信息
for (Annotation annotation : paramAnnotations[paramIndex]) {
if (annotation instanceof Param) {
hasParamAnnotation = true;
name = ((Param) annotation).value();
break;
}
}
if (name == null) {
// @Param was not specified.
if (config.isUseActualParamName()) {
name = getActualParamName(method, paramIndex);
}
if (name == null) {
// use the parameter index as the name ("0", "1", ...)
// gcode issue #71
name = String.valueOf(map.size());
}
}
// 参数下标->参数名称的映射
map.put(paramIndex, name);
}
names = Collections.unmodifiableSortedMap(map);
}
可以看到最终解析成:参数下标->参数名称的映射,并存储在names属性中。
至此参数解析已完成:
解析形参:参数下标->参数名称的映射
解析实体:根据参数下标,拿到实际参数的值,并赋值给对应的参数,得到:key为参数名称,value为参数值的最终执行参数
以执行查询为例SimpleExecutor#doQuery
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(RoutingStatementHandler,其delegate默认为PreparedStatementHandler)
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
// 创建PreparedStatement对象(含SQL参数设置)
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
// 创建PrepareStatement对象
stmt = handler.prepare(connection, transaction.getTimeout());
// 设置SQL占位符参数
handler.parameterize(stmt);
return stmt;
}
着重看下设置SQL参数的过程:
public void parameterize(Statement statement) throws SQLException {
delegate.parameterize(statement);
}
调用的是PreparedStatementHandler#parameterize:
public void parameterize(Statement statement) throws SQLException {
parameterHandler.setParameters((PreparedStatement) statement);
}
接着调用DefaultParameterHandler#setParameters:
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;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
// 从参数对象里,得到参数值(见参数解析:参数名->参数值的映射)
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
TypeHandler typeHandler = parameterMapping.getTypeHandler();
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) {
jdbcType = configuration.getJdbcTypeForNull();
}
try {
// 设置SQL占位符参数
typeHandler.setParameter(ps, i + 1, value, jdbcType);
} catch (TypeException | SQLException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
}
}
}
}
}
这里的核心就是利用了前面参数解析得到的:参数名->参数值的映射关系,再通过PreparedStatement对象的setXx方法设置占位符的值(mybatis使用了策略模式)
参数解析并设置SQL占位符后,就可以执行SQL语句了:
return handler.query(stmt, resultHandler);
调用PreparedStatementHandler#query:
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
// 执行SQL
ps.execute();
// 解析结果集,并返回
return resultSetHandler.handleResultSets(ps);
}
// 解析结果集,并返回
return resultSetHandler.handleResultSets(ps);
调用DefaultResultSetHandler#handleResultSets:
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
final List<Object> multipleResults = new ArrayList<>();
int resultSetCount = 0;
// 拿到第一个结果集,并包装成ResultSetWrapper
ResultSetWrapper rsw = getFirstResultSet(stmt);
// 获取MappedStatement所有ResultMap(因为可能存在嵌套)
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
// 有结果集,必须要有ResultMap,否则抛出异常
validateResultMapsCount(rsw, resultMapCount);
while (rsw != null && resultMapCount > resultSetCount) {
ResultMap resultMap = resultMaps.get(resultSetCount);
// 处理结果集
handleResultSet(rsw, resultMap, multipleResults, null);
// 获取下一个结果集
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
String[] resultSets = mappedStatement.getResultSets();
if (resultSets != null) {
while (rsw != null && resultSetCount < resultSets.length) {
ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
if (parentMapping != null) {
String nestedResultMapId = parentMapping.getNestedResultMapId();
ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
handleResultSet(rsw, resultMap, null, parentMapping);
}
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
}
// 返回结果
return collapseSingleResultList(multipleResults);
}
重点看下处理结果集的过程:
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);
// 处理这个结果集的所有行数据,并存放到DefaultResultHandler的list属性中
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());
}
}
来到handleRowValues:
public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
// 存在嵌套映射:一对一、一对多
if (resultMap.hasNestedResultMaps()) {
ensureNoRowBounds();
checkResultHandler();
// 处理嵌套映射
handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
} else {
// 处理简单映射
handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
}
}
简单映射
// 处理简单映射
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<>();
ResultSet resultSet = rsw.getResultSet();
skipRows(resultSet, rowBounds);
while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
// Discriminator:鉴别器,可以看作是字典转换,很少使用,跳过
ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
// 获取每行的值对象
Object rowValue = getRowValue(rsw, discriminatedResultMap, null);
// 将结果集存放到DefaultResultSetHandler的list属性中
storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
}
}
看是如何将每一行转换为对象的:
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
final ResultLoaderMap lazyLoader = new ResultLoaderMap();
// 通过反射创建目标对象:objectFactory.create(resultType),此时对象所有属性为空
Object rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
// 将目标对象,包装为MetaObject
final MetaObject metaObject = configuration.newMetaObject(rowValue);
boolean foundValues = this.useConstructorMappings;
// 判断是否需要应用自动映射:AutoMappingBehavior.NONE != configuration.getAutoMappingBehavior()
if (shouldApplyAutomaticMappings(resultMap, false)) {
// 应用自动映射
foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
}
// 应用属性映射(ResultMap)
foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
foundValues = lazyLoader.size() > 0 || foundValues;
rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
}
return rowValue;
}
可以看到,这里主要是分为几个步骤:
根据返回值类型,通过反射创建对象
根据列名,找到属性名,构建映射关系
从结果集中拿到列名对应的值,根据映射关系,给对象的属性赋值
嵌套映射
// 处理嵌套映射
handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
private void handleRowValuesForNestedResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
final DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
ResultSet resultSet = rsw.getResultSet();
skipRows(resultSet, rowBounds);
Object rowValue = previousRowValue;
while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
final ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
final CacheKey rowKey = createRowKey(discriminatedResultMap, rsw, null);
Object partialObject = nestedResultObjects.get(rowKey);
// issue #577 && #542
if (mappedStatement.isResultOrdered()) {
if (partialObject == null && rowValue != null) {
nestedResultObjects.clear();
storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
}
rowValue = getRowValue(rsw, discriminatedResultMap, rowKey, null, partialObject);
} else {
// 获取每一个行对象
rowValue = getRowValue(rsw, discriminatedResultMap, rowKey, null, partialObject);
if (partialObject == null) {
storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
}
}
}
if (rowValue != null && mappedStatement.isResultOrdered() && shouldProcessMoreRows(resultContext, rowBounds)) {
storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
previousRowValue = null;
} else if (rowValue != null) {
previousRowValue = rowValue;
}
}
看下如何处理行对象的:
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, CacheKey combinedKey, String columnPrefix, Object partialObject) throws SQLException {
final String resultMapId = resultMap.getId();
Object rowValue = partialObject;
if (rowValue != null) {
final MetaObject metaObject = configuration.newMetaObject(rowValue);
putAncestor(rowValue, resultMapId);
applyNestedResultMappings(rsw, resultMap, metaObject, columnPrefix, combinedKey, false);
ancestorObjects.remove(resultMapId);
} else {
final ResultLoaderMap lazyLoader = new ResultLoaderMap();
rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
final MetaObject metaObject = configuration.newMetaObject(rowValue);
boolean foundValues = this.useConstructorMappings;
// 判断是否使用自动映射
if (shouldApplyAutomaticMappings(resultMap, true)) {
// 应用自动映射
foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
}
// 映射属性映射
foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
// 将祖先对象及其resultMapId存储在ancestorObjects属性中
putAncestor(rowValue, resultMapId);
// 跟简单映射不一样的地方:应用嵌套映射(association、collection都属性嵌套映射)
foundValues = applyNestedResultMappings(rsw, resultMap, metaObject, columnPrefix, combinedKey, true) || foundValues;
ancestorObjects.remove(resultMapId);
foundValues = lazyLoader.size() > 0 || foundValues;
rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
}
if (combinedKey != CacheKey.NULL_CACHE_KEY) {
nestedResultObjects.put(combinedKey, rowValue);
}
}
return rowValue;
}
重点看如何处理嵌套映射:
private boolean applyNestedResultMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String parentPrefix, CacheKey parentRowKey, boolean newObject) {
boolean foundValues = false;
for (ResultMapping resultMapping : resultMap.getPropertyResultMappings()) {
final String nestedResultMapId = resultMapping.getNestedResultMapId();
// association和collection才有nestedResultMapId
if (nestedResultMapId != null && resultMapping.getResultSet() == null) {
try {
final String columnPrefix = getColumnPrefix(parentPrefix, resultMapping);
// 拿到association和collection里面的映射关系
final ResultMap nestedResultMap = getNestedResultMap(rsw.getResultSet(), nestedResultMapId, columnPrefix);
if (resultMapping.getColumnPrefix() == null) {
// try to fill circular reference only when columnPrefix
// is not specified for the nested result map (issue #215)
Object ancestorObject = ancestorObjects.get(nestedResultMapId);
if (ancestorObject != null) {
if (newObject) {
linkObjects(metaObject, resultMapping, ancestorObject); // issue #385
}
continue;
}
}
final CacheKey rowKey = createRowKey(nestedResultMap, rsw, columnPrefix);
final CacheKey combinedKey = combineKeys(rowKey, parentRowKey);
Object rowValue = nestedResultObjects.get(combinedKey);
boolean knownValue = rowValue != null;
// 根据映射关系,实例化集合属性,并设置到目标对象的属性中,并返回集合对象:
// propertyValue = objectFactory.create(type);
// metaObject.setValue(propertyName, propertyValue);
instantiateCollectionPropertyIfAppropriate(resultMapping, metaObject); // mandatory
if (anyNotNullColumnHasValue(resultMapping, columnPrefix, rsw)) {
// 通过递归的方式:实例化关联对象
rowValue = getRowValue(rsw, nestedResultMap, combinedKey, columnPrefix, rowValue);
if (rowValue != null && !knownValue) {
// 设置到关联的属性中
linkObjects(metaObject, resultMapping, rowValue);
foundValues = true;
}
}
} catch (SQLException e) {
throw new ExecutorException("Error getting nested result map values for '" + resultMapping.getProperty() + "'. Cause: " + e, e);
}
}
}
return foundValues;
}
看下属性设置的方法:
private void linkObjects(MetaObject metaObject, ResultMapping resultMapping, Object rowValue) {
final Object collectionProperty = instantiateCollectionPropertyIfAppropriate(resultMapping, metaObject);
if (collectionProperty != null) {
// 处理集合属性,因为在此之前已经将该集合设置到了对象属性中,所以只需要将该对象放入到集合中即可
final MetaObject targetMetaObject = configuration.newMetaObject(collectionProperty);
targetMetaObject.add(rowValue);
} else {
// 设置单个对象属性
metaObject.setValue(resultMapping.getProperty(), rowValue);
}
}