Mybatis核心原理
2021/7/23 8:05:53
本文主要是介绍Mybatis核心原理,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
简介
Mybatis是一款流行的持久层框架,基于ORM(Object-Relation Mapper)思想,对针对JDBC的封装,通过xml配置支持灵活复杂的SQL查询。
框架组件架构图
Mybatis核心成员数据流
核心成员说明
核心代码流程
1)Mybatis通过SqlSessionFactory获取sqlSession,然后有sqlSession完成数据库的交互。SqlSessionFactory默认接口实现是是DefaultSqlSessionFactory。
//默认new 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); } //executor 默认有多个实现,根据ExecutorType可知有以下几种 public enum ExecutorType { SIMPLE, REUSE, BATCH; private ExecutorType() { } }
针对Dao层的定义的接口,MapperRegistry维护了Dao层接口的代理工厂,并由工厂生成具体的代理去处理sqlSession,并交底层Executor调度器去执行。
public class MapperProxyFactory<T> { private final Class<T> mapperInterface; //MapperMethod对应Dao层接口的方法 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; } }
————————————————
版权声明:本文为CSDN博主「slagsea」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_34147021/article/details/118885110
这篇关于Mybatis核心原理的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-16ShardingSphere 如何完美驾驭分布式事务与 XA 协议?
- 2024-11-16ShardingSphere如何轻松驾驭Seata柔性分布式事务?
- 2024-11-16Maven资料入门指南
- 2024-11-16Maven资料入门教程
- 2024-11-16MyBatis Plus资料:新手入门教程与实践指南
- 2024-11-16MyBatis-Plus资料入门教程:快速上手指南
- 2024-11-16Mybatis资料入门教程:新手必看指南
- 2024-11-16MyBatis资料详解:新手入门与初级实战指南
- 2024-11-16MyBatisPlus资料:初学者入门指南与实用教程
- 2024-11-16MybatisPlus资料详解:初学者入门指南