Mybatis执行原理
- 1.获取SqlSessionFactory
- 2.创建SqlSession
- 3.创建Mapper、执行SQL
MyBatis 是一款优秀的持久层框架,MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录。
Mybatis中的mapper接口并没有具体实现,那么他是如何执行SQL的?
mybatis-config.xml配置
<!-- mybatis-config.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><environments default="development"><environment id="development"><!-- 使用jdbc事务管理 --><transactionManager type="JDBC" /><!-- 数据库连接池 --><dataSource type="POOLED"><property name="driver" value="com.mysql.jdbc.Driver" /><property name="url"value="jdbc:mysql://127.0.0.1:3306/demo?characterEncoding=utf8"/><property name="username" value="" /><property name="password" value="" /></dataSource></environment></environments><!-- 加载mapper.xml --><mappers><!-- <package name=""> --><mapper resource="mapper/DemoMapper.xml"></mapper></mappers>
</configuration>
从一个简单的mybatis示例开始源码分析:
public static void main(String[] args) throws Exception {String resource = "mybatis-config.xml";InputStream inputStream = Resources.getResourceAsStream(resource);//创建SqlSessionFacorySqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);SqlSession sqlSession = sqlSessionFactory.openSession();//获取MapperDemoMapper mapper = sqlSession.getMapper(DemoMapper.class);Map<String,Object> map = new HashMap<>();map.put("id","1");System.out.println(mapper.selectAll(map));sqlSession.close();
}
1.获取SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
new SqlSessionFactoryBuilder().build()方法得到SqlSessionFactory实例,并解析mybatis-config.xml文件configuration节点数据源配置及mapper配置到Configuration对象,Configuration是SqlSessionFactory的一个属性。
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {try {XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);return build(parser.parse());} catch (Exception e) {throw ExceptionFactory.wrapException("Error building SqlSession.", e);} finally {ErrorContext.instance().reset();try {if (inputStream != null) {inputStream.close();}} catch (IOException e) {// Intentionally ignore. Prefer previous error.}}
}public SqlSessionFactory build(Configuration config) {return new DefaultSqlSessionFactory(config);
}
XMLConfigBuilder解析配置并组装SQL语句:
public Configuration parse() {if (parsed) {throw new BuilderException("Each XMLConfigBuilder can only be used once.");}parsed = true;// 解析mybatis-config.xml文件configuration节点数据源配置及mapper配置到Configuration对象parseConfiguration(parser.evalNode("/configuration"));return configuration;
}private void parseConfiguration(XNode root) {try {// issue #117 read properties firstpropertiesElement(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 #631environmentsElement(root.evalNode("environments"));databaseIdProviderElement(root.evalNode("databaseIdProvider"));typeHandlerElement(root.evalNode("typeHandlers"));// 读取mapper配置mapperElement(root.evalNode("mappers"));} catch (Exception e) {throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);}
}
2.创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
通过SqlSession工厂类创建SqlSession,并增加事务处理、执行器等。这里将SqlSessionFactory中的Environment传递给了SqlSession,Environment中包含DataSource,用于创建数据库连接。
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level,boolean autoCommit) {Transaction tx = null;try {final Environment environment = configuration.getEnvironment();final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);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();}}
3.创建Mapper、执行SQL
DemoMapper mapper = sqlSession.getMapper(DemoMapper.class);
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {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);}
}
在XMLConfigBuilder.parseConfiguration()中configuration.addMapper(mapperInterface)方法对knownMappers初始化,将所有Mapper的代理类放入knownMappers。
knownMappers.put(type, new MapperProxyFactory<>(type));
sqlSession.getMapper()从knownMappers获取代理类对象。
mapper.selectAll(map);
代理类通过MapperProxy.invoke()方法执行,自定义代理类会执行cachedInvoker()方法。
public class MapperProxy<T> implements InvocationHandler, Serializable {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {if (Object.class.equals(method.getDeclaringClass())) {return method.invoke(this, args);}return cachedInvoker(method).invoke(proxy, method, args, sqlSession);} catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);}}
}
最终通过mapperMethod.execute()方法执行对应的SQL语句。
private static class PlainMethodInvoker implements MapperMethodInvoker {private final MapperMethod mapperMethod;public PlainMethodInvoker(MapperMethod mapperMethod) {this.mapperMethod = mapperMethod;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {return mapperMethod.execute(sqlSession, args);}
}