一周学完MyBatis源码,万字总结

1. MyBatis简介

MyBatis是一款开源的持久化框架,它实现了ORM(对象关系映射)的思想,将Java对象和数据库表之间的映射关系配置在XML文件中。它可以大大减少编码量,同时提高数据库的效率,被广泛应用于Java应用程序中。

2. MyBatis源码结构

MyBatis的源码包含以下几个目录和文件:

2.1. core

该目录下包含MyBatis核心代码,比如SqlSession和Configuration等,也就是我们使用MyBatis必须要依赖的类,是MyBatis的核心部分。

2.2. io

该目录下包含MyBatis IO相关的类,比如VFS等,主要用于处理文件系统和JAR包中的资源访问。

2.3. jdbc

该目录下包含MyBatis JDBC相关的类,比如DataSource等,主要是用于管理数据库的连接和事务。

2.4. logging

该目录下包含MyBatis日志相关的类,比如LogFactory和Log等,主要是用于日志记录和调试。

2.5. plugin

该目录下包含MyBatis插件相关的类,主要是用于拦截MyBatis的执行过程,可以在不修改MyBatis源码的情况下进行自定义扩展。

2.6. scripting

该目录下包含MyBatis动态语言相关的类,比如ScriptingLanguageDriver等,主要是用于支持动态SQL。

2.7. session

该目录下包含MyBatis会话相关的类,比如Executor,StatementHandler和ResultSetHandler等,主要是用于处理数据库的操作。

2.8. transaction

该目录下包含MyBatis事务相关的类,比如Transaction,TransactionFactory和TransactionIsolationLevel等,主要是用于管理数据库事务。

3. MyBatis源码学习方法

MyBatis源码是学习和掌握MyBatis的重要途径之一,但是由于MyBatis源码量大,逻辑复杂,代码注释少等原因,对初学者来说是有难度的。下面是MyBatis源码学习的一些方法和建议:

3.1. 掌握MyBatis的基本用法

在学习MyBatis源码之前,必须先掌握MyBatis的基本用法,比如SqlSession的使用,Mapper和XML文件的配置等。

3.2. 熟悉MyBatis的设计模式

MyBatis采用了很多设计模式,比如工厂模式、单例模式、代理模式、装饰器模式等,熟悉这些设计模式对于理解MyBatis源码是非常有帮助的。

3.3. 学习MyBatis的关键类

MyBatis其他类都是围绕Configuration、SqlSession和Executor等关键类展开的,因此需要重点学习这些关键类的实现原理和设计思路。

3.4. 阅读MyBatis的单元测试

MyBatis的源码中包含了大量的单元测试代码,这些代码可以帮助我们理解MyBatis的实现原理和设计思路。

4. MyBatis源码分析

下面对MyBatis源码的一些重要部分进行分析,便于大家更好地理解MyBatis的实现原理和设计思路。

4.1. Configuration

Configuration是MyBatis的核心配置类,它负责管理MyBatis的所有配置信息,包括数据库连接信息、映射关系、缓存配置、类型处理器等。在SqlSessionFactoryBuilder.build()方法中会使用Configuration来构建SqlSessionFactory。

public class SqlSessionFactoryBuilder {

public SqlSessionFactory build(InputStream inputStream) {

return build(inputStream, null, null);

}

public SqlSessionFactory build(InputStream inputStream, String environment) {

return build(inputStream, environment, null);

}

public SqlSessionFactory build(InputStream inputStream, Properties properties) {

return build(inputStream, null, properties);

}

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 {

inputStream.close();

} catch (IOException e) {

// Intentionally ignore. Prefer previous error.

}

}

}

public SqlSessionFactory build(Configuration config) {

return new DefaultSqlSessionFactory(config);

}

}

Configuration是单例模式,通过XMLConfigBuilder.parse()方法来解析XML配置文件,生成Configuration对象,然后使用该对象创建DefaultSqlSessionFactory。

public Configuration parse() {

if (parsed) {

throw new BuilderException("Each XMLConfigBuilder can only be used once.");

}

parsed = true;

parseConfiguration(parser.evalNode("/configuration"));

return configuration;

}

private void parseConfiguration(XNode root) {

try {

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"));

} catch (Exception e) {

throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);

}

}

4.2. SqlSession

SqlSession是MyBatis的面向用户的接口,它对外提供了对数据库的增删改查操作,包括查询单个对象、查询对象列表、插入对象、更新对象和删除对象等方法。SqlSession的实现类是DefaultSqlSession。

public interface SqlSession extends Closeable {

// 事务管理

void commit();

void commit(boolean force);

void rollback();

void rollback(boolean force);

// 数据库操作

T selectOne(String statement);

T selectOne(String statement, Object parameter);

List selectList(String statement);

List selectList(String statement, Object parameter);

List selectList(String statement, Object parameter, RowBounds rowBounds);

Map selectMap(String statement, String mapKey);

Map selectMap(String statement, Object parameter, String mapKey);

Map selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds);

int insert(String statement);

int insert(String statement, Object parameter);

int update(String statement);

int update(String statement, Object parameter);

int delete(String statement);

int delete(String statement, Object parameter);

// 获取Mapper对象

T getMapper(Class type);

// 获取Configuration对象

Configuration getConfiguration();

}

4.3. Executor

Executor是MyBatis底层执行器接口,它的实现类有SimpleExecutor、ReuseExecutor和BatchExecutor。Executor负责对MappedStatement进行执行,MappedStatement是把Mapper映射文件对应的增删改查操作封装成对象,一般是由SqlSessionFactoryBuilder解析Mapper映射文件生成的。

4.4. MapperProxy

MapperProxy用于动态代理Mapper接口,它实现了Java动态代理的InvocationHandler接口,接口方法的实现是动态生成的。MapperProxy的构造函数需要Executor和MapperInterface类型的参数,Executor用于执行Mapper接口方法对应的SQL语句,MapperInterface用于表示Mapper接口的类型。

public class MapperProxy implements InvocationHandler, Serializable {

private static final long serialVersionUID = -6424540398559729838L;

private final SqlSession sqlSession;

private final Class mapperInterface;

private final Map methodCache;

public MapperProxy(SqlSession sqlSession, Class mapperInterface, Map methodCache) {

this.sqlSession = sqlSession;

this.mapperInterface = mapperInterface;

this.methodCache = methodCache;

}

@Override

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

try {

if (Object.class.equals(method.getDeclaringClass())) {

return method.invoke(this, args);

} else if (isDefaultMethod(method)) {

return invokeDefaultMethod(proxy, method, args);

}

} catch (Throwable t) {

throw ExceptionUtil.unwrapThrowable(t);

}

final MapperMethod mapperMethod = cachedMapperMethod(method);

return mapperMethod.execute(sqlSession, args);

}

private MapperMethod cachedMapperMethod(Method method) {

return methodCache.computeIfAbsent(method, k -> new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));

}

}

4.5. MappedStatement

MappedStatement是MyBatis中的SQL语句映射对象,它包含了一个SQL语句以及对应的输入参数和结果映射。MappedStatement是由SqlSession.getConfiguration()方法中解析Mapper映射文件生成的。

public final class MappedStatement {

// ID和Namespace

private String resource;

private Configuration configuration;

private String id;

private Integer fetchSize;

private Integer timeout;

private StatementType statementType;

private ResultSetType resultSetType;

private SqlSource sqlSource;

private Cache cache;

private ParameterMap parameterMap;

private List resultMaps;

private boolean flushCacheRequired;

private boolean useCache;

private boolean resultOrdered;

private SqlCommandType sqlCommandType;

private KeyGenerator keyGenerator;

private String[] keyProperties;

private String[] keyColumns;

private boolean hasNestedResultMaps;

private String databaseId;

private Log statementLog;

private LanguageDriver lang;

private String[] resultSets;

private String resultMapId;

private String keyProperty;

private String keyColumn;

private DatabaseMetaData databaseMetaData;

// Getter和Setter方法

}

5. 总结

本文对MyBatis源码进行了简要的介绍和说明,并提供了一些学习MyBatis源码的方法和建议。MyBatis源码的学习对于深入理解MyBatis的底层实现原理和机制是非常有帮助的,同时也有助于我们开发出更加高效、可靠的Java应用程序。

后端开发标签