Mybatis源码解析
首先是Mybatis的基本使用步骤
public class StudentMapperTest {
private static SqlSessionFactory sqlSessionFactory;
@BeforeClass
public static void init() {
try {
Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
//内部实际创建了一个Configuration对象,并以此创建出DefaultSqlSessionFactory对象
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
public void testSelectList() {
SqlSession sqlSession = null;
try {
sqlSession = sqlSessionFactory.openSession();
//这里实际上是通过executor对象调用方法
List<Student> students = sqlSession.selectList("com.demo.mapper.UserMapper.getUserByName");
//这里实际上是通过this.configuration.getMapper(type, this);来获取代理对象的。这个代理对象内部是通过sqlSession执行sql。
//而sqlSession内部实际上都是通过executor执行的。而一级缓存又在executor中所以,所有通过同一个sqlSession执行的方法共享一级缓存。
//sqlSessionFactory.openSession()如果再创建一个sqlSession,实际上内部的executor也是新创建的所以就不会与先前那个sqlSession共享一级缓存了。
Mapper mapper = sqlSession.getMapper(mapper.class);
for (int i = 0; i < students.size(); i++) {
System.out.println(students.get(i));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (sqlSession != null) {
sqlSession.close();
}
}
}
}
这里是具体的配置文件
<?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">
<!-- 通过这个配置文件完成mybatis与数据库的连接 -->
<configuration>
<!-- 引入数据源配置 database.properties 文件 -->
<properties resource="database.properties"></properties>
<!--配置mybatis 运行中的一些行为 -->
<settings>
<!-- 设置Mybatis的log实现为LOG4J -->
<setting name="logImpl" value="LOG4J"/>
</settings>
<typeAliases>
<!--
<typeAlias alias="User" type="com.zy.entity.User"/>
-->
<package name="cn.zy.entity"/>
</typeAliases>
<!-- 配置mybatis运行环境 -->
<environments default="dev">
<environment id="dev">
<!-- 采用jdbc事务管理 -->
<transactionManager type="JDBC"></transactionManager>
<!-- 采用Mybatis自带的数据源 POOLED -->
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${user}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<!-- 将mapper文件加入到配置文件中 -->
<mappers>
<mapper resource="cn/zy/dao/UserMapper.xml"/>
<package name="com.hytc.mapper" />
<package url="....." />
<package class="接口路径"/>
</mappers>
</configuration>
先放上通过sqlSession执行sql语句的过程这里是引用Coder648的博客
//此方法在SimpleExecutor的父类BaseExecutor中实现
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
//根据传入的参数动态获得SQL语句,最后返回用BoundSql对象表示
BoundSql boundSql = ms.getBoundSql(parameter);
//为本次查询创建缓存的Key
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
//进入query的重载方法中
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++;
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
// 如果缓存中没有本次查找的值,那么从数据库中查询
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;
}
//从数据库查询
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 {
// 查询的方法
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;
}
// SimpleExecutor中实现父类的doQuery抽象方法
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
// 传入参数创建StatementHanlder对象来执行查询
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
// 创建jdbc中的statement对象
stmt = prepareStatement(handler, ms.getStatementLog());
// StatementHandler进行处理
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
// 创建Statement的方法
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
//条代码中的getConnection方法经过重重调用最后会调用openConnection方法,从连接池中获得连接。
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection, transaction.getTimeout());
handler.parameterize(stmt);
return stmt;
}
//从连接池获得连接的方法
protected void openConnection() throws SQLException {
if (log.isDebugEnabled()) {
log.debug("Opening JDBC Connection");
}
//从连接池获得连接
connection = dataSource.getConnection();
if (level != null) {
connection.setTransactionIsolation(level.getLevel());
}
setDesiredAutoCommit(autoCommit);
}
//进入StatementHandler进行处理的query,StatementHandler中默认的是PreparedStatementHandler
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
//原生jdbc的执行
ps.execute();
//处理结果返回。
return resultSetHandler.handleResultSets(ps);
}
我们来根据基本使用来看一下源码
第一步
Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
获取mybatis配置文件的字节流对象
第二步
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
获取sqlSessionFactory对象,实际上这是一个接口,我们获取到的对象实际上是该接口的实现类DefaultSqlSessionFactory。
SqlSessionFactoryBuilder有关源码如下
build方法的参数
1、properties 实际上这是一个hashTable,存放了外部数据库连接池的位置信息。可以当参数传入,也可以在配置文件配置,因为最终将二者存放在一个default properties 中。作用是给数据库连接池提供配置数据(url username password)
2、reader读取mybatis配置文件的文件流对象 这是主要的
3、environment,这个参数的作用是当mybatis配置了多个数据源时,指定使用哪一个。
public class SqlSessionFactoryBuilder {
public SqlSessionFactoryBuilder() {
}
//从这可以看出build大体分为两类,主要是字符流,以及字符流。
public SqlSessionFactory build(Reader reader) {
return this.build((Reader)reader, (String)null, (Properties)null);
}
public SqlSessionFactory build(Reader reader, String environment) {
return this.build((Reader)reader, environment, (Properties)null);
}
public SqlSessionFactory build(Reader reader, Properties properties) {
return this.build((Reader)reader, (String)null, properties);
}
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
SqlSessionFactory var5;
try {
//parser创建出Configuration,并根据配置文件对其初始化
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
//var5就是调用build最终返回的对象(看最下面的方法),也就是DefaultSqlSessionFactory
var5 = this.build(parser.parse()传入一个Configuration);
}
....省略若干
return var5;
}
public SqlSessionFactory build(InputStream inputStream) {
return this.build((InputStream)inputStream, (String)null, (Properties)null);
}
public SqlSessionFactory build(InputStream inputStream, String environment) {
return this.build((InputStream)inputStream, environment, (Properties)null);
}
public SqlSessionFactory build(InputStream inputStream, Properties properties) {
return this.build((InputStream)inputStream, (String)null, properties);
}
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
SqlSessionFactory var5;
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
var5 = this.build(parser.parse());
}
....省略若干
return var5;
}
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
}
接下来是XMLConfigBuilder的源码,省略若干
public class XMLConfigBuilder extends BaseBuilder {
private boolean parsed;
private final XPathParser parser;
private String environment;
private final ReflectorFactory localReflectorFactory;
public XMLConfigBuilder(Reader reader) {
this((Reader)reader, (String)null, (Properties)null);
}
public XMLConfigBuilder(Reader reader, String environment) {
this((Reader)reader, environment, (Properties)null);
}
public XMLConfigBuilder(Reader reader, String environment, Properties props) {
//XPathParser是用来解析xml配置文件的
this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props);
}
public XMLConfigBuilder(InputStream inputStream) {
this((InputStream)inputStream, (String)null, (Properties)null);
}
public XMLConfigBuilder(InputStream inputStream, String environment) {
this((InputStream)inputStream, environment, (Properties)null);
}
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
}
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
//首先创建了Configuration对象。并传给了它的父类,同时也是parse返回的对象
super(new Configuration());
this.localReflectorFactory = new DefaultReflectorFactory();
ErrorContext.instance().resource("SQL Mapper Configuration");
this.configuration.setVariables(props);
this.parsed = false;
this.environment = environment;
this.parser = parser;
}
public Configuration parse() {
//这里的parsed是全局变量,不允许对configuration进行多次初始化
if (this.parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
} else {
this.parsed = true;
//这个方法实际上是xml配置文件每个标签对解析成以一个节点对象,并对configuration进行初始化
this.parseConfiguration(this.parser.evalNode("/configuration"));
//最终返回从父类继承过来的configuration对象
return this.configuration;
}
}
//这里是具体的方法,下列的方法中大部分通过这种形式this.configuration.setVfsImpl(vfsImpl)给configuration初始化。
private void parseConfiguration(XNode root) {
try {
this.propertiesElement(root.evalNode("properties"));
Properties settings = this.settingsAsProperties(root.evalNode("settings"));
this.loadCustomVfs(settings);
this.loadCustomLogImpl(settings);
this.typeAliasesElement(root.evalNode("typeAliases"));
this.pluginElement(root.evalNode("plugins"));
this.objectFactoryElement(root.evalNode("objectFactory"));
this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
this.reflectorFactoryElement(root.evalNode("reflectorFactory"));
this.settingsElement(settings);
this.environmentsElement(root.evalNode("environments"));
this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
this.typeHandlerElement(root.evalNode("typeHandlers"));
this.mapperElement(root.evalNode("mappers"));
} catch (Exception var3) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
}
}
private void loadCustomVfs(Properties props) {
this.configuration.setVfsImpl(vfsImpl);
}
private void loadCustomLogImpl(Properties props) {
this.configuration.setLogImpl(logImpl);
}
private void typeAliasesElement(XNode parent) {
this.configuration.getTypeAliasRegistry().registerAliases(alias);
}
private void pluginElement(XNode parent) throws Exception {
this.configuration.addInterceptor(interceptorInstance);
}
private void objectFactoryElement(XNode context) throws Exception { this.configuration.setObjectFactory(factory)
}
private void objectWrapperFactoryElement(XNode context) throws Exception {
this.configuration.setObjectWrapperFactory(factory);
}
private void reflectorFactoryElement(XNode context) throws Exception {
this.configuration.setReflectorFactory(factory);
}
private void propertiesElement(XNode context) throws Exception {
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.");
}
if (resource != null) {
defaults.putAll(Resources.getResourceAsProperties(resource));
} else if (url != null) {
//这一步是解析资源,返回一个propertise对象,并放进defaults
defaults.putAll(Resources.getUrlAsProperties(url));
}
//这里是获取build传入的Properties,因为前面设置了this.configuration.setVariables(props);
Properties vars = this.configuration.getVariables();
//如果不为空就合并。
if (vars != null) {
defaults.putAll(vars);
}
//设置的作用是提供将${user}替换成真实值的数据。
this.parser.setVariables(defaults);
//将最终合并后Properties给configuration赋值。注意Properties实际上是一个hashTable存放的连接数据库的配置
this.configuration.setVariables(defaults);
}
}
private void settingsElement(Properties props) {
this.configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
this.configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE")));
//开起二级缓存的配置
this.configuration.setCacheEnabled(this.booleanValueOf(props.getProperty("cacheEnabled"), true));
this.configuration.setProxyFactory((ProxyFactory)this.createInstance(props.getProperty("proxyFactory")));
this.configuration.setLazyLoadingEnabled(this.booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));
this.configuration.setAggressiveLazyLoading(this.booleanValueOf(props.getProperty("aggressiveLazyLoading"), false));
this.configuration.setMultipleResultSetsEnabled(this.booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));
this.configuration.setUseColumnLabel(this.booleanValueOf(props.getProperty("useColumnLabel"), true));
this.configuration.setUseGeneratedKeys(this.booleanValueOf(props.getProperty("useGeneratedKeys"), false));
this.configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));
this.configuration.setDefaultStatementTimeout(this.integerValueOf(props.getProperty("defaultStatementTimeout"), (Integer)null));
this.configuration.setDefaultFetchSize(this.integerValueOf(props.getProperty("defaultFetchSize"), (Integer)null));
this.configuration.setDefaultResultSetType(this.resolveResultSetType(props.getProperty("defaultResultSetType")));
this.configuration.setMapUnderscoreToCamelCase(this.booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));
this.configuration.setSafeRowBoundsEnabled(this.booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));
this.configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));
this.configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));
this.configuration.setLazyLoadTriggerMethods(this.stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));
this.configuration.setSafeResultHandlerEnabled(this.booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));
this.configuration.setDefaultScriptingLanguage(this.resolveClass(props.getProperty("defaultScriptingLanguage")));
this.configuration.setDefaultEnumTypeHandler(this.resolveClass(props.getProperty("defaultEnumTypeHandler")));
this.configuration.setCallSettersOnNulls(this.booleanValueOf(props.getProperty("callSettersOnNulls"), false));
this.configuration.setUseActualParamName(this.booleanValueOf(props.getProperty("useActualParamName"), true));
this.configuration.setReturnInstanceForEmptyRow(this.booleanValueOf(props.getProperty("returnInstanceForEmptyRow"), false));
this.configuration.setLogPrefix(props.getProperty("logPrefix"));
this.configuration.setConfigurationFactory(this.resolveClass(props.getProperty("configurationFactory")));
}
private void environmentsElement(XNode context) throws Exception {
if (context != null) {
//这里就是判断builed传过来的String environment参数了,如果不传使用默认数据源。当然这个默认的也是配置文件中指定的
if (this.environment == null) {
this.environment = context.getStringAttribute("default");
}
Iterator var2 = context.getChildren().iterator();
//这里是遍历所有的数据源,找到与environment相等名字的数据源
while(var2.hasNext()) {
XNode child = (XNode)var2.next();
String id = child.getStringAttribute("id");
if (this.isSpecifiedEnvironment(id)) {
TransactionFactory txFactory = this.transactionManagerElement(child.evalNode("transactionManager"));
//创建数据库连接池工厂对象。
DataSourceFactory dsFactory = this.dataSourceElement(child.evalNode("dataSource"));
DataSource dataSource = dsFactory.getDataSource();
//级联调用,实际上是返回键id,dataSource,txFactory封装在一起的一个对象
Builder environmentBuilder = (new Builder(id)).transactionFactory(txFactory).dataSource(dataSource);
//environmentBuilder.build()方法将上述三个参数作为environment的有参构造参数创建environment对象,environment其实也是对这三个参数的一个封装
this.configuration.setEnvironment(environmentBuilder.build());
}
}
}
}
//创建连接池工厂
private DataSourceFactory dataSourceElement(XNode context) throws Exception {
if (context != null) {
String type = context.getStringAttribute("type");
//这一步获取连接数据库的信息等等
Properties props = context.getChildrenAsProperties();
DataSourceFactory factory = (DataSourceFactory)this.resolveClass(type).newInstance();
factory.setProperties(props);
return factory;
} else {
throw new BuilderException("Environment declaration requires a DataSourceFactory.");
}
}
private void databaseIdProviderElement(XNode context) {
this.configuration.setDatabaseId(databaseId);
}
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
Iterator var2 = parent.getChildren().iterator();
while(true) {
while(var2.hasNext()) {
XNode child = (XNode)var2.next();
String resource;
//在这mappers分为两种,package分一种,其他url class resource分一种
if ("package".equals(child.getName())) {
resource = child.getStringAttribute("name");
this.configuration.addMappers(resource);
} else {
resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
XMLMapperBuilder mapperParser;
InputStream inputStream;
//从这可以看出对于每一个 mapper url class resource只能选择一种
if (resource != null && url == null && mapperClass == null) {
ErrorContext.instance().resource(resource);
inputStream = Resources.getResourceAsStream(resource);
//这里是对mapper.xml进行解析同解析mybatis配置文件类似
mapperParser = new XMLMapperBuilder(inputStream, this.configuration, resource, this.configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url != null && mapperClass == null) {
ErrorContext.instance().resource(url);
inputStream = Resources.getUrlAsStream(url);
mapperParser = new XMLMapperBuilder(inputStream, this.configuration, url, this.configuration.getSqlFragments());
mapperParser.parse();
} else {
if (resource != null || url != null || mapperClass == null) {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
//无论是什么方式最终都会将mapperInterface通过addMapper创建实现类对象,上面的也会调用this.configuration.addMapper()方法,存进configuration的属性mapperRegistry的一个map集合中Map<Class<?>, MapperProxyFactory<?>>集合中。而这个集合中的value是mapper的代理工厂,用来创建mapper的代理对象。在这里只是创建工厂,当调用getMapper是代理对象才被创建出来
Class<?> mapperInterface = Resources.classForName(mapperClass);
this.configuration.addMapper(mapperInterface);
}
}
}
return;
}
}
}
}
这里是对mapper操作的方法
public <T> void addMapper(Class<T> type) {
this.mapperRegistry.addMapper(type);
}
//这个MapperRegistry也是Configuration的一个成员变量
public class MapperRegistry {
private final Configuration config;
//this.mapperRegistry.addMapper(type)就是存放进knownMappers这个map中
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap();
public MapperRegistry(Configuration config) {
this.config = config;
}
//这个getMapper是返回一个Mapper的代理对象
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
//从knownMappers中获取,它的值是调用addMapper方法赋予的。而这个方法的调用是Configuration初始化时执行的。
MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
} else {
try {
//详细查看这个方法,这个方法创建了mapper代理对象
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception var5) {
throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
}
}
}
public <T> boolean hasMapper(Class<T> type) {
return this.knownMappers.containsKey(type);
}
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
if (this.hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
this.knownMappers.put(type, new MapperProxyFactory(type));
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(this.config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
this.knownMappers.remove(type);
}
}
}
}
public Collection<Class<?>> getMappers() {
return Collections.unmodifiableCollection(this.knownMappers.keySet());
}
//扫描包中的class加入knownMappers map集合中
public void addMappers(String packageName, Class<?> superType) {
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil();
//这个函数是扫描包中所有的class并且将类型为new IsA(superType)加进mapperSet中,就是下面返回的set集合
resolverUtil.find(new IsA(superType), packageName);
Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
Iterator var5 = mapperSet.iterator();
while(var5.hasNext()) {
Class<?> mapperClass = (Class)var5.next();
this.addMapper(mapperClass);
}
}
public void addMappers(String packageName) {
this.addMappers(packageName, Object.class);
}
}
mapperProxyFactory
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap();
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public Class<T> getMapperInterface() {
return this.mapperInterface;
}
public Map<Method, MapperMethod> getMethodCache() {
return this.methodCache;
}
protected T newInstance(MapperProxy<T> mapperProxy) {
return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
}
//为什么要传入sqlsession呢?实际上通过mapper代理对象执行的sql语句实际上是通过sqlsession来执行的,
public T newInstance(SqlSession sqlSession) {
MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
return this.newInstance(mapperProxy);
}
}
MapperProxy
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;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
//直接放行Object定义的方法
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
}
//直接放行默认方法。默认方法是接口中定义一个已实现方法,且该接口的实现类不需要实现该方法。为什么要有默认方法。因为如果没有默认方法,加入给JDK中的某个接口添加一个新的抽象方法,那么所有实现了该接口的类都得修改,影响将非常大。
if (method.isDefault()) {
return this.invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable var5) {
throw ExceptionUtil.unwrapThrowable(var5);
}
//创建出mapperMethod对象,这个对象内部通过调用sqlSession执行sql语句,同时MapperMethod内内有一个属性command,这个command对象封装了一个内部通过从configuration对象中的mappedStatements.get(mapperInterface.getName() + "." + methodName)获取mappedStatement。
//然后获取mappedStatement的id="com.demo.mapper.UserMapper.getUserByName"赋值给command。用来执行sqlSession.方法("com.demo.mapper.UserMapper.getUserByName",params)
MapperMethod mapperMethod = this.cachedMapperMethod(method);
return mapperMethod.execute(this.sqlSession, args);
}
private MapperMethod cachedMapperMethod(Method method) {
return (MapperMethod)this.methodCache.computeIfAbsent(method, (k) -> {
return new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration());
});
}
private Object invokeDefaultMethod(Object proxy, Method method, Object[] args) throws Throwable {
Constructor<Lookup> constructor = Lookup.class.getDeclaredConstructor(Class.class, Integer.TYPE);
if (!constructor.isAccessible()) {
constructor.setAccessible(true);
}
Class<?> declaringClass = method.getDeclaringClass();
return ((Lookup)constructor.newInstance(declaringClass, 15)).unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args);
}
}
以上都是通过读取mybatis配置文件创建Configuration对象,并为Configuration对象初始化的一个过程,主要发生在XMLConfigBuilder方法中,重点是通过读取配置信息创建了environment对象,这个对象封装了我们指定的数据库连接池对象,事务工厂对象,id。将mapper代理工厂对象存储在 MapperRegistry的knownMappers集合中
创建出Configuration对象后通过这个对象创建出new DefaultSqlSessionFactory(config)对象,再获取SqlSession对象,注意SqlSession中是封装了Configuration对象的
private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) {
DefaultSqlSession var8;
try {
boolean autoCommit;
try {
autoCommit = connection.getAutoCommit();
} catch (SQLException var13) {
autoCommit = true;
}
Environment environment = this.configuration.getEnvironment();
TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
Transaction tx = transactionFactory.newTransaction(connection);
//configuration.newExecutor源码如下,配合下面一张图
Executor executor = this.configuration.newExecutor(tx, execType);
var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
} catch (Exception var14) {
throw ExceptionFactory.wrapException("Error opening session. Cause: " + var14, var14);
} finally {
ErrorContext.instance().reset();
}
return var8;
}
private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {
return (TransactionFactory)(environment != null && environment.getTransactionFactory() != null ? environment.getTransactionFactory() : new ManagedTransactionFactory());
}
这里是Executor的继承体系
最底层的接口是Executor,有两个实现类:BaseExecutor和CachingExecutor,CachingExecutor用于二级缓存,而BaseExecutor则用于一级缓存及基础的操作。
1、SimpleExecutor是最简单的执行器,根据对应的sql直接执行即可,不会做一些额外的操作;
2、BatchExecutor执行器,顾名思义,通过批量操作来优化性能。通常需要注意的是批量更新操作,由于内部有缓存的实现,使用完成后记得调用flushStatements来清除缓存。
3、ReuseExecutor 可重用的执行器,重用的对象是Statement,也就是说该执行器会缓存同一个sql的Statement,省去Statement的重新创建,优化性能。内部的实现是通过一个HashMap来维护Statement对象的。由于当前Map只在该sqlsession中有效,所以使用完成后记得调用flushStatements来清除Map。
public class Configuration {
//成员变量,通过读取配置文件进行初始化,是否开启二级缓存
protected boolean cacheEnabled;
//mapper中的每一条sql语句都会被封装成一个MappedStatement对象key = 全限定类名 + 方法名,value = 对应的MappedStatement对象
protected final Map<String, MappedStatement> mappedStatements;
//内部存放了mapper代理工厂对象,以及通过getMapper方法创建出代理对象并返回
protected final MapperRegistry mapperRegistry;
//这个对象主要封装了数据库连接池对象,事务对象,id
protected Environment environment;
//这个主要是连接数据库的信息,如user=root password=1234 等实际上是一个hashTable
protected Properties variables;
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? this.defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Object 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 (this.cacheEnabled) {
executor = new CachingExecutor((Executor)executor);
}
Executor executor = (Executor)this.interceptorChain.pluginAll(executor);
return executor;
}
}
先来看看BaseExecutor的源码
public abstract class BaseExecutor implements Executor {
//这个就是一级缓存,内部封装了一个hashMap private Map<Object, Object> cache = new HashMap();而BaseExecutor又是sqlsession的一个属性所以说一级缓存的范围是sqlsession
protected PerpetualCache localCache;
//这个query的调用是通过sqlsession对象来调用的,当通过sqlsession调用方法时内部就调用了BaseExecutor的方法来执行sql语句
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 (this.closed) {
throw new ExecutorException("Executor was closed.");
} else {
if (this.queryStack == 0 && ms.isFlushCacheRequired()) {
this.clearLocalCache();
}
List list;
try {
++this.queryStack;
//先判断缓存中有没有
list = resultHandler == null ? (List)this.localCache.getObject(key) : null;
if (list != null) {
//注意这个key值是由CacheKey key = this.createCacheKey(ms, parameter, rowBounds, boundSql);这几个参数共同影响的
this.handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
//这里是数据库中真正的查询。
list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
--this.queryStack;
}
if (this.queryStack == 0) {
Iterator var8 = this.deferredLoads.iterator();
while(var8.hasNext()) {
BaseExecutor.DeferredLoad deferredLoad = (BaseExecutor.DeferredLoad)var8.next();
deferredLoad.load();
}
this.deferredLoads.clear();
if (this.configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
this.clearLocalCache();
}
}
return list;
}
}
}
下面来看看DefaultSqlSession的源码
public class DefaultSqlSession implements SqlSession {
private final Configuration configuration;
private final Executor executor;
private final boolean autoCommit;
//从这看出通过Sqlsession执行sql语句实际上是通过executor.query来执行的。
public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
try {
MappedStatement ms = this.configuration.getMappedStatement(statement);
this.executor.query(ms, this.wrapCollection(parameter), rowBounds, handler);
} catch (Exception var9) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + var9, var9);
} finally {
ErrorContext.instance().reset();
}
}
}
关于二级缓存,注意二级缓存并不是只有一个,对于每一个MappedStatement有一个cache属性就是该sql语句对应的二级缓冲区。而每个MappedStatement在初始化Configuration时创建出来的。然后把cache作为key值通过获取value=TransactionalCache,TransactionalCache内部有个临时二级缓存,把cache传进去内部有方法flushPendingEntries将临时与cache之间进行数据赋值。
public class CachingExecutor implements Executor {
private final Executor delegate;
private final TransactionalCacheManager tcm = new TransactionalCacheManager();
//这里传入的Executor是上文创建的三种Executor之一
public CachingExecutor(Executor delegate) {
this.delegate = delegate;
delegate.setExecutorWrapper(this);
}
public Transaction getTransaction() {
return this.delegate.getTransaction();
}
public void close(boolean forceRollback) {
try {
if (forceRollback) {
this.tcm.rollback();
} else {
this.tcm.commit();
}
} finally {
this.delegate.close(forceRollback);
}
}
public boolean isClosed() {
return this.delegate.isClosed();
}
public int update(MappedStatement ms, Object parameterObject) throws SQLException {
//注意这一点,如果再mybatis配置中配置了flushCache属性,那么再update时会清空临时缓冲区。否则只清理真正的二级缓冲区
this.flushCacheIfRequired(ms);
return this.delegate.update(ms, parameterObject);
}
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameterObject);
CacheKey key = this.createCacheKey(ms, parameterObject, rowBounds, boundSql);
return this.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
public <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException {
this.flushCacheIfRequired(ms);
return this.delegate.queryCursor(ms, parameter, rowBounds);
}
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
//重点是这个真正存放二级缓存的地方,这个是每一个MappedStatement共用一个。所以二级缓存才能在不同sqlSession共享数据
Cache cache = ms.getCache();
if (cache != null) {
this.flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
this.ensureNoOutParams(ms, boundSql);
//先从二级缓存中取,如果map中不存在cache为key的value 那么就将cache作为参数创建TransactionalCache对象,并通过key从cache中获取数据。
List<E> list = (List)this.tcm.getObject(cache, key);
if (list == null) {
//这里是走一级缓存读取数据库的路子
list = this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
//这里是将读取的数据放进临时二级缓存只有执行commit之后才会将临时二级缓存中的数据放进MappedStatement中的cache中
this.tcm.putObject(cache, key, list);
}
return list;
}
}
return this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
public void commit(boolean required) throws SQLException {
this.delegate.commit(required);
//实际上调用了TransactionalCache,中的commit方法
//这里的commit会将临时二级缓冲区中存进真正的二级缓冲区,并清空临时二级缓冲区,如果在调用commit之前调用了clear那么会先清理掉二级缓冲区再将临时缓冲区存进二级缓冲区
this.tcm.commit();
}
}
TransactionalCache类中的方法
public void clear() {
this.clearOnCommit = true;
this.entriesToAddOnCommit.clear();
}
public void commit() {
if (this.clearOnCommit) {
this.delegate.clear();
}
this.flushPendingEntries();
this.reset();
}
public void rollback() {
this.unlockMissedEntries();
this.reset();
}
private void reset() {
this.clearOnCommit = false;
this.entriesToAddOnCommit.clear();
this.entriesMissedInCache.clear();
}
private void flushPendingEntries() {
Iterator var1 = this.entriesToAddOnCommit.entrySet().iterator();
while(var1.hasNext()) {
Entry<Object, Object> entry = (Entry)var1.next();
this.delegate.putObject(entry.getKey(), entry.getValue());
}
var1 = this.entriesMissedInCache.iterator();
while(var1.hasNext()) {
Object entry = var1.next();
if (!this.entriesToAddOnCommit.containsKey(entry)) {
this.delegate.putObject(entry, (Object)null);
}
}
}
主要的过程就是这样了。我自己觉得思路写的不是很清晰,如果有错误请大家指出我会改正。写这个文章的目的主要是为了记笔记,同时也希望能够帮助别人。