简介
Mybatis是一款流行的持久层框架,基于ORM(Object-Relation Mapper)思想,对针对JDBC的封装,通过xml配置支持灵活复杂的SQL查询。
框架组件架构图
Mybatis核心成员数据流
核心成员说明
核心成员 | 功能说明 |
---|
Configuration | 保存MyBatis大部分配置信息 | SqlSession | MyBatis主要的顶层API,与数据库交互,实现数据库增删改查功能。 | Executor | MyBatis 调度器,负责SQL语句的生成和查询缓存的维护 | StatementHandler | 封装JDBC,负责对JDBC statement 的操作,如设置参数等 | ParameterHandler | 用户传递的参数转换成JDBC Statement 所对应的数据类型 | ResultSetHandler | 负责将JDBC返回的ResultSet结果集对象转换成List类型的集合 | TypeHandler | 负责java数据类型和jdbc数据类型(也可以说是数据表列类型)之间的映射和转换 | MappedStatement | MappedStatement维护一条<select|update|delete|insert>节点的封装 | SqlSource | 负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封装到BoundSql对象中,并返回 | BoundSql | 表示动态生成的SQL语句以及相应的参数信息 |
核心代码流程
1)Mybatis通过SqlSessionFactory获取sqlSession,然后有sqlSession完成数据库的交互。SqlSessionFactory默认接口实现是是DefaultSqlSessionFactory。
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
2) SqlSessionFactory有多个openSession方法,以无参的方法为例。
public SqlSession openSession() {
return this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), (TransactionIsolationLevel)null, false);
}
public enum ExecutorType {
SIMPLE,
REUSE,
BATCH;
private ExecutorType() {
}
}
- 针对Dao层的定义的接口,MapperRegistry维护了Dao层接口的代理工厂,并由工厂生成具体的代理去处理sqlSession,并交底层Executor调度器去执行。
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap();
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public Class<T> getMapperInterface() {
return this.mapperInterface;
}
public Map<Method, MapperMethod> getMethodCache() {
return this.methodCache;
}
protected T newInstance(MapperProxy<T> mapperProxy) {
return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
return this.newInstance(mapperProxy);
}
}
- Executor调度器与StatementHandler等处理器交互,完成SQL操作
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
List var9;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = this.prepareStatement(handler, ms.getStatementLog());
var9 = handler.query(stmt, resultHandler);
} finally {
this.closeStatement(stmt);
}
return var9;
}
并发场景的思考:DefaultSqlSessionFactory是线程安全的么?如何做到线程安全?
是不线程安全的,这个会在后面解释。
缓存机制
一级缓存核心类是PerpetualCache,本质是一个hashMap
二级缓存默认不开启。
Spring 与Mybatis的整合
Spring bean 生命周期
MapperScannerConfigurer
MapperScannerConfigurer的主要工作是扫描basePackage包下所有的mapper接口类,并将mapper接口类封装成为BeanDefinition对象,注册到spring的BeanFactory容器中核心类图如下:
以上知道了Spring的bean注册到容器的核心流程,通过理解Spring的核心流程,可以梳理出Dao层接口Mapper通过MapperScannerConfigurer整合到spring的流程:
SqlSessionFactoryBean
类定义如下:
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
public void setDataSource(DataSource dataSource) {
if (dataSource instanceof TransactionAwareDataSourceProxy) {
this.dataSource = ((TransactionAwareDataSourceProxy)dataSource).getTargetDataSource();
} else {
this.dataSource = dataSource;
}
}
public void setMapperLocations(Resource[] mapperLocations) {
this.mapperLocations = mapperLocations;
}
}
从这个类的定义可以看出SqlSessionFactoryBean与DataSource和Mapper有关。
在bean被创建的中,通过接口InitializingBean中的afterPropertiesSet方法设置属性。
public void afterPropertiesSet() throws Exception {
Assert.notNull(this.dataSource, "Property 'dataSource' is required");
Assert.notNull(this.sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
Assert.state(this.configuration == null && this.configLocation == null || this.configuration == null || this.configLocation == null, "Property 'configuration' and 'configLocation' can not specified with together");
this.sqlSessionFactory = this.buildSqlSessionFactory();
}
buildSqlSessionFactory方法主要完成SqlSession的初始化操作,完成在上面讲的Mybatis核心代码流程。
SqlSessionFactoryBean整合进Spring的流程
Mybatis整合Spring的整体流程
以下面简单的代码做主要流程说明
@Service
class AService{
@Autowire
private BDao bDao;
}
Spring在初始化的过程中@Service注解的类,AService类初始化完成之后,会进行属性赋值,bDao接口就是AService的一个属性,
1)首先根据这个bDao的名字或者类型从spring的BeanFactory中获取它的BeanDefinition,再从BeanDefinition中获取BeanClass,bDao对应的BeanClass就是MapperFactoryBean,这在创建MapperScannerConfigurer对象的时候设置的。
2)创建MapperFactoryBean对象,创建完成后,对属性进行赋值,其中有一个属性就是SqlSessionFactoryBean
3)MapperFactoryBean对象的属性设置完成之后,就调用它的getObject()方法,来获取bDao对应的实现类,获取的是一个JDK的代理类
public class MapperProxy<T> implements InvocationHandler, Serializable {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else {
MapperMethod mapperMethod = this.cachedMapperMethod(method);
return mapperMethod.execute(this.sqlSession, args);
}
}
}
程序在调用AService对象的某个方法的时候,就会调用到MapperProxy对象的invoke()方法,去完成对数据库的操作。
如何解决SqlSession的线程安全问题
MapperFactoryBean.getObject()获取的实例,实际是通过一个SqlSessionTemplate对象创建的,注入的Mapper对象实际上最终都执行的是SqlSessionTemplate方法。
关键代码如下:
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
Assert.notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
Assert.notNull(executorType, "Property 'executorType' is required");
this.sqlSessionFactory = sqlSessionFactory;
this.executorType = executorType;
this.exceptionTranslator = exceptionTranslator;
this.sqlSessionProxy = (SqlSession)Proxy.newProxyInstance(SqlSessionFactory.class.getClassLoader(), new Class[]{SqlSession.class}, new SqlSessionTemplate.SqlSessionInterceptor());
}
private class SqlSessionInterceptor implements InvocationHandler {
private SqlSessionInterceptor() {
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
SqlSession sqlSession = SqlSessionUtils.getSqlSession(SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
Object unwrapped;
try {
Object result = method.invoke(sqlSession, args);
if (!SqlSessionUtils.isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
sqlSession.commit(true);
}
unwrapped = result;
} catch (Throwable var11) {
unwrapped = ExceptionUtil.unwrapThrowable(var11);
if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
sqlSession = null;
Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException)unwrapped);
if (translated != null) {
unwrapped = translated;
}
}
throw (Throwable)unwrapped;
} finally {
if (sqlSession != null) {
SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
return unwrapped;
}
}
|