Mybatis底层深入理解
public class MybatisDome {
public static void main(String[] args) throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("Mybatis.xml");
//mybatis初始化发生在下面的这一步
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = build.openSession();
List<Object> objects = sqlSession.selectList("com.ssm.dao.UserMapper.selectAllUser");
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
System.out.println(mapper.selectAllUser());
System.out.println(objects);
}
}
在mybatis初始化时,会通过加载mybatis主配置文件最终形成一个Configuration,里面包含了mbatis主配置文件的所有信息
//上面的build方法最终会进入这个方法执行
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
//解析mybatis主配置文件和约束文件.dtd 最终会解析成XPathParser里面的一个个Document对象
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
//下面这个方法是解析 上面的XMLConfigBuilder 对象
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.
}
}
}
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
//进入这个方法 parser就是XPathParser其中parser.evalNode获取到mybatis主配置文件中的 configuration标签中的所有信息
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
//这段代码就是会具体解析configuration标签中的子标签,熟悉mybatis主配置文件的都可以看出来下面的标签全
//都是configuration中的子标签 ,最终会把解析结果放入到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);
}
}
//这个标签是解析environments这个标签的代码
/**
也就是这部分的配置
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
*/
private void environmentsElement(XNode context) throws Exception {
if (context != null) {
if (environment == null) {
environment = context.getStringAttribute("default");
}
for (XNode child : context.getChildren()) {
String id = child.getStringAttribute("id");
if (isSpecifiedEnvironment(id)) {
TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
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());
}
}
}
}
//解析transactionManager标签会根据你的type属性中的值具体属性值生成最终的事务管理机制,主要又下面几种
private TransactionFactory transactionManagerElement(XNode context) throws Exception {
if (context != null) {
String type = context.getStringAttribute("type");
Properties props = context.getChildrenAsProperties();
TransactionFactory factory = (TransactionFactory) resolveClass(type).newInstance();
factory.setProperties(props);
return factory;
}
throw new BuilderException("Environment declaration requires a TransactionFactory.");
}
//解析dataSource标签会根据type生成具体的数据源,主要有以下几种
private DataSourceFactory dataSourceElement(XNode context) throws Exception {
if (context != null) {
String type = context.getStringAttribute("type");
Properties props = context.getChildrenAsProperties();
DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance();
factory.setProperties(props);
return factory;
}
throw new BuilderException("Environment declaration requires a DataSourceFactory.");
}
//解析mapers标签会根据你不同标签进行不同的操作
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
String mapperPackage = child.getStringAttribute("name");
configuration.addMappers(mapperPackage);
} else {
String resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
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());
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());
mapperParser.parse();
} else if (resource == null && url == null && mapperClass != null) {
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.");
}
}
}
}
}
//上面的判断最终都会执行这个方法
public void parse() {
if (!configuration.isResourceLoaded(resource)) {
需要注意的是下面这个标签是mapper映射文件中的mapper标签,也就是下面的标签
/**
<mapper namespace="com.ssm.dao.UserMapper">
<select id="selectAllUser" resultType="com.ssm.pojo.User">
select * from user
</select>
<insert id="insertUser" parameterType="com.ssm.pojo.User">
insert into user(id,username,password) values(#{user.id},#{user.username},#{user.password})
</insert>
</mapper>
*/
configurationElement(parser.evalNode("/mapper"));
为了方便我把这个解析代码放出来
/**
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"));
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
resultMapElements(context.evalNodes("/mapper/resultMap"));
sqlElement(context.evalNodes("/mapper/sql"));
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);
}
}
*/
下面这这个方法就是解析mapper映射文件的具体标签,比如insert|update|select|delete标签
会把一个标签解析成一个对应的MappedStatement对象,最终将这个MappedStatement对象放入到
Configuration对象的一个名为mappedStatements中,需要注意的是mappedStatements是一个Map
key为MappedStatement的id属性zhi,value为MappedStatement对象本身
,最终执行Configuration中的方法
/**
public void addMappedStatement(MappedStatement ms) {
mappedStatements.put(ms.getId(), ms);
}
*/
configuration.addLoadedResource(resource);
bindMapperForNamespace();
最终会执行MapperRegistry这个方法 ,这个方法就是进行Map的put操作,具体怎么实现可以自行添加
断点debug跟一下
/**
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);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
}
*/
knownMappers是一个Map 其中key是对应的class,value是一个MapperProxyFactory,这个为什么是
MapperProxyFactory,是因为在后面做jdk的动态代理时要用
}
parsePendingResultMaps();
parsePendingCacheRefs();
parsePendingStatements();
}
下面有两种执行查询的方法
//第一种是通过sqlsession的api方法传进入具体的statementIed(唯一对应一个mapper接口中的sql语句)和参数执行的
//会根据传入statementid查询Configuration中的mappedStatements的map集合得到Mappedstatement对象
//然后经过一系列参数配置,获取数据库连接执行
List<Object> objects = sqlSession.selectList("com.ssm.dao.UserMapper.selectAllUser");
//第二个是通过得到mapper接口的代理类通过反射的方式执行的,这个生成代理的方法在和Spring整合时也会使用,在和Spring进
//行整合时每个mapper接口会被生成一个MapperFactoryBean,调用getObject()生成代理类,进而生成查询语句
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mybatis的一级缓存的key由 ( MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql)这几个参数生成