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应用程序。