Mybatis解析
select * from user where name =? and pwd=?
-
statement:select * from user where name = and pwd=(采用直接拼接的方式,有sql注入的风险)
-
preparestatement: **~.setString(1,‘name’) ~.setString(2,‘pwd’)** select * from user where name =‘name’andpwd=′‘{name}’ and pwd='‘name’andpwd=′‘{pwd}’' (采用String拼接的方式,没有sql注入的风险)
Mybatis的执行流程图
mybatis的四大对象:
流程如下:
Exeutor
发起sql执行任务
1、先调用statementHandler
中的prepare()
进行SQL的编译
2、然后调用statementHandler
中的parameterize()
设置参数
2.1、这里其实真正设置参数的是`ParameterHandler`中的`setparameters()`方法,该方法与`typeHandler`进行参数类型的转换
3、然后执行query/update方法,这里使用ResultSetHandler
进行结果的组装工作
3.1、这里`ResultSetHandler`又与`typeHandler`、`ObjectFactory`配合工作共同完成结果的组装工作
每一条sql语句都会绑定对应的对象:
MappedStatement:sql的ID、缓存信息、resultType、ParameterType、resultMap等信息
(mapper.xml文件就是被解析成这个对象);
Sqlsource:是MappedStatement的一个属性,是一个接口,主要提供BoundSql;
BoudSql:是建立SQL和参数的地方,有三个主要属性,ParameterMappings、ParameterObject和sql,这个对象比较重要,我们通常使用插件(实现接口Interceptor
+注解@Intercepts+mybatiscong.xml的plugin标签配置)就是对它进行拦截;
解析config.xml、mapper.xml:
@Before
public static void initFactory() {
try {
SqlSession session = null;
String resource = "configuration.xml";
// 使用io流读取配置
InputStream inputStream;
inputStream = Resources.getResourceAsStream(resource);
//这里是解析配置文件
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 得到了一个会话,有了这个会话,你就可以对数据进行增,删,改,查的操作
session = sqlSessionFactory.openSession();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
=======》new SqlSessionFactoryBuilder().build(inputStream);
return build(inputStream, null, null);
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse());
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));//解析节点
//全局的配置文件就会被解析成一个org.apache.ibatis.session.Configuration
return configuration;
====parser.parse()调用========》parseConfiguration(parser.evalNode("/configuration"));//解析节点
private void parseConfiguration(XNode root) {
try {
//标签配置顺序:
//properties,settings,typeAliases,typeHandlers,objectFactory,objectWrapperFactory, //plugins,environments,databaseIdProvider,mappers
//issue #117 read properties first
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
loadCustomLogImpl(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"));//解析mapper节点
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
/**
*mapper接口的方法同时写xml配置和注解配置的话会报错
*xml配置和注解配置都会被mybatis翻译成MappedStatement对象(两个mappedStatement的id相同(就是方法名),用的缓存容器是Hashmap(继承了hashmap的一个StateMap,重写了put())所以会报错)
*<mappers>
//如何只找到mapper文件的(mapper接口+*mapper.xml)
<package name="mapper" />
<!-- <mapper class="" resource="" url=""/> -->
</mappers>
*/
=============》 mapperElement(root.evalNode("mappers"));//解析mapper节点
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
//解析mapper/package节点
if ("package".equals(child.getName())) {
String mapperPackage = child.getStringAttribute("name");
configuration.addMappers(mapperPackage);//各种各样的mapper文件
} else {
String resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
//解析你写的<mapper>注解的这三个属性(看源码可以看出这三个属性你只能配置一个属性)
/**
*resource/url是资源定位符(就是路径)
* mapperClass你直接指定了一个接口
*/
if (resource != null && url == null && mapperClass == null) {
//配置了resource
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) {
//配置了url
ErrorContext.instance().resource(url);
InputStream inputStream = Resources.getUrlAsStream(url);//拿到mapper.xml文件的输入流
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
mapperParser.parse();//解析
} else if (resource == null && url == null && mapperClass != null) {
//配置了mapperClass(直接拿接口了)
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.");
}
}
}
}
}
===============》configuration.addMappers(mapperPackage);//各种各样的mapper文件
public void addMappers(String packageName, Class<?> superType) {
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
for (Class<?> mapperClass : mapperSet) {
addMapper(mapperClass);//干活的地方
}
}
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
//拿接口
if (hasMapper(type)) {
//验证你是不是有一个加载的mapper接口了
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
knownMappers.put(type, new