mybatis(一)基本执行流程
文章目录
- 1.查询
一,mybatis组件
SqlSessionFactoryBuilder
- 根据XML配置文件或者Java代码生成SqlSessionFactory
SqlSessionFactory
- 使用它生成Sqlsession
是一定定义了生成各种类型sqlSession的接口
public interface SqlSessionFactory {
//8个方法可以用来创建SqlSession实例
SqlSession openSession();
//自动提交
SqlSession openSession(boolean autoCommit);
//连接
SqlSession openSession(Connection connection);
//事务隔离级别
SqlSession openSession(TransactionIsolationLevel level);
//根据执行器类型生成sqlSession
SqlSession openSession(ExecutorType execType);
SqlSession openSession(ExecutorType execType, boolean autoCommit);
SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);
SqlSession openSession(ExecutorType execType, Connection connection);
// 获取配置信息
Configuration getConfiguration();
}
SqlSessionFactory只有两个子类:
Sqlsession
- 可以发送SQL语句返回结果,也可以获取Mapper接口。
- 主要作用:执行sql,管理事务,获取mapper
Mapper
- 它由一个Java接口和一个XML文件(或注解)构成,需要给出对应的SQL和映射规则,它可以发送SQL并返回结果。
解释方面过程:
public class Main {
public static void main(String[] args) throws IOException {
// mybatis配置文件信息
String resource = "mybatis.xml";
// 通过输入流读取配置文件信息
InputStream inputStream = Resources.getResourceAsStream(resource);
// 根据配置文件信息由SqlSessionFactoryBuilder构建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 由SqlSessionFactory构建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
// 通过SqlSession获取mapper接口,从而进行操作
ProductMapper productMapper = sqlSession.getMapper(ProductMapper.class);
List<Product> productList = productMapper.selectProductList();
for (Product product : productList) {
System.out.printf(product.toString());
}
} finally {
sqlSession.close();
}
}
}
二,mybatis基本执行流程
根据上面定义的流程,一步步看
2.1 构建SqlSessionFactory
根据配置文件信息由SqlSessionFactoryBuilder构建SqlSessionFactory
= new SqlSessionFactoryBuilder().build(inputStream);
翻看源码:
public SqlSessionFactory build(InputStream inputStream) {
return build(inputStream, null, null);
}
// 委托XMLConfigBuilder来解析xml文件封装在Configuration对象中,并根据配置文件构建SqlSessionFactory
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.
}
}
}
//最后一个build方法使用了一个Configuration作为参数,并返回DefaultSqlSessionFactory
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config); // DefaultSqlSessionFactory是SqlSessionFactory的实现类
}
2.2 构建SqlSession
获取到sqlSessionFactory后就可以通过定义好的接口方法获取一个sqlSession,从而进行数据库交互
// 由SqlSessionFactory构建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
翻看源码:默认的打开一个sqlSession的方法:
public SqlSession openSession() {
// 构建默认类型的sqlSession
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
sqlSession有几种类型:通过一个枚举去维护
public enum ExecutorType {
//ExecutorType.SIMPLE
//这个执行器类型不做特殊的事情。它为每个语句的执行创建一个新的预处理语句。
//ExecutorType.REUSE
//这个执行器类型会复用预处理语句。
//ExecutorType.BATCH
//这个执行器会批量执行所有更新语句,如果SELECT在它们中间执行还会标定它们是必须的,来保证一个简单并易于理解的行为。
SIMPLE, REUSE, BATCH
}
2.3 利用sqlSession执行数据库交互
// 先获取mapper
ProductMapper productMapper = sqlSession.getMapper(ProductMapper.class);
获取mapper源码:
public <T> T getMapper(Class<T> type) {
//最后会去调用MapperRegistry.getMapper
return configuration.<T>getMapper(type, this);
}
//映射注册机
protected MapperRegistry mapperRegistry = new MapperRegistry(this);
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
//返回mapper的代理对象
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);
}
}
通过JDK动态代理生成mapper的代理对象:
.newInstance(sqlSession);
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
protected T newInstance(MapperProxy<T> mapperProxy) {
//用JDK自带的动态代理生成映射器
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
解析一波:
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
这里根据class类型获取到的代理工厂其实很重要,最后获取mapper代理对象就是通过这个工厂的newInstance方法:
return mapperProxyFactory.newInstance(sqlSession);
所以,我们看看这个代理工厂是如何通过class类型获取到的:
所有的mapper代理工厂都被根据类型封装在一个map中,key是class类型,value是mapperProxyFactory
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();
那么mapper的配置是怎么添加到这个map中的呢?
在看XMLConfigBuilder配置解析
//解析配置
private void parseConfiguration(XNode root) {
try {
//分步骤解析
//issue #117 read properties first
//1.properties
propertiesElement(root.evalNode("properties"));
//2.类型别名
typeAliasesElement(root.evalNode("typeAliases"));
//3.插件
pluginElement(root.evalNode("plugins"));
//4.对象工厂
objectFactoryElement(root.evalNode("objectFactory"));
//5.对象包装工厂
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
//6.设置
settingsElement(root.evalNode("settings"));
// read it after objectFactory and objectWrapperFactory issue #631
//7.环境
environmentsElement(root.evalNode("environments"));
//8.databaseIdProvider
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
//9.类型处理器
typeHandlerElement(root.evalNode("typeHandlers"));
//10.映射器
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
mapper xml解析
//10.映射器
// 10.1使用类路径
// <mappers>
// <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
// <mapper resource="org/mybatis/builder/BlogMapper.xml"/>
// <mapper resource="org/mybatis/builder/PostMapper.xml"/>
// </mappers>
//
// 10.2使用绝对url路径
// <mappers>
// <mapper url="file:///var/mappers/AuthorMapper.xml"/>
// <mapper url="file:///var/mappers/BlogMapper.xml"/>
// <mapper url="file:///var/mappers/PostMapper.xml"/>
// </mappers>
//
// 10.3使用java类名
// <mappers>
// <mapper class="org.mybatis.builder.AuthorMapper"/>
// <mapper class="org.mybatis.builder.BlogMapper"/>
// <mapper class="org.mybatis.builder.PostMapper"/>
// </mappers>
//
// 10.4自动扫描包下所有映射器
// <mappers>
// <package name="org.mybatis.builder"/>
// </mappers>
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
//10.4自动扫描包下所有映射器
String mapperPackage = child.getStringAttribute("name");
configuration.addMappers(mapperPackage);
} else {
String resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
if (resource != null && url == null && mapperClass == null) {
//10.1使用类路径
ErrorContext.instance().resource(resource);
InputStream inputStream = Resources.getResourceAsStream(resource);
//映射器比较复杂,调用XMLMapperBuilder
//注意在for循环里每个mapper都重新new一个XMLMapperBuilder,来解析
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url != null && mapperClass == null) {
//10.2使用绝对url路径
ErrorContext.instance().resource(url);
InputStream inputStream = Resources.getUrlAsStream(url);
//映射器比较复杂,调用XMLMapperBuilder
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url == null && mapperClass != null) {
//10.3使用java类名
Class<?> mapperInterface = Resources.classForName(mapperClass);
//直接把这个映射加入配置
configuration.addMapper(mapperInterface);
} else {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
}
}
}
}
将解析好的配置添加到映射map:
//将包下所有类加入到mapper
public void addMappers(String packageName, Class<?> superType) {
mapperRegistry.addMappers(packageName, superType);
}
public void addMappers(String packageName) {
mapperRegistry.addMappers(packageName);
}
public <T> void addMapper(Class<T> type) {
mapperRegistry.addMapper(type);
}
public <T> void addMapper(Class<T> type) {
//mapper必须是接口!才会添加
if (type.isInterface()) {
if (hasMapper(type)) {
//如果重复添加了,报错
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
// 添加映射对
knownMappers.put(type, new MapperProxyFactory<T>(type));
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
//如果加载过程中出现异常需要再将这个mapper从mybatis中删除
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
2.4 执行数据库操作
1.查询
public <T> T selectOne(String statement) {
return this.<T>selectOne(statement, null);
}
public <T> T selectOne(String statement, Object parameter) {
// 内部其实调用的是selectList
List<T> list = this.<T>selectList(statement, parameter);
if (list.size() == 1) {
return list.get(0);
} else if (list.size() > 1) {
// 查询经典错误,selectOne时出现多结果集就会报TooManyResultsException
throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
} else {
return null;
}
}
public <E> List<E> selectList(String statement, Object parameter) {
return this.selectList(statement, parameter, RowBounds.DEFAULT);
}
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
//根据statement id找到对应的MappedStatement
MappedStatement ms = configuration.getMappedStatement(statement);
//转而用执行器来查询结果,注意这里传入的ResultHandler是null
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
//SqlSession.selectList会调用此方法
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
//得到绑定sql
BoundSql boundSql = ms.getBoundSql(parameter);
//创建缓存Key
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
//查询,query方法是先查缓存,再调用jdbc的方法查数据库
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
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();
//新建一个StatementHandler
//这里看到ResultHandler传入了
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
//准备语句
stmt = prepareStatement(handler, ms.getStatementLog());
//StatementHandler.query
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
String sql = boundSql.getSql();
statement.execute(sql);
//先执行Statement.execute,然后交给ResultSetHandler.handleResultSets
return resultSetHandler.<E>handleResultSets(statement);
}
所以整个链路到现在为止已经比较清楚了:
- 读取配置,构建SqlSessionFactoryBuilder
- 利用SqlSessionFactoryBuilder生成SqlSessionFactory,再生成SqlSession
- 利用SqlSession执行数据库操作
- 数据库操作是通过Executor执行器去执行,内部执行原理是jdbc封装后的数据库操作