MyBatis初化Configuration流程
示例
String resource = "mybatis/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sessionFactory.openSession();
AuthorMapper authorMapper = sqlSession.getMapper(AuthorMapper.class);
Author byId = authorMapper.getById(110);
System.out.println("byId:" + byId);
可以看出一切都是从 new SqlSessionFactoryBuilder().build开始的
new SqlSessionFactoryBuilder().build方法
public SqlSessionFactory build(InputStream inputStream) {
return build(inputStream, null, null);
}
//调用下面的build方法最终返回SqlSessionFactory->DefaultSqlSessionFactory
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
//1.构造
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
//2.返回DefaultSqlSessionFactory对象
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.
}
}
}
1.XMLConfigBuilder对象构造
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
//XPathParser 解析XML的工具类, 对XPath的封装 1.
this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
}
// 调用这个构造方法
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
//调用父类的构造方法
super(new Configuration());
ErrorContext.instance().resource("SQL Mapper Configuration");
//设置Properties中设置的变量
this.configuration.setVariables(props);
//设置为没有解析的标识,后面真正解析mybatisConfig.xml配置文件中使用
this.parsed = false;
//设置environment
this.environment = environment;
//赋值解析器
this.parser = parser;
}
//类中的属性
public class XMLConfigBuilder extends BaseBuilder {
//是否解析
private boolean parsed;
//XML解析器XPath
private final XPathParser parser;
//环境
private String environment;
//反射工厂
private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory();
XMLMapperEntityResolver
public class XMLMapperEntityResolver implements EntityResolver {
//验证DTD文件使用本地的
private static final String IBATIS_CONFIG_SYSTEM = "ibatis-3-config.dtd";
private static final String IBATIS_MAPPER_SYSTEM = "ibatis-3-mapper.dtd";
private static final String MYBATIS_CONFIG_SYSTEM = "mybatis-3-config.dtd";
private static final String MYBATIS_MAPPER_SYSTEM = "mybatis-3-mapper.dtd";
private static final String MYBATIS_CONFIG_DTD = "org/apache/ibatis/builder/xml/mybatis-3-config.dtd";
private static final String MYBATIS_MAPPER_DTD = "org/apache/ibatis/builder/xml/mybatis-3-mapper.dtd";
/**
* Converts a public DTD into a local one
使用本地的DTD文件来验证XML文件 本地文件位置 上面最后2个属性值
*/
@Override
public InputSource resolveEntity(String publicId, String systemId) throws SAXException {
try {
if (systemId != null) {
String lowerCaseSystemId = systemId.toLowerCase(Locale.ENGLISH);
if (lowerCaseSystemId.contains(MYBATIS_CONFIG_SYSTEM) || lowerCaseSystemId.contains(IBATIS_CONFIG_SYSTEM)) {
return getInputSource(MYBATIS_CONFIG_DTD, publicId, systemId);
} else if (lowerCaseSystemId.contains(MYBATIS_MAPPER_SYSTEM) || lowerCaseSystemId.contains(IBATIS_MAPPER_SYSTEM)) {
return getInputSource(MYBATIS_MAPPER_DTD, publicId, systemId);
}
}
return null;
} catch (Exception e) {
throw new SAXException(e.toString());
}
}
//解析XML文件返回InputSource
private InputSource getInputSource(String path, String publicId, String systemId) {
InputSource source = null;
if (path != null) {
try {
InputStream in = Resources.getResourceAsStream(path);
source = new InputSource(in);
source.setPublicId(publicId);
source.setSystemId(systemId);
} catch (IOException e) {
// ignore, null is ok
}
}
return source;
}
}
XPathParser
public class XPathParser {
//解析xml生成文档对象
private final Document document;
//是否开启验证
private boolean validation;
//实体验证 用于加载本地 文件DTD验证文件 http://mybatis.org/dtd/mybatis-3-config.dtd由于网络有
private EntityResolver entityResolver;
//属性变量 mybatis-config.xml properties 标签定义的键位对集合
private Properties variables;
//XPATH
private XPath xpath;
//inputStream就是我们的mybatis-config.xml文件
//validation 默认为true 开启验证
//variables默认为 空
//entityResolver 为 org.apache.ibatis.builder.xml.XMLMapperEntityResolver 这是Mybatis中的实现
public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) {
//设置属性值
commonConstructor(validation, variables, entityResolver);
//创建Document对象
this.document = createDocument(new InputSource(inputStream));
}
commonConstructor
private void commonConstructor(boolean validation, Properties variables, EntityResolver entityResolver) {
//设置是否验证 true
this.validation = validation;
//验证DTD文件本地加载
this.entityResolver = entityResolver;
//变量
this.variables = variables;
//XPath工厂
XPathFactory factory = XPathFactory.newInstance();
//构建Xpath对象
this.xpath = factory.newXPath();
}
createDocument
private Document createDocument(InputSource inputSource) {
// important: this must only be called AFTER common constructor
try {
//构造DocumentBuilderFactory对象
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(validation);
factory.setNamespaceAware(false);
factory.setIgnoringComments(true);
factory.setIgnoringElementContentWhitespace(false);
factory.setCoalescing(false);
factory.setExpandEntityReferences(true);
//构造 DocumentBuilder
DocumentBuilder builder = factory.newDocumentBuilder();
builder.setEntityResolver(entityResolver);
builder.setErrorHandler(new ErrorHandler() {
@Override
public void error(SAXParseException exception) throws SAXException {
throw exception;
}
@Override
public void fatalError(SAXParseException exception) throws SAXException {
throw exception;
}
@Override
public void warning(SAXParseException exception) throws SAXException {
}
});
//返回Document对象
return builder.parse(inputSource);
} catch (Exception e) {
throw new BuilderException("Error creating document instance. Cause: " + e, e);
}
}
XMLConfigBuilder中new Configuration()
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
//调用父类的构造方法
super(new Configuration());
先看super方法
public abstract class BaseBuilder {
//
protected final Configuration configuration;
protected final TypeAliasRegistry typeAliasRegistry;
protected final TypeHandlerRegistry typeHandlerRegistry;
public BaseBuilder(Configuration configuration) {
//赋值
this.configuration = configuration;
//别名类型注册器 1.
this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
//typeHandler注册器2.
this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
}
typeAliasRegistry
public class TypeAliasRegistry {
private final Map<String, Class<?>> TYPE_ALIASES = new HashMap<String, Class<?>>();
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);
}
可以看到主要是注册JAVA中的类型与Mybatis的数据类型进行绑定
TypeHandlerRegistry 注册一个Mybatis中内置的类型转换扩展
public final class TypeHandlerRegistry {
private final Map<JdbcType, TypeHandler<?>> JDBC_TYPE_HANDLER_MAP = new EnumMap<JdbcType, TypeHandler<?>>(JdbcType.class);
private final Map<Type, Map<JdbcType, TypeHandler<?>>> TYPE_HANDLER_MAP = new ConcurrentHashMap<Type, Map<JdbcType, TypeHandler<?>>>();
private final TypeHandler<Object> UNKNOWN_TYPE_HANDLER = new UnknownTypeHandler(this);
private final Map<Class<?>, TypeHandler<?>> ALL_TYPE_HANDLERS_MAP = new HashMap<Class<?>, TypeHandler<?>>();
private static final Map<JdbcType, TypeHandler<?>> NULL_TYPE_HANDLER_MAP = Collections.emptyMap();
private Class<? extends TypeHandler> defaultEnumTypeHandler = EnumTypeHandler.class;
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 ClobTypeHandler());
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 ClobTypeHandler());
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, UNKNOWN_TYPE_HANDLER);
register(Object.class, JdbcType.OTHER, UNKNOWN_TYPE_HANDLER);
register(JdbcType.OTHER, UNKNOWN_TYPE_HANDLER);
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());
// mybatis-typehandlers-jsr310
if (Jdk.dateAndTimeApiExists) {
this.register(Instant.class, InstantTypeHandler.class);
this.register(LocalDateTime.class, LocalDateTimeTypeHandler.class);
this.register(LocalDate.class, LocalDateTypeHandler.class);
this.register(LocalTime.class, LocalTimeTypeHandler.class);
this.register(OffsetDateTime.class, OffsetDateTimeTypeHandler.class);
this.register(OffsetTime.class, OffsetTimeTypeHandler.class);
this.register(ZonedDateTime.class, ZonedDateTimeTypeHandler.class);
this.register(Month.class, MonthTypeHandler.class);
this.register(Year.class, YearTypeHandler.class);
this.register(YearMonth.class, YearMonthTypeHandler.class);
this.register(JapaneseDate.class, JapaneseDateTypeHandler.class);
}
// issue #273
register(Character.class, new CharacterTypeHandler());
register(char.class, new CharacterTypeHandler());
}
new Configuration() 方法核心方法
public class Configuration {
//环境
protected Environment environment;
//安全SQL是否启用
protected boolean safeRowBoundsEnabled;
protected boolean safeResultHandlerEnabled = true;
protected boolean mapUnderscoreToCamelCase;
protected boolean aggressiveLazyLoading;
protected boolean multipleResultSetsEnabled = true;
protected boolean useGeneratedKeys;
protected boolean useColumnLabel = true;
protected boolean cacheEnabled = true;
protected boolean callSettersOnNulls;
protected boolean useActualParamName = true;
protected boolean returnInstanceForEmptyRow;
//log前缀
protected String logPrefix;
//LOG实现可在Mybatis-config中setting中配置
protected Class <? extends Log> logImpl;
protected Class <? extends VFS> vfsImpl;
//本地Cache范围 在一个SESSION中
protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;
protected JdbcType jdbcTypeForNull = JdbcType.OTHER;
//懒加载触发器
protected Set<String> lazyLoadTriggerMethods = new HashSet<String>(Arrays.asList(new String[] { "equals", "clone", "hashCode", "toString" }));
//默认超时时间
protected Integer defaultStatementTimeout;
//默认批量执行的SQL的数
protected Integer defaultFetchSize;
//默认ExecutorType类型ExecutorType.SIMPLE
protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL;
protected AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior = AutoMappingUnknownColumnBehavior.NONE;
//Properties标签与文件中变量
protected Properties variables = new Properties();
//反射工厂
protected ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
protected ObjectFactory objectFactory = new DefaultObjectFactory();
protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();
//是否懒加载 默认false
protected boolean lazyLoadingEnabled = false;
//代理工厂
protected ProxyFactory proxyFactory = new JavassistProxyFactory(); // #224 Using internal Javassist instead of OGNL
//数据库id
protected String databaseId;
/**
* Configuration factory class.
* Used to create Configuration for loading deserialized unread properties.
*
* @see <a href='https://code.google.com/p/mybatis/issues/detail?id=300'>Issue 300 (google code)</a>
*/
protected Class<?> configurationFactory;
//解析mybatisCofnig中mappers标签中注册所有mapper
protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
//拦截器链
protected final InterceptorChain interceptorChain = new InterceptorChain();
//扩展TypeHandle注册
protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();
//别名注册
protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();
//MappedStatement
protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection");
//缓存
protected final Map<String, Cache> caches = new StrictMap<Cache>("Caches collection");
protected final Map<String, ResultMap> resultMaps = new StrictMap<ResultMap>("Result Maps collection");
protected final Map<String, ParameterMap> parameterMaps = new StrictMap<ParameterMap>("Parameter Maps collection");
protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<KeyGenerator>("Key Generators collection");
//加载文件资料如mapper resouce='xxx/xxMapper.xml'
protected final Set<String> loadedResources = new HashSet<String>();
//保存sql
protected final Map<String, XNode> sqlFragments = new StrictMap<XNode>("XML fragments parsed from previous mappers");
protected final Collection<XMLStatementBuilder> incompleteStatements = new LinkedList<XMLStatementBuilder>();
protected final Collection<CacheRefResolver> incompleteCacheRefs = new LinkedList<CacheRefResolver>();
protected final Collection<ResultMapResolver> incompleteResultMaps = new LinkedList<ResultMapResolver>();
protected final Collection<MethodResolver> incompleteMethods = new LinkedList<MethodResolver>();
/*
* A map holds cache-ref relationship. The key is the namespace that
* references a cache bound to another namespace and the value is the
* namespace which the actual cache is bound to.
*/
protected final Map<String, String> cacheRefMap = new HashMap<String, String>();
public Configuration(Environment environment) {
this();
this.environment = environment;
}
//各种别名 与Mybatis中的类绑定
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);
languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
languageRegistry.register(RawLanguageDriver.class);
}
new Configuration()中会对所有成员变量默认初始化
2. build()方法参数是XMLConfigBuilder
parser.parse()返回Configruration
public class XMLConfigBuilder extends BaseBuilder {
private boolean parsed;
private final XPathParser parser;
private String environment;
private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory();
...
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
parseConfiguration()解析mybatisConfig中的所有标签包含未设置的都会使用Mybatis中的默认设置
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
typeAliasesElement(root.evalNode("typeAliases"));
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
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
propertiesElement对Properties标签中的变量解析
public class XMLConfigBuilder extends BaseBuilder {
private boolean parsed;
private final XPathParser parser;
private String environment;
private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory();
....
private void propertiesElement(XNode context) throws Exception {
//context<properties resource="mybatis/conn.properties">
// ...
//</properties>
if (context != null) {
Properties defaults = context.getChildrenAsProperties();
String resource = context.getStringAttribute("resource");
String url = context.getStringAttribute("url");
if (resource != null && url != null) {
throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other.");
}
//这里使用是resource mybatis/conn.properties 获取Properties文件中的变量
if (resource != null) {
defaults.putAll(Resources.getResourceAsProperties(resource));
} else if (url != null) {
defaults.putAll(Resources.getUrlAsProperties(url));
}
//properties标签中设置的变量
Properties vars = configuration.getVariables();
if (vars != null) {
defaults.putAll(vars);
}
//设置值XpathParser中赋值--解析到下面的变量时直接从解析器获取
parser.setVariables(defaults);
//为configuration设置Properties
configuration.setVariables(defaults);
//至此Properties文件与Properties标签中的所有变量解析完成
}
}
conn.properties文件示例
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/mybatis-test?characterEncoding=utf8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&useSSL=false
jdbc.user=root
jdbc.password=root
-
解析完成
{jdbc.url=jdbc:mysql://127.0.0.1:3306/mybatis-test?characterEncoding=utf8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&useSSL=false, jdbc.driver=com.mysql.jdbc.Driver, jdbc.user=root, jdbc.password=root}
因为我的只有properties文件,没有标签中的只有这些
Properties settings = settingsAsProperties(root.evalNode(“settings”));
解析setting标签
<settings>
<!--开启二级缓存 默认true需要在xxMapper.xml中同样开启才有效果-->
<setting name="cacheEnabled" value="true"/>
<!--开启懒加载 默认false-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--当开启时,任何方法的调用都会加载该对象的所有属性。否则,每个属性会按需加载 true为直接懒加载 false为深度懒加载 默认false 3.4.1之前是true-->
<setting name="aggressiveLazyLoading" value="false"/>
<!--开启多结果集-是否允许单一语句返回多结果集-->
<setting name="multipleResultSetsEnabled" value="true"/>
<!--使用列标签代替列名。-->
<setting name="useColumnLabel" value="true"/>
<!-- 允许 JDBC 支持自动生成主键,需要驱动兼容-->
<setting name="useGeneratedKeys" value="false"/>
<!-- 指定 MyBatis 应如何自动映射列到字段或属性。
NONE 表示取消自动映射;
PARTIAL 只会自动映射没有定义嵌套结果集映射的结果集。 默认
FULL 会自动映射任意复杂的结果集(无论是否嵌套)-->
<setting name="autoMappingBehavior" value="PARTIAL"/>
<!-- 指定发现自动映射目标未知列(或者未知属性类型)的行为。
NONE: 不做任何反应
WARNING: 输出提醒日志 ('org.apache.ibatis.session.AutoMappingUnknownColumnBehavior' 的日志等级必须设置为 WARN)
FAILING: 映射失败 (抛出 SqlSessionException)-->
<setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
<!--配置默认的执行器。
SIMPLE 就是普通的执行器; 默认
REUSE 执行器会重用预处理语句(prepared statements);
BATCH 执行器将重用语句并执行批量更新 -->
<setting name="defaultExecutorType" value="SIMPLE"/>
<!--设置超时时间,它决定驱动等待数据库响应的秒数。没有默认值 -->
<setting name="defaultStatementTimeout" value="25"/>
<!--为驱动的结果集获取数量(fetchSize)设置一个提示值。此参数只可以在查询设置中被覆盖 无-->
<setting name="defaultFetchSize" value="100"/>
<!-- 允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为false 默认False-->
<setting name="safeRowBoundsEnabled" value="false"/>
<!--是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射。 默认false
-->
<setting name="mapUnderscoreToCamelCase" value="false"/>
<!--
MyBatis 利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询。 默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地会话仅用在语句执行上,对相同 SqlSession 的不同调用将不会共享数据 默认 SESSION一个会话中
-->
<setting name="localCacheScope" value="SESSION"/>
<!--
当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。 某些驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。
-->
<setting name="jdbcTypeForNull" value="OTHER"/>
<!--指定哪个对象的方法触发一次延迟加载 -->
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>
就这解析这一堆东东
具体请看官方文档
下面都一堆这样的解析为Configuration中的标签解析并赋值
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
typeAliasesElement(root.evalNode("typeAliases"));
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
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
主要 看mapperElement()这个解析
Mappers的种配置方式
<!-- 使用相对于类路径的资源引用 xml和Mapper接口可以不在同一个目录下-->
<mappers>
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
<mapper resource="org/mybatis/builder/BlogMapper.xml"/>
<mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
<!-- 使用完全限定资源定位符(URL) xml和Mapper接口可以不在同一个目录下-->
<mappers>
<mapper url="file:///var/mappers/AuthorMapper.xml"/>
<mapper url="file:///var/mappers/BlogMapper.xml"/>
<mapper url="file:///var/mappers/PostMapper.xml"/>
</mappers>
<!-- 使用映射器接口实现类的完全限定类名 xml和Mapper接口必须在同一个目录下-->
<mappers>
<mapper class="org.mybatis.builder.AuthorMapper"/>
<mapper class="org.mybatis.builder.BlogMapper"/>
<mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
<!-- 将包内的映射器接口实现全部注册为映射器 xml和Mapper接口必须在同一个目录下-->
<mappers>
<package name="org.mybatis.builder"/>
</mappers>
以上4种只能同时使用一种不能多种
对Mappers标签下的xxMapper.xml标签解析
//parent=
//<mappers>
//<mapper resource="mapper/AuthorMapper.xml"/>
//....
//</mappers>
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
//获取所有子节点
for (XNode child : parent.getChildren()) {
//示例:child =<mapper resource="mapper/AuthorMapper.xml"/>
//判断是不是package的配置方式,get节点名称 mapper
if ("package".equals(child.getName())) {
String mapperPackage = child.getStringAttribute("name");
configuration.addMappers(mapperPackage);
} else {
//获取属性名称
//resource方式 resource
String resource = child.getStringAttribute("resource");
//url方式 url
String url = child.getStringAttribute("url");
//class方式 class
String mapperClass = child.getStringAttribute("class");
//resource 最常用 只说这一种
if (resource != null && url == null && mapperClass == null) {
ErrorContext.instance().resource(resource);
//获取 mapper/AuthorMapper.xml
InputStream inputStream = Resources.getResourceAsStream(resource);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
// 这个方法见下面 mapperParser.parse
mapperParser.parse();
} else if (resource == null && url != null && mapperClass == null) {
//url方式
ErrorContext.instance().resource(url);
InputStream inputStream = Resources.getUrlAsStream(url);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url == null && mapperClass方式 != null) {
//Class方式
Class<?> mapperInterface = Resources.classForName(mapperClass);
configuration.addMapper(mapperInterface);
} else {
//其它类型抛出类型
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
}
}
}
}
XMLMapperBuilder 用来解析Mapper.xml的构建器
public XMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
//和解析Configuration开始里一样的套路 使用Document之类的,设置解析
this(new XPathParser(inputStream, true, configuration.getVariables(), new XMLMapperEntityResolver()),
configuration, resource, sqlFragments);
}
还是使用Xpath解析
看构造方法
public XMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
this(new XPathParser(inputStream, true, configuration.getVariables(), new XMLMapperEntityResolver()),
configuration, resource, sqlFragments);
}
private XMLMapperBuilder(XPathParser parser, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
super(configuration);
this.builderAssistant = new MapperBuilderAssistant(configuration, resource);
this.parser = parser;
this.sqlFragments = sqlFragments;
this.resource = resource;
}
这里为configuration赋了值
BaseBuilder
public abstract class BaseBuilder {
protected final Configuration configuration;
protected final TypeAliasRegistry typeAliasRegistry;
protected final TypeHandlerRegistry typeHandlerRegistry;
public BaseBuilder(Configuration configuration) {
this.configuration = configuration;
this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
}
MapperBuilderAssistant
public class MapperBuilderAssistant extends BaseBuilder {
//当前的命名空间
private String currentNamespace;
//资料
private final String resource;
//缓存
private Cache currentCache;
private boolean unresolvedCacheRef; // issue #676
XMLMapperBuilder parse方法
public class XMLMapperBuilder extends BaseBuilder {
//解析器
private final XPathParser parser;
//
private final MapperBuilderAssistant builderAssistant;
//SQL节点
private final Map<String, XNode> sqlFragments;
//资源
private final String resource;
...
//真正解析xxxMapper.xml文件
public void parse() {
//示例resource --mapper/AuthorMapper.xml 判断是否已经加载过了是个HashSet在Configuration类中loadedResources BaseBuilder中有configuration上面一步设置的
if (!configuration.isResourceLoaded(resource)) { //见附2
//1. 解析mapper parser中已经到mapper/AuthorMapper.xml转换为Document对象 evalNode("/mapper")Xpath语法 见下面附1 ,2获取mapper节点的所有内容
configurationElement(parser.evalNode("/mapper"));
//添加到loadedResources中
configuration.addLoadedResource(resource);
//绑定Mapper与xm的命名空间
bindMapperForNamespace();
}
//解析ResultMap
parsePendingResultMaps();
//解析缓存
parsePendingCacheRefs();
//解析select insert之类的标签
parsePendingStatements();
}
附1.parse示例
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ghgcn.mybatis_demo.mapper.AuthorMapper">
<resultMap id="BaseResultMap" type="com.ghgcn.mybatis_demo.entity.Author">
<id column="id" property="id" />
<result column="username" property="username" />
<result column="password" property="password" />
<result column="email" property="email" />
<result column="bio" property="bio"
typeHandler="com.ghgcn.mybatis_demo.handler.IntArrayJoinTypeHandler" />
<result column="favourite_section" property="favouriteSection" />
</resultMap>
<resultMap id="BaseResultMap1" type="com.ghgcn.mybatis_demo.entity.Author">
<id column="id" property="id" />
<result column="username" property="username" />
<result column="password" property="password" />
<result column="email" property="email" />
<result column="bio" property="bio"
typeHandler="com.ghgcn.mybatis_demo.handler.IntArrayJoinTypeHandler" />
<result column="favourite_section" property="favouriteSection" />
<association property="blog" column="id"
javaType="com.ghgcn.mybatis_demo.entity.Blog">
<id column="id" property="id" />
<result column="author_id" property="authorId" />
<result column="title" property="title" />
</association>
</resultMap>
<resultMap id="BaseResultMap2" type="com.ghgcn.mybatis_demo.entity.Author">
<id column="id" property="id" />
<result column="username" property="username" />
<result column="password" property="password" />
<result column="email" property="email" />
<result column="bio" property="bio"
typeHandler="com.ghgcn.mybatis_demo.handler.IntArrayJoinTypeHandler" />
<result column="favourite_section" property="favouriteSection" />
<association property="blog" column="id"
javaType="com.ghgcn.mybatis_demo.entity.Blog" select="findBlogById">
</association>
</resultMap>
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
id,username,password,email,bio,favourite_section
</sql>
<select id="getById" resultMap="BaseResultMap">
SELECT
<include refid="Base_Column_List" />
FROM author
WHERE id = #{id}
</select>
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
INSERT
INTO author (
username,
password,
email,
bio,
favourite_section
)
VALUES
(
#{username},
#{password},
#{email},
#{bio},
#{favouriteSection}
);
</insert>
<update id="updateById">
update author
<set>
<if test="username!=null and username !=''">
username=#{username},
</if>
<if test="password!=null and password !=''">
password=#{password},
</if>
<if test="email!=null and email !=''">
email=#{email},
</if>
<if test="bio!=null and bio !=''">
bio=#{bio},
</if>
<if test="favouriteSection!=null and favouriteSection !=''">
favourite_section=#{favouriteSection},
</if>
</set>
<where>
id=#{id}
</where>
</update>
<select id="getList" resultMap="BaseResultMap1">
SELECT
author.*,
blog.*
FROM
author
LEFT JOIN blog on author.id = blog.author_id
</select>
<select id="getList2" resultMap="BaseResultMap2">
SELECT
<include refid="Base_Column_List" />
FROM author
</select>
<select id="findBlogById" resultType="com.ghgcn.mybatis_demo.entity.Blog" parameterType="Integer">
SELECT
id,
author_id,
title
FROM
blog
WHERE
author_id = #{id}
</select>
</mapper>
configurationElement方法
private void configurationElement(XNode context) {
//context 是xml见上图
try {
//com.ghgcn.mybatis_demo.mapper.AuthorMapper
String namespace = context.getStringAttribute("namespace");
if (namespace == null || namespace.equals("")) {
//如果命名空间为空抛出异常,Mybatis要求必有命名空间
throw new BuilderException("Mapper's namespace cannot be empty");
}
//设置命名空间 MapperBuilderAssistantMapper构造助理类
builderAssistant.setCurrentNamespace(namespace);
//解析有没有开启缓存连接
cacheRefElement(context.evalNode("cache-ref"));
//解析有没有二级缓存标签 这里NULL
cacheElement(context.evalNode("cache"));
//解析有没有parameterMap 上面的xml没有
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
//解析resultMap有
resultMapElements(context.evalNodes("/mapper/resultMap"));
//解析sql标签 这里没如果XML中有SQL标签就会在这里解析
sqlElement(context.evalNodes("/mapper/sql"));
//解析select|insert|update|delete标签
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);
}
}
cacheRefElement(context.evalNode(“cache-ref”));
参照缓存
回想一下上一节内容, 这个特殊命名空间的唯一缓存会被使用或者刷新相同命名空间内 的语句。也许将来的某个时候,你会想在命名空间中共享相同的缓存配置和实例。在这样的 情况下你可以使用 cache-ref 元素来引用另外一个缓存。
<cache-ref namespace="com.someone.application.data.SomeMapper"/>
<cache-ref namespace=""/>解析这种标签
private void cacheRefElement(XNode context) {
//不是空的情况下
if (context != null) {
//添加CacheRef属性和其中的命名空间
configuration.addCacheRef(builderAssistant.getCurrentNamespace(), context.getStringAttribute("namespace"));
//缓存
CacheRefResolver cacheRefResolver = new CacheRefResolver(builderAssistant, context.getStringAttribute("namespace"));
try {
//建立连接
cacheRefResolver.resolveCacheRef();
} catch (IncompleteElementException e) {
configuration.addIncompleteCacheRef(cacheRefResolver);
}
}
}
字面上看就是这样。这个简单语句的效果如下:
- 映射语句文件中的所有 select 语句将会被缓存。
- 映射语句文件中的所有 insert,update 和 delete 语句会刷新缓存。
- 缓存会使用 Least Recently Used(LRU,最近最少使用的)算法来收回。
- 根据时间表(比如 no Flush Interval,没有刷新间隔), 缓存不会以任何时间顺序 来刷新。
- 缓存会存储列表集合或对象(无论查询方法返回什么)的 1024 个引用。
- 缓存会被视为是 read/write(可读/可写)的缓存,意味着对象检索不是共享的,而 且可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
-
示例值
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
这个更高级的配置创建了一个 FIFO 缓存,并每隔 60 秒刷新,存数结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此在不同线程中的调用者之间修改它们会 导致冲突。
可用的收回策略有:
LRU
– 最近最少使用的:移除最长时间不被使用的对象。FIFO
– 先进先出:按对象进入缓存的顺序来移除它们。SOFT
– 软引用:移除基于垃圾回收器状态和软引用规则的对象。WEAK
– 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
默认的是 LRU。
flushInterval(刷新间隔)可以被设置为任意的正整数,而且它们代表一个合理的毫秒 形式的时间段。默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新。
size(引用数目)可以被设置为任意正整数,要记住你缓存的对象数目和你运行环境的 可用内存资源数目。默认值是 1024。
readOnly(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓 存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。可读写的缓存 会返回缓存对象的拷贝(通过序列化) 。这会慢一些,但是安全,因此默认是 false。
使用自定义缓存
除了这些自定义缓存的方式, 你也可以通过实现你自己的缓存或为其他第三方缓存方案 创建适配器来完全覆盖缓存行为。
<cache type="com.domain.something.MyCustomCache"/>
这个示 例展 示了 如何 使用 一个 自定义 的缓 存实 现。type 属 性指 定的 类必 须实现 org.mybatis.cache.Cache 接口。这个接口是 MyBatis 框架中很多复杂的接口之一,但是简单 给定它做什么就行。
使用Cache接口
package org.apache.ibatis.cache
public interface Cache {
String getId();
void putObject(Object key, Object value);
Object getObject(Object key);
Object removeObject(Object key);
void clear();
int getSize();
ReadWriteLock getReadWriteLock();
}
从3.4.2版本开始,MyBatis已经支持在所有属性设置完毕以后可以调用一个初始化方法。如果你想要使用这个特性,请在你的自定义缓存类里实现 org.apache.ibatis.builder.InitializingObject
接口。
public interface InitializingObject {
void initialize() throws Exception;
}
记得缓存配置和缓存实例是绑定在 SQL 映射文件的命名空间是很重要的。因此,所有 在相同命名空间的语句正如绑定的缓存一样。 语句可以修改和缓存交互的方式, 或在语句的 语句的基础上使用两种简单的属性来完全排除它们。默认情况下,语句可以这样来配置:
<select ... flushCache="false" useCache="true"/>
<insert ... flushCache="true"/>
<update ... flushCache="true"/>
<delete ... flushCache="true"/>
cacheElement(context.evalNode(“cache”));
private void cacheElement(XNode context) throws Exception {
//不是空的情况
if (context != null) {
//获取类型
String type = context.getStringAttribute("type", "PERPETUAL");
//缓存的类型
Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type);
//哪种缓存方式
String eviction = context.getStringAttribute("eviction", "LRU");
//
Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction);
Long flushInterval = context.getLongAttribute("flushInterval");
//获取大小
Integer size = context.getIntAttribute("size");
//是否只读
boolean readWrite = !context.getBooleanAttribute("readOnly", false);
boolean blocking = context.getBooleanAttribute("blocking", false);
Properties props = context.getChildrenAsProperties();
//使用新的缓存
builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props);
}
}
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
<parameterMap type="" id=""></parameterMap>
//主要是来解析 parameterMap的标签,没有用过,看样子和resultMap类似
private void parameterMapElement(List<XNode> list) throws Exception {
for (XNode parameterMapNode : list) {
String id = parameterMapNode.getStringAttribute("id");
String type = parameterMapNode.getStringAttribute("type");
Class<?> parameterClass = resolveClass(type);
List<XNode> parameterNodes = parameterMapNode.evalNodes("parameter");
List<ParameterMapping> parameterMappings = new ArrayList<ParameterMapping>();
for (XNode parameterNode : parameterNodes) {
String property = parameterNode.getStringAttribute("property");
String javaType = parameterNode.getStringAttribute("javaType");
String jdbcType = parameterNode.getStringAttribute("jdbcType");
String resultMap = parameterNode.getStringAttribute("resultMap");
String mode = parameterNode.getStringAttribute("mode");
String typeHandler = parameterNode.getStringAttribute("typeHandler");
Integer numericScale = parameterNode.getIntAttribute("numericScale");
ParameterMode modeEnum = resolveParameterMode(mode);
Class<?> javaTypeClass = resolveClass(javaType);
JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
@SuppressWarnings("unchecked")
Class<? extends TypeHandler<?>> typeHandlerClass = (Class<? extends TypeHandler<?>>) resolveClass(typeHandler);
ParameterMapping parameterMapping = builderAssistant.buildParameterMapping(parameterClass, property, javaTypeClass, jdbcTypeEnum, resultMap, modeEnum, typeHandlerClass, numericScale);
parameterMappings.add(parameterMapping);
}
builderAssistant.addParameterMap(id, parameterClass, parameterMappings);
}
}
resultMapElements(context.evalNodes("/mapper/resultMap"));
<resultMap id="BaseResultMap" type="com.ghgcn.mybatis_demo.entity.Author">
<id column="id" property="id" />
<result column="username" property="username" />
<result column="password" property="password" />
<result column="email" property="email" />
<result column="bio" property="bio"
typeHandler="com.ghgcn.mybatis_demo.handler.IntArrayJoinTypeHandler" />
<result column="favourite_section" property="favouriteSection" />
</resultMap>
private void resultMapElements(List<XNode> list) throws Exception {
for (XNode resultMapNode : list) {
try {
resultMapElement(resultMapNode);
} catch (IncompleteElementException e) {
// ignore, it will be retried
}
}
}
对所有resultMap标签解析
private ResultMap resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings) throws Exception {
ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier());
//获取ResultMap的id
String id = resultMapNode.getStringAttribute("id",
resultMapNode.getValueBasedIdentifier());
//获取类型 result映射的类的类型全路径 com.ghgcn.mybatis_demo.entity.Author
// <resultMap id="BaseResultMap" type="com.ghgcn.mybatis_demo.entity.Author"> type
String type = resultMapNode.getStringAttribute("type",
resultMapNode.getStringAttribute("ofType",
resultMapNode.getStringAttribute("resultType",
resultMapNode.getStringAttribute("javaType"))));
//获取继承的resultMap
String extend = resultMapNode.getStringAttribute("extends");
//获取autoMapping autoMapping 如果设置这个属性,MyBatis将会为这个ResultMap开启或者关闭自动映射。这个属性会覆盖全局的属性 建立不设置 获取不到就是null 不理会
Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping");
//反射类
Class<?> typeClass = resolveClass(type);
Discriminator discriminator = null;
List<ResultMapping> resultMappings = new ArrayList<ResultMapping>();
//添加到
resultMappings.addAll(additionalResultMappings);
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<ResultFlag>();
if ("id".equals(resultChild.getName())) {
flags.add(ResultFlag.ID);
}
resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags));
}
}
ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping);
try {
return resultMapResolver.resolve();
} catch (IncompleteElementException e) {
configuration.addIncompleteResultMap(resultMapResolver);
throw e;
}
}
-
获取resultMap的所有子节点
buildResultMappingFromContext方法
解析
<resultMap id="BaseResultMap" type="com.ghgcn.mybatis_demo.entity.Author">
<id column="id" property="id" />
<result column="username" property="username" />
<result column="password" property="password" />
<result column="email" property="email" />
<result column="bio" property="bio"
typeHandler="com.ghgcn.mybatis_demo.handler.IntArrayJoinTypeHandler" />
<result column="favourite_section" property="favouriteSection" />
</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.<ResultMapping> emptyList()));
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);
@SuppressWarnings("unchecked")
Class<? extends TypeHandler<?>> typeHandlerClass = (Class<? extends TypeHandler<?>>) resolveClass(typeHandler);
JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
return builderAssistant.buildResultMapping(resultType, property, column, javaTypeClass, jdbcTypeEnum, nestedSelect, nestedResultMap, notNullColumn, columnPrefix, typeHandlerClass, flags, resultSet, foreignColumn, lazy);
}
- 一个ResultMap解析完
[
ResultMapping{property='id', column='id', javaType=class java.lang.Integer, jdbcType=null, nestedResultMapId='null', nestedQueryId='null', notNullColumns=[], columnPrefix='null', flags=[ID], composites=[], resultSet='null', foreignColumn='null', lazy=true},
ResultMapping{property='username', column='username', javaType=class java.lang.String, jdbcType=null, nestedResultMapId='null', nestedQueryId='null', notNullColumns=[], columnPrefix='null', flags=[], composites=[], resultSet='null', foreignColumn='null', lazy=true},
ResultMapping{property='password', column='password', javaType=class java.lang.String, jdbcType=null, nestedResultMapId='null', nestedQueryId='null', notNullColumns=[], columnPrefix='null', flags=[], composites=[], resultSet='null', foreignColumn='null', lazy=true},
ResultMapping{property='email', column='email', javaType=class java.lang.String, jdbcType=null, nestedResultMapId='null', nestedQueryId='null', notNullColumns=[], columnPrefix='null', flags=[], composites=[], resultSet='null', foreignColumn='null', lazy=true}, ResultMapping{property='bio', column='bio', javaType=class [Ljava.lang.Integer;, jdbcType=null, nestedResultMapId='null', nestedQueryId='null', notNullColumns=[], columnPrefix='null', flags=[], composites=[], resultSet='null', foreignColumn='null', lazy=true}, ResultMapping{property='favouriteSection', column='favourite_section', javaType=class java.lang.String, jdbcType=null, nestedResultMapId='null', nestedQueryId='null', notNullColumns=[], columnPrefix='null', flags=[], composites=[], resultSet='null', foreignColumn='null', lazy=true}
]
都有property , column,javaType属性
每一列都是一个集合元素生成ResultMapResolver对象
ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping);
public ResultMap resolve() {
return assistant.addResultMap(this.id, this.type, this.extend, this.discriminator, this.resultMappings, this.autoMapping);
}
sqlElement(context.evalNodes("/mapper/sql")); 解析节点把SQL取出 根据id
private void sqlElement(List<XNode> list) throws Exception {
if (configuration.getDatabaseId() != null) {
sqlElement(list, configuration.getDatabaseId());
}
sqlElement(list, null);
}
private void sqlElement(List<XNode> list, String requiredDatabaseId) throws Exception {
for (XNode context : list) {
String databaseId = context.getStringAttribute("databaseId");
String id = context.getStringAttribute("id");
id = builderAssistant.applyCurrentNamespace(id, false);
if (databaseIdMatchesCurrent(id, databaseId, requiredDatabaseId)) {
sqlFragments.put(id, context);
}
}
}
buildStatementFromContext(context.evalNodes(“select|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 {
statementParser.parseStatementNode();
} catch (IncompleteElementException e) {
configuration.addIncompleteStatement(statementParser);
}
}
}
解析
public void parseStatementNode() {
//获取select update delete insert方法的id名称
String id = context.getStringAttribute("id");
//获取数据库id默认为null
String databaseId = context.getStringAttribute("databaseId");
//判断数据库是不是必须要有databaseId
if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
return;
}
//获取属性fetchSize 这是尝试影响驱动程序每次批量返回的结果行数和这个设置值相等。默认值为 unset
Integer fetchSize = context.getIntAttribute("fetchSize");
// timeout超里时间
Integer timeout = context.getIntAttribute("timeout");
//参数Map
String parameterMap = context.getStringAttribute("parameterMap");
//属性参数类型
String parameterType = context.getStringAttribute("parameterType");
//获取参数类型的具体类型反射
Class<?> parameterTypeClass = resolveClass(parameterType);
//获取结果集映射
String resultMap = context.getStringAttribute("resultMap");
//返回类型
String resultType = context.getStringAttribute("resultType");
//获取lang
String lang = context.getStringAttribute("lang");
LanguageDriver langDriver = getLanguageDriver(lang);
//获取返加类型反射 可以是别名,会先打别名找不到就反射
Class<?> resultTypeClass = resolveClass(resultType);
//resultSetType
String resultSetType = context.getStringAttribute("resultSetType");
//statementType statementType STATEMENT,PREPARED 或 CALLABLE 的一个。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。
StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
//获取节点名称select insert update delete这种
String nodeName = context.getNode().getNodeName();
//获取枚举
SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
//判断是不是select
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
XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
includeParser.applyIncludes(context.getNode());
// Parse selectKey after includes and remove them.
processSelectKeyNodes(id, parameterTypeClass, langDriver);
// Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
//解析sql 把一些变量标签替换
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
String resultSets = context.getStringAttribute("resultSets");
String keyProperty = context.getStringAttribute("keyProperty");
String keyColumn = context.getStringAttribute("keyColumn");
KeyGenerator keyGenerator;
String keyStatementId = 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;
}
//添加到
builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
resultSetTypeEnum, flushCache, useCache, resultOrdered,
keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
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");
}
//例如:com.ghgcn.mybatis_demo.mapper.AuthorMapper.updateById
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)
.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) {
//替换ParameterMap
statementBuilder.parameterMap(statementParameterMap);
}
//构建MappedStatement
MappedStatement statement = statementBuilder.build();
//将statement添加到configuration中
configuration中.addMappedStatement(statement添加到);
return statement;
}
public class Configuration {
...
protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection");
public void addMappedStatement(MappedStatement ms) {
//xml中标签的全距径为key com.ghgcn.mybatis_demo.mapper.AuthorMapper.updateById 这种一个方法一条记录
mappedStatements.put(ms.getId(), ms);
}
附2 加载Mapper.xml的集合上面的一loadedResources
Configuration类中
protected final Set<String> loadedResources = new HashSet<String>();
protected final Map<String, XNode> sqlFragments = new StrictMap<XNode>("XML fragments parsed from previous mappers");
public boolean isResourceLoaded(String resource) {
return loadedResources.contains(resource);
}
configuration.addLoadedResource(resource);
protected final Set<String> loadedResources = new HashSet<String>();
configuration.addLoadedResource(resource);
public void addLoadedResource(String resource) {
//loadedResources添加这个资源
loadedResources.add(resource);
}
bindMapperForNamespace ();方法绑定命名空间
bindMapperForNamespace()
private void bindMapperForNamespace() {
//当前的命名空间
String namespace = builderAssistant.getCurrentNamespace();
if (namespace != null) {
Class<?> boundType = null;
try {
//获取命名空间对应的类反射
boundType = Resources.classForName(namespace);
} catch (ClassNotFoundException e) {
//ignore, bound type is not required
}
//判断是不是空 绝对不能是空
if (boundType != null) {
//判断这个Map集合是否有这个
if (!configuration.hasMapper(boundType)) {
// Spring may not know the real resource name so we set a flag
// to prevent loading again this resource from the mapper interface
// look at MapperAnnotationBuilder#loadXmlResource
//添加到Set中
configuration.addLoadedResource("namespace:" + namespace);
//添加到Map中
configuration.addMapper(boundType);
}
}
}
}
Configuration 类
public <T> void addMapper(Class<T> type) {
mapperRegistry.addMapper(type);
}
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
public boolean hasMapper(Class<?> type) {
return mapperRegistry.hasMapper(type);
}
MappperRegistry
public class MapperRegistry {
private final Configuration config;
//key命名空间的对象的类如com.ghgcn.mybatis_demo.mapper.AuthorMapper全路径
//value是生成的代理工厂
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();
public MapperRegistry(Configuration config) {
this.config = config;
}
就是维护了一个Map
这样就完成了Class与代理的映射
MapperProxyFactory
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);
}
添加addMapper
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));
// 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);
}
}
}
}
build 方法
public SqlSessionFactory build(Configuration config) {
//构造参数传入Configuration,所以DefaultSqlSessionFactory持有Configuration所有属性
return new DefaultSqlSessionFactory(config);
}
至此返回 DefaultSqlSessionFactory是SqlSessionFactory的一个实现
MyBatis GetMapper流程
SqlSession sqlSession = sessionFactory.openSession();
AuthorMapper authorMapper = sqlSession.getMapper(AuthorMapper.class);
authorMapper.getById(101);
一切从这里开始
openSession public class DefaultSqlSessionFactory implements SqlSessionFactory
@Override
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
//这个方法
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
//从configuration中获取environment
final Environment environment = configuration.getEnvironment();
//获取事务工厂
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
//构建事务默认是JdbcTransaction
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
//获取执行器默认是SIMPLE
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();
}
}
JdbcTransactionFactory
public class JdbcTransactionFactory implements TransactionFactory {
@Override
public void setProperties(Properties props) {
}
@Override
public Transaction newTransaction(Connection conn) {
return new JdbcTransaction(conn);
}
@Override
public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
return new JdbcTransaction(ds, level, autoCommit);
}
}
### final Executor executor = configuration.newExecutor(tx, execType);
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;
}
返回 return new DefaultSqlSession(configuration, executor, autoCommit);
public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
this.configuration = configuration;
this.executor = executor;
this.dirty = false;
this.autoCommit = autoCommit;
}
执行器 final Executor executor = configuration.newExecutor(tx, execType);
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;
}
SimpleExecutor
public class SimpleExecutor extends BaseExecutor {
public SimpleExecutor(Configuration configuration, Transaction transaction) {
super(configuration, transaction);
}
//super BaseExecutor
protected BaseExecutor(Configuration configuration, Transaction transaction) {
this.transaction = transaction;
this.deferredLoads = new ConcurrentLinkedQueue<DeferredLoad>();
this.localCache = new PerpetualCache("LocalCache");
this.localOutputParameterCache = new PerpetualCache("LocalOutputParameterCache");
this.closed = false;
this.configuration = configuration;
this.wrapper = this;
}
public class InterceptorChain {
private final List<Interceptor> interceptors = new ArrayList<Interceptor>();
//配置的插件里面取出插件,然后用插件的plugin方法去生成代理对象
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);
}
}
AuthorMapper authorMapper = sqlSession.getMapper(AuthorMapper.class);
public class DefaultSqlSession implements SqlSession {
...
@Override
public <T> T getMapper(Class<T> type) {
return configuration.<T>getMapper(type, this);
}
configuration->getMapper 这个就是Configuration加载时的Mapkey接口 VALUE是代理工厂
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
以上都是在configuration类中
MapperRegistry类中才是真正的Map 在解析Mapper标签时做了映射关系
public class MapperRegistry {
private final Configuration config;
// key Mapper接口 VALUE是代理工厂
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();
public MapperRegistry(Configuration config) {
this.config = config;
}
//==这个是核心方法==
@SuppressWarnings("unchecked")
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
//根据Key值获取Value
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
// 代理工厂是空就抛出异常
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
//使用代理工厂生成代理对象的实例
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
...
}
mapperProxyFactory.newInstance(sqlSession);
public class MapperProxyFactory<T> {
//代理的接口
private final Class<T> mapperInterface;
//方法与Mapper方法
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;
}
// 2. JDK动态代理
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
//1.生成MapperProxy对象
public T newInstance(SqlSession sqlSession) {
//见下面JDK动态代理的实现
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}
MapperProxy Jdk动态代理
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 {
try {
//判断是不理Object类
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
//缓存Mapper方法 真正调用Mapper方法时会调用个invoke方法
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;
}
@UsesJava7
private Object invokeDefaultMethod(Object proxy, Method method, Object[] args)
throws Throwable {
final Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class
.getDeclaredConstructor(Class.class, int.class);
if (!constructor.isAccessible()) {
constructor.setAccessible(true);
}
final Class<?> declaringClass = method.getDeclaringClass();
return constructor
.newInstance(declaringClass,
MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED
| MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC)
.unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args);
}
/**
* Backport of java.lang.reflect.Method#isDefault()
*/
private boolean isDefaultMethod(Method method) {
return (method.getModifiers()
& (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC)) == Modifier.PUBLIC
&& method.getDeclaringClass().isInterface();
}
}
这回Mapper代理对象
调用newInstance生成JDK动态代理对象
MyBatis 执行SQL流程
//这里返回的是代理对象
AuthorMapper authorMapper = sqlSession.getMapper(AuthorMapper.class);
//这个执行SQL
authorMapper.getById(101);
org.apache.ibatis.binding.MapperProxy invode中打断点
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 {
try {
//判断是不理Object类
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
//缓存Mapper方法 真正调用Mapper方法时会调用个invoke方法
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;
}
...
}
方法是命名空间全名+方法名
参数
判断是否为Object类
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
//判断 是否为Object类
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (isDefaultMethod(method)) {
//判断是否为默认方法
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
//下面这2个方法才是最重要的
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
cachedMapperMethod
private final Map<Method, MapperMethod> methodCache;
....
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;
}
MapperMethod构造方法
public class MapperMethod {
private final SqlCommand command;
private final MethodSignature method;
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
this.command = new SqlCommand(config, mapperInterface, method);
this.method = new MethodSignature(config, mapperInterface, method);
}
...
方法签名
mapperMethod.execute(sqlSession, args);
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
case SELECT:
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
//转换参数
Object param = method.convertArgsToSqlCommandParam(args);
//底层还是执行sqlSession.selectOne
result = sqlSession.selectOne(command.getName(), param);
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + command.getName());
}
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException("Mapper method '" + command.getName()
+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
convertArgsToSqlCommandParam
public Object convertArgsToSqlCommandParam(Object[] args) {
return paramNameResolver.getNamedParams(args);
}
参数处理器
public class ParamNameResolver {
//通用参数前缀
private static final String GENERIC_NAME_PREFIX = "param";
//Map
private final SortedMap<Integer, String> names;
//是否有注解@Param
private boolean hasParamAnnotation;
public Object getNamedParams(Object[] args) {
final int paramCount = names.size();
if (args == null || paramCount == 0) {
return null;
} else if (!hasParamAnnotation && paramCount == 1) {
return args[names.firstKey()];
} else {
final Map<String, Object> param = new ParamMap<Object>();
int i = 0;
for (Map.Entry<Integer, String> entry : names.entrySet()) {
param.put(entry.getValue(), args[entry.getKey()]);
// 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;
}
}
}
selectOne
org.apache.ibatis.session.defaults.DefaultSqlSession
@Override
public <T> T selectOne(String statement, Object parameter) {
// Popular vote was to return null on 0 results and throw exception on too many.
List<T> list = this.<T>selectList(statement, parameter);
if (list.size() == 1) {
return list.get(0);
} else if (list.size() > 1) {
throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
} else {
return null;
}
}
@Override
public <E> List<E> selectList(String statement, Object parameter) {
return this.selectList(statement, parameter, RowBounds.DEFAULT);
}
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
//根据statement获取MappedStatement
//statement是 com.ghgcn.mybatis_demo.mapper.AuthorMapper.getById 这种类型,在解析Mappers节点时在Configuration中的有保存对应关系与SQL
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();
}
}
MappedStatement
public final class MappedStatement {
private String resource;
private Configuration configuration;
// SELECT UPDATE 标签的ID 方法名
private String id;
private Integer fetchSize;
private Integer timeout;
private StatementType statementType;
private ResultSetType resultSetType;
//中包含SQL语句
private SqlSource sqlSource;
private Cache cache;
private ParameterMap parameterMap;
private List<ResultMap> resultMaps;
private boolean flushCacheRequired;
private boolean useCache;
private boolean resultOrdered;
private SqlCommandType sqlCommandType;
private KeyGenerator keyGenerator;
private String[] keyProperties;
private String[] keyColumns;
private boolean hasNestedResultMaps;
private String databaseId;
private Log statementLog;
private LanguageDriver lang;
private String[] resultSets;
MappedStatement() {
// constructor disabled
}
public static class Builder {
private MappedStatement mappedStatement = new MappedStatement();
public Builder(Configuration configuration, String id, SqlSource sqlSource, SqlCommandType sqlCommandType) {
mappedStatement.configuration = configuration;
mappedStatement.id = id;
mappedStatement.sqlSource = sqlSource;
mappedStatement.statementType = StatementType.PREPARED;
mappedStatement.parameterMap = new ParameterMap.Builder(configuration, "defaultParameterMap", null, new ArrayList<ParameterMapping>()).build();
mappedStatement.resultMaps = new ArrayList<ResultMap>();
mappedStatement.sqlCommandType = sqlCommandType;
mappedStatement.keyGenerator = configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType) ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
String logId = id;
if (configuration.getLogPrefix() != null) {
logId = configuration.getLogPrefix() + id;
}
mappedStatement.statementLog = LogFactory.getLog(logId);
mappedStatement.lang = configuration.getDefaultScriptingLanguageInstance();
}
query方法 org.apache.ibatis.executor.BaseExecutor
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
//获取绑定的SQL及参数
BoundSql boundSql = ms.getBoundSql(parameter);
//建立缓存
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
//查询
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
query org.apache.ibatis.executor.CachingExecutor
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
Cache cache = ms.getCache();
if (cache != null) {
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, boundSql);
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
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);
}
真正
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++;
//判断有没有resultHandler 本地缓存
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {
//有就直接返回 一级缓存
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
//这里执行SQL
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;
}
queryFromDatabase org.apache.ibatis.executor.BaseExecutor
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 {
//调用子类的org.apache.ibatis.executor.SimpleExecutor
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;
}
org.apache.ibatis.executor.SimpleExecutor doQuery模版方法
@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
//获取Configuration
Configuration configuration = ms.getConfiguration();
//创建StatementHandler
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
//预编译
stmt = prepareStatement(handler, ms.getStatementLog());
//执行
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
prepareStatement org.apache.ibatis.executor.SimpleExecutor
激动人心的时刻到来了
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
//JDBC中的
Statement stmt;
//JDBC中的连接
Connection connection = getConnection(statementLog);
//
stmt = handler.prepare(connection, transaction.getTimeout());
handler.parameterize(stmt);
return stmt;
}
handler.prepare
@Override
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
//设置属性
statement = instantiateStatement(connection);
//超时时间
setStatementTimeout(statement, transactionTimeout);
//批量的数量
setFetchSize(statement);
return statement;
} catch (SQLException e) {
closeStatement(statement);
throw e;
} catch (Exception e) {
closeStatement(statement);
throw new ExecutorException("Error preparing statement. Cause: " + e, e);
}
}
instantiateStatement
@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
String sql = boundSql.getSql();
if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
String[] keyColumnNames = mappedStatement.getKeyColumns();
if (keyColumnNames == null) {
//预编译SQL
return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
} else {
//预编译SQL
return connection.prepareStatement(sql, keyColumnNames);
}
} else if (mappedStatement.getResultSetType() != null) {
//预编译SQL
return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
} else {
//预编译SQL
return connection.prepareStatement(sql);
}
}
设置参数
@Override
public void parameterize(Statement statement) throws SQLException {
parameterHandler.setParameters((PreparedStatement) statement);
}
return handler.query(stmt, resultHandler);
最终执行SQL
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.<E> handleResultSets(ps);
}
返回类型映射resultSetHandler. handleResultSets(ps)
@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);
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
//对ResultSet循环通过反射来获取值
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++;
}
}
文章目录
- MyBatis初化Configuration流程
- 示例
- new SqlSessionFactoryBuilder().build方法
- 1.XMLConfigBuilder对象构造
- 2. build()方法参数是XMLConfigBuilder
- parser.parse()返回Configruration
- parseConfiguration()解析mybatisConfig中的所有标签包含未设置的都会使用Mybatis中的默认设置
- propertiesElement对Properties标签中的变量解析
- Properties settings = settingsAsProperties(root.evalNode("settings"));
- 主要 看mapperElement()这个解析
- XMLMapperBuilder 用来解析Mapper.xml的构建器
- 看构造方法
- BaseBuilder
- MapperBuilderAssistant
- XMLMapperBuilder parse方法
- configurationElement方法
- cacheRefElement(context.evalNode("cache-ref"));
- 参照缓存
- 使用自定义缓存
- cacheElement(context.evalNode("cache"));
- parameterMapElement(context.evalNodes("/mapper/parameterMap"));
- resultMapElements(context.evalNodes("/mapper/resultMap"));
- buildResultMappingFromContext方法
- sqlElement(context.evalNodes("/mapper/sql")); 解析节点把SQL取出 根据id
- buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
- 解析
- 附2 加载Mapper.xml的集合上面的一loadedResources
- configuration.addLoadedResource(resource);
- bindMapperForNamespace ();方法绑定命名空间
- MappperRegistry
- MapperProxyFactory
- 添加addMapper
- build 方法
- MyBatis GetMapper流程
- openSession public class DefaultSqlSessionFactory implements SqlSessionFactory
- JdbcTransactionFactory
- 返回 return new DefaultSqlSession(configuration, executor, autoCommit);
- 执行器 final Executor executor = configuration.newExecutor(tx, execType);
- SimpleExecutor
- configuration->getMapper 这个就是Configuration加载时的Mapkey接口 VALUE是代理工厂
- MyBatis 执行SQL流程