-
目录
关于源码解析,我们首先提出以上10个问题,然后根据问题去查看mybatis的源码
框架如何初始化? - 如何映射数据类型?
- 如何创建SqlSession?
- 如何调用Mapper接口的【抽象方法】完成SQL语句?
- 如何解析SQL语法?
- 如何设置SQL参数?
- 缓存如何起作用?
- 如何执行insert/update/delete?
- 如何执行查询?
- 如何映射查询结果?
关于源码解析,我们首先提出以上10个问题,然后根据问题去查看mybatis的源码
1.框架如何初始化?
创建 SqlSessionFactory(数据库连接池) 实例
private static SqlSessionFactory factory;
static {//通过静态代码块,唯一一次创建 SqlSessionFactory 实例
try {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");//创建输入流
factory = new SqlSessionFactoryBuilder().build(is);//调用build方法实例化一个SqlSessionFactory
} catch (Exception e) {
e.printStackTrace();
}
}
通过一个输入流读取MyBatis配置文件,
通过构建器SqlSessionFactoryBuilder的build(InputStream)方法将配置文件中的配置
在build(InputStream)方法中会根据传过去的输入流生成XMLConfigBuilder实例。
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse());//build返回初始过的SqlSessionFactory对象
} 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.
}
}
}
XMLConfigBuilder的作用就是解析xml配置文件。在parse()方法中解析原生DOM文档初始化所有的配置参数 。
然后调用本类中的parseConfiguration(XNode root)将所有的配置保存到Configuration属性中
Configuration用来保存所有的配置信息
public Configuration parse() {
if (parsed) {//解析标识,true表示已经解析创建过,false则表示没有
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;//改变解析标识,并调用解析方法
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
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);
}
}
然后将保存了所有配置信息的Configuration参数调用buide()方法返回一个SqlSessionFactory的默认实现类DefaultSqlSessionFactory
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
2.如何映射数据类型?
Configuration中的属性TypeHandlerRegistry的构造函数对数据库的数据类型和java的数据类型进行了一一对应。
protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();
public TypeHandlerRegistry() {
register(Boolean.class, new BooleanTypeHandler());
register(boolean.class, new BooleanTypeHandler());
register(JdbcType.BOOLEAN, new BooleanTypeHandler());
register(JdbcType.BIT, new BooleanTypeHandler());
register(Byte.class, new ByteTypeHandler());
register(byte.class, new ByteTypeHandler());
register(JdbcType.TINYINT, new ByteTypeHandler());
register(Short.class, new ShortTypeHandler());
register(short.class, new ShortTypeHandler());
register(JdbcType.SMALLINT, new ShortTypeHandler());
register(Integer.class, new IntegerTypeHandler());
register(int.class, new IntegerTypeHandler());
register(JdbcType.INTEGER, new IntegerTypeHandler());
register(Long.class, new LongTypeHandler());
register(long.class, new LongTypeHandler());
register(Float.class, new FloatTypeHandler());
register(float.class, new FloatTypeHandler());
register(JdbcType.FLOAT, new FloatTypeHandler());
register(Double.class, new DoubleTypeHandler());
register(double.class, new DoubleTypeHandler());
register(JdbcType.DOUBLE, new DoubleTypeHandler());
register(Reader.class, new ClobReaderTypeHandler());
register(String.class, new StringTypeHandler());
register(String.class, JdbcType.CHAR, new StringTypeHandler());
register(String.class, JdbcType.CLOB, new ClobTypeHandler());
register(String.class, JdbcType.VARCHAR, new StringTypeHandler());
register(String.class, JdbcType.LONGVARCHAR, new StringTypeHandler());
register(String.class, JdbcType.NVARCHAR, new NStringTypeHandler());
register(String.class, JdbcType.NCHAR, new NStringTypeHandler());
register(String.class, JdbcType.NCLOB, new NClobTypeHandler());
register(JdbcType.CHAR, new StringTypeHandler());
register(JdbcType.VARCHAR, new StringTypeHandler());
register(JdbcType.CLOB, new ClobTypeHandler());
register(JdbcType.LONGVARCHAR, new StringTypeHandler());
register(JdbcType.NVARCHAR, new NStringTypeHandler());
register(JdbcType.NCHAR, new NStringTypeHandler());
register(JdbcType.NCLOB, new NClobTypeHandler());
register(Object.class, JdbcType.ARRAY, new ArrayTypeHandler());
register(JdbcType.ARRAY, new ArrayTypeHandler());
register(BigInteger.class, new BigIntegerTypeHandler());
register(JdbcType.BIGINT, new LongTypeHandler());
register(BigDecimal.class, new BigDecimalTypeHandler());
register(JdbcType.REAL, new BigDecimalTypeHandler());
register(JdbcType.DECIMAL, new BigDecimalTypeHandler());
register(JdbcType.NUMERIC, new BigDecimalTypeHandler());
register(InputStream.class, new BlobInputStreamTypeHandler());
register(Byte[].class, new ByteObjectArrayTypeHandler());
register(Byte[].class, JdbcType.BLOB, new BlobByteObjectArrayTypeHandler());
register(Byte[].class, JdbcType.LONGVARBINARY, new BlobByteObjectArrayTypeHandler());
register(byte[].class, new ByteArrayTypeHandler());
register(byte[].class, JdbcType.BLOB, new BlobTypeHandler());
register(byte[].class, JdbcType.LONGVARBINARY, new BlobTypeHandler());
register(JdbcType.LONGVARBINARY, new BlobTypeHandler());
register(JdbcType.BLOB, new BlobTypeHandler());
register(Object.class, unknownTypeHandler);
register(Object.class, JdbcType.OTHER, unknownTypeHandler);
register(JdbcType.OTHER, unknownTypeHandler);
register(Date.class, new DateTypeHandler());
register(Date.class, JdbcType.DATE, new DateOnlyTypeHandler());
register(Date.class, JdbcType.TIME, new TimeOnlyTypeHandler());
register(JdbcType.TIMESTAMP, new DateTypeHandler());
register(JdbcType.DATE, new DateOnlyTypeHandler());
register(JdbcType.TIME, new TimeOnlyTypeHandler());
register(java.sql.Date.class, new SqlDateTypeHandler());
register(java.sql.Time.class, new SqlTimeTypeHandler());
register(java.sql.Timestamp.class, new SqlTimestampTypeHandler());
register(String.class, JdbcType.SQLXML, new SqlxmlTypeHandler());
register(Instant.class, InstantTypeHandler.class);
register(LocalDateTime.class, LocalDateTimeTypeHandler.class);
register(LocalDate.class, LocalDateTypeHandler.class);
register(LocalTime.class, LocalTimeTypeHandler.class);
register(OffsetDateTime.class, OffsetDateTimeTypeHandler.class);
register(OffsetTime.class, OffsetTimeTypeHandler.class);
register(ZonedDateTime.class, ZonedDateTimeTypeHandler.class);
register(Month.class, MonthTypeHandler.class);
register(Year.class, YearTypeHandler.class);
register(YearMonth.class, YearMonthTypeHandler.class);
register(JapaneseDate.class, JapaneseDateTypeHandler.class);
// issue #273
register(Character.class, new CharacterTypeHandler());
register(char.class, new CharacterTypeHandler());
}
Configuration中的属性TypeAliasRegistry主要是对JavaBean和mybatis中的核心配置文件中的类型的别名进行一一对应。
protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
3.如何创建SqlSession?
SqlSession类似JDBC 中的 Connection 对象。
调用SqlSessionFactory的openSession方法会调用DefaultSqlSessionFactory方法
@Override
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
在DefaultSqlSessionFactory方法中做了以下几件事:
1.在configuration获取mybatis配置中的数据库连接所需要的参数
2.创建事务实例
3.创建事务工厂
4.创建执行器
5.最终返回一个默认的DefaultSqlSession
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;//声明事务变量
try {
final Environment environment = configuration.getEnvironment();//获取mybatis配置中的数据库连接所需要的参数
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();
}
}
Executor执行器,是Mybatis内的核心对象,负责执行SQL语句(总负责人)Mybatis默认用的是Simple执行器这个实现类
4.如何调用Mapper接口的【抽象方法】完成SQL语句?
调用初始化过的SqlSession对象的getMapper方法
public <T> T getMapper(Class<T> type) {
return configuration.getMapper(type, this);
}
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
然后在Configuration的getMapper方法中会调用mapperRegistry(保存了之前初始化时所有注册过的mapper)的getMapper方法
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);
}
}
该处使用了代理模式。
在该方法中根据传过来的Class<T>作为key值取出相对应代理工厂,
最终返回了代理工厂的newInstance方法生成的实例对象。
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
在该newInstance方法中会初始化一个MapperProxy
MapperProxy中实现了InvocationHandler动态代理接口
@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);
}
try块中均为了过滤掉默认方法,是默认方法则不代理,直接反射使用原先的方法。
若过滤掉了,调用mapperMethod的execute方法执行SQL语句。
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
case SELECT:
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
if (method.returnsOptional()
&& (result == null || !method.getReturnType().equals(result.getClass()))) {
result = Optional.ofNullable(result);
}
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + command.getName());
}
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException("Mapper method '" + command.getName()
+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
SqlCommand是MapperMethod的一个类部类,其中的SqlCommandType属性中封装了SQL标签
public enum SqlCommandType {
UNKNOWN, INSERT, UPDATE, DELETE, SELECT, FLUSH;
}
判断SQL语句的标签,然后给SQL语句传值后执行,并返回结果
private Object rowCountResult(int rowCount) {
final Object result;
if (method.returnsVoid()) {
result = null;
} else if (Integer.class.equals(method.getReturnType()) || Integer.TYPE.equals(method.getReturnType())) {
result = rowCount;
} else if (Long.class.equals(method.getReturnType()) || Long.TYPE.equals(method.getReturnType())) {
result = (long) rowCount;
} else if (Boolean.class.equals(method.getReturnType()) || Boolean.TYPE.equals(method.getReturnType())) {
result = rowCount > 0;
} else {
throw new BindingException("Mapper method '" + command.getName() + "' has an unsupported return type: " + method.getReturnType());
}
return result;
}
增删改操作都是执行的rowCountResult获得返回值
从代码中我们可以看出,返回值可以直接取Boolean,Long,Integer
查询操作:
- private final boolean returnsMany;//是否多值查询
- private final boolean returnsMap;//是否map查询
- private final boolean returnsVoid;//是否void查询
- private final boolean returnsCursor;//是否游标查询
- private final Class<?> returnType; //返回类型
- private final String mapKey;//获取mapKey的值
根据不同的判断执行相对应的方法获得对应的返回值