分位三步:
1、创建SqlSessionFactoryBuilder对象
2、通过SqlSessionFactoryBuilder创建SqlSessionFactory对象(解析xml配置过程)
3、SqlSessionFactory对象OpenSession方法,返回SqlSession,调用SqlSession的相应Api。
String conf = "Mybatis/SqlMapConfig.xml";
Reader reader = null;
try {
reader = Resources.getResourceAsReader(conf);
} catch (IOException e) {
e.printStackTrace();
}
//创建SqlSessionFactoryBuilder
SqlSessionFactoryBuilder sfb = new SqlSessionFactoryBuilder();
//build方法时解析mybatis配置信息。返回SqlSessionFactory
SqlSessionFactory ssf = sfb.build(reader);
//
SqlSession session = ssf.openSession();
SqlSessionFactoryBuilder的build方法的源码如下
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
var5 = this.build(parser.parse());
return var5;
解析配置文件,将jdbc配置、mapper映射接口等封装到Configuration对象中。XMLConfigBuilder的parse方法就是解析xml配置,将xml配置存入configuration对象的过程。解析过程中,mapper里每一条sql都会解析成一个MappedStatement对象。存放sqlId,参数类型,返回值类型等等。
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
调用DefaultSqlSessionFactory的OpenSession方法,得到SqlSession对象
Environment environment = this.configuration.getEnvironment();
TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
Executor executor = this.configuration.newExecutor(tx, execType);
var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
创建SqlSession对象的时候,创建了一个Executor,符合单一职责原则,查询数据库就是由Executor来执行,底层实现是由原生Jdbc一致。
mapper接口映射应该遵循下面的规范:
1、Mapper.xml文件中的nameSpace于mapper接口的类路径相同。
2、Mapper接口方法名称和Mapper.xml中定义的每个sql的Id相同。
3、parameterType和resultType的类型相同。
例如:创建LeaderMapper接口,定义selectLeaderList方法
public interface LeaderMapper {
List
创建LeaderMapper.xml
调用时:
LeaderMapper leaderService = session.getMapper(LeaderMapper.class);
List list = leaderService.selectLeaderList();
问题:在我们的工程中,并没有定义接口的实现,但是接口是怎么实现查询数据库的呢?
在mybatis配置文件中配置了TypeAlias扫描包时,解析生成Configuration时会调用MapperRegistry的addMapper方法。
public void addMapper(Class type) {
this.mapperRegistry.addMapper(type);
}
public void addMapper(Class type) {
if (type.isInterface()) {
this.knownMappers.put(type, new MapperProxyFactory(type));
}
}
调用SqlSession的getMapper的方法时,调用MapperRegistry对应的方法getMapper()
public T getMapper(Class type, SqlSession sqlSession) {
MapperProxyFactory mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
return mapperProxyFactory.newInstance(sqlSession);
}
这个过程其实就是通过MapperProxyFactory动态代理创建对象,每一个对象都会调用SqlSession底层的方法。
protected T newInstance(MapperProxy mapperProxy) {
return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
MapperProxy mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
return this.newInstance(mapperProxy);
}
创建的动态代理对象MapperProxy,我们需要关注它的invoke方法,因为动态代理对象调用都是调用的invoke方法。
MapperProxy的invoke方法。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
try {
return method.invoke(this, args);
} catch (Throwable var5) {
throw ExceptionUtil.unwrapThrowable(var5);
}
} else {
MapperMethod mapperMethod = this.cachedMapperMethod(method);
return mapperMethod.execute(this.sqlSession, args);
}
}
解释一下这段代码含义:getDeclaringClass方法用来判断当前这个方法是哪个类的方法。接口创建出来的代理对象不仅有实现接口的方法,也有从Object继承过来的方法,如果是Object类继承过来的方法,直接反射调用,否则调用MapperMethod调用。
MapperMethod的execute方法
public Object execute(SqlSession sqlSession, Object[] args) {
Object param;
Object result;
if (SqlCommandType.INSERT == this.command.getType()) {
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
} else if (SqlCommandType.UPDATE == this.command.getType()) {
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
} else if (SqlCommandType.DELETE == this.command.getType()) {
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
} else {
if (SqlCommandType.SELECT != this.command.getType()) {
throw new BindingException("Unknown execution method for: " + this.command.getName());
}
if (this.method.returnsVoid() && this.method.hasResultHandler()) {
this.executeWithResultHandler(sqlSession, args);
result = null;
} else if (this.method.returnsMany()) {
result = this.executeForMany(sqlSession, args);
} else if (this.method.returnsMap()) {
result = this.executeForMap(sqlSession, args);
} else {
param = this.method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(this.command.getName(), param);
}
}
if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {
throw new BindingException("Mapper method '" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ").");
} else {
return result;
}
}
MapperMethod调用时,根据method的传参,sql语句解析的xml确定调用SqlSession的相应的方法。
文章如描述有差错,可互相交流探讨。