Mybatis是支持定制化SQL、存储过程和高级映射的持久层框架。主要完成两件事:
mybatis的主要目的就是管理执行SQL是参数的输入和输出,编写SQL和结果集的映射是mybatis的主要优点
try {
// 1.mybatis配置文件
String resources = "mybatis.xml";
// 2.获取Reader对象
Reader resourceAsReader = Resources.getResourceAsReader(resources);
// 3.获取SqlSessionFactoryBuilder
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsReader);
// 4.创建对应的session
SqlSession sqlSession = build.openSession();
// 5.获取对应的mapper
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 6.执行方法
UserEntity user = userMapper.getUser(1);
System.out.println("name:" + user.getName());
} catch (Exception e) {
e.printStackTrace();
}
Reader resourceAsReader = Resources.getResourceAsReader(resources);
public static Reader getResourceAsReader(String resource) throws IOException {
Reader reader;
if (charset == null) {
reader = new InputStreamReader(getResourceAsStream(resource));
} else {
reader = new InputStreamReader(getResourceAsStream(resource), charset);
}
return reader;
}
通过resource(配置文件路径)获取reader对象
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsReader);
1.进入到SqlSessionFactoryBuilder().build(resourceAsReader)方法
public SqlSessionFactory build(Reader reader) {
return build(reader, null, null);
}
2.进入源码最终执行的方法是
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
try {
//XMLConfigBuilder是对mybatis的配置文件进行解析的类,会对myabtis解析后的信息存放在Configuration对象中
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
reader.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
3.进入XMLConfigBuilder对象
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
super(new Configuration());
ErrorContext.instance().resource("SQL Mapper Configuration");
this.configuration.setVariables(props);
this.parsed = false;
this.environment = environment;
this.parser = parser;
}
4.通过build(parser.parse())方法
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
通过源码可以知道XMLConfigBuilder只能使用一次,被parsed属性控制执行一次parsed会被设置成true
5.通过parseConfiguration(parser.evalNode("/configuration"))
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
// 解析元素properties,保存在variables中
propertiesElement(root.evalNode("properties"));
// 解析元素typeAliases,保存在typeAliasRegistry中
typeAliasesElement(root.evalNode("typeAliases"));
// 解析插元素plugins,保存在interceptorChain中
pluginElement(root.evalNode("plugins"));
// 解析元素objectFactory,保存在objectFactory中
objectFactoryElement(root.evalNode("objectFactory"));
// 解析元素objectWrapperFactory,保存在objectWrapperFactory中
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
// 解析元素reflectorFactory,保存在reflectorFactory中
reflectionFactoryElement(root.evalNode("reflectionFactory"));
// 解析元素settings,保存在configuration的属性中
settingsElement(root.evalNode("settings"));
// read it after objectFactory and objectWrapperFactory issue #631
//解析environments 可以配置多个运行环境,但是每个SqlSessionFactory 实例只能选择一个运行环境
environmentsElement(root.evalNode("environments"));
//解析databaseIdProvider MyBatis能够执行不同的语句取决于你提供的数据库供应商。许多数据库供应商的支持是基于databaseId映射
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
//解析typeHandlers 当MyBatis设置参数到PreparedStatement 或者从ResultSet 结果集中取得值时,就会使用TypeHandler 来处理数据库类型与java 类型之间转换
typeHandlerElement(root.evalNode("typeHandlers"));
//解析mappers 主要的crud操作都是在mappers中定义的
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
6. 通过environmentsElement(root.evalNode("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)) {
//事务配置解析,获得事务工厂(JDBC和MANAGED两种)
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());
}
}
}
}
通过源码可以知道该方法读取配置文件数据库配置
7. 通过mapperElement(root.evalNode("mappers"))
//mapper元素解析,获得各种crud操作或者配置文件
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
// 对应配置方式(4),查找属性name指定包下所有的接口类型,注册到mapperRegistry
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) {
// 对应配置方式(1),解析resource属性指定的mapper配置文件并添加配置到mapperRegistry
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) {
// 对应配置方式(2),解析url属性指定的mapper配置文件并添加配置到mapperRegistry
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) {
// 对应配置方式(3),添加class属性指定的mapper配置到mapperRegistry
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.");
}
}
}
}
}
通过上面源码可以知道mybatis解析mapper的方法一共有四种 package、resource、url、class
8. 通过mapperParser.parse()该方法在XMLMapperBuilder中
public void parse() {
if (!configuration.isResourceLoaded(resource)) {
configurationElement(parser.evalNode("/mapper"));
configuration.addLoadedResource(resource);
bindMapperForNamespace();
}
//下边三个方法都是存储信息到configuration中。
parsePendingResultMaps();
parsePendingChacheRefs();
parsePendingStatements();
}
XMLMapperBuilder这个类主要是用于解析mybatis中的
其实XMLMapperBuilder和XMLConfigBuilder的功能比较相似,只是XMLConfigBuilder的作用范围,或者说包括的范围比较大,其中
通过
private void bindMapperForNamespace() {
String namespace = builderAssistant.getCurrentNamespace();
if (namespace != null) {
Class> boundType = null;
try {
//得到mapper接口实现类,实现映射
boundType = Resources.classForName(namespace);
} catch (ClassNotFoundException e) {
//ignore, bound type is not required
}
if (boundType != null) {
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
configuration.addLoadedResource("namespace:" + namespace);
configuration.addMapper(boundType);
}
}
}
}
我们知道每个mapper配置文件的namespace属性对应于某个接口,应用程序通过接口访问mybatis时,mybatis会为这个接口生成一个代理对象,这个对象就叫mapper对象,在生成代理对象前mybatis会校验接口是否已注册,未注册的接口会产生一个异常。为了避免这种异常,就需要注册mapper类型。这个步骤是在XMLMapperBuilder的bindMapperForNamespace方法中完成的。它通过调用Configuration对象的addMapper方法完成,而Configuration对象的addMapper方法是通过MapperRegistry的addMapper方法完成的,它只是简单的将namespace属性对应的接口类型存入本地缓存中。另外,我们使用Mapper接口的某一个方法时,MyBatis会根据这个方法的方法名和参数类型,确定Statement Id,底层还是通过SqlSession.select("statementId",parameterObject);
上述源码中通过configuration.addLoadedResource("namespace:" + namespace)、configuration.addMapper(boundType);将namespace添加到protected final Set
protected final Set loadedResources = new HashSet();
public void addLoadedResource(String resource) {
loadedResources.add(resource);
}
将mapper添加到protected MapperRegistry mapperRegistry = new MapperRegistry(this)中,mapperRegistry是由private final Map
protected MapperRegistry mapperRegistry = new MapperRegistry(this);
public void addMapper(Class type) {
mapperRegistry.addMapper(type);
}
MapperRegistry类
private final Map, MapperProxyFactory>> knownMappers = new HashMap, MapperProxyFactory>>();
public void addMapper(Class 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(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);
}
}
}
}
备注:mapperRegistry存放当前所有的mapper文件
使用Configuration获取默认的DefaultSqlSessionFactory