在spring中,dao层大多都是用Mybatis,那么

1,Mybatis执行sql最重要的是什么?

在以前对Mybatis的源码解读中,我们知道,Mybatis利用了动态代理来做,最后实现的类是MapperProxy,在最后执行具体的方法时,实际上执行的是:

@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {if (Object.class.equals(method.getDeclaringClass())) {try {return method.invoke(this, args);} catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);}}final MapperMethod mapperMethod = cachedMapperMethod(method);return mapperMethod.execute(sqlSession, args);}

最重要的一步:

mapperMethod.execute(sqlSession, args);

这里的sqlSession 其实是在Spring的配置时设置的 sqlSessionTemplate,随便对其中的一个进行跟进:可以在sqlSessionTemplate类中发现很好这样的方法,用来执行具体的sql,如:

@Overridepublic <T> T selectOne(String statement, Object parameter) {return this.sqlSessionProxy.<T> selectOne(statement, parameter);}

这一步就是最后执行的方法,那么问题来了 sqlSessionProxy 到底是啥呢? 这又得回到最开始。

2,使用mybatis连接mysql时一般都是需要注入SqlSessionFactory,SqlSessionTemplate,PlatformTransactionManager。

其中SqlSessionTemplate是生成sqlSession的模版,来看他的注入过程(注解形式注入):

@Bean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {return new SqlSessionTemplate(sqlSessionFactory);
}

在这个初始化过程中:

 /**
* Constructs a Spring managed {@code SqlSession} with    the given
* {@code SqlSessionFactory} and {@code ExecutorType}.
* A custom {@code SQLExceptionTranslator} can be provided as an
* argument so any {@code PersistenceException} thrown by MyBatis
* can be custom translated to a {@code  RuntimeException}
* The {@code SQLExceptionTranslator} can also be null and thus no
* exception translation will be done and MyBatis exceptions will be
* thrown
*
* @param sqlSessionFactory
* @param executorType
* @param exceptionTranslator
*/
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,PersistenceExceptionTranslator exceptionTranslator) {notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
notNull(executorType, "Property 'executorType' is required");this.sqlSessionFactory = sqlSessionFactory;
this.executorType = executorType;
this.exceptionTranslator = exceptionTranslator;
this.sqlSessionProxy = (SqlSession) newProxyInstance(SqlSessionFactory.class.getClassLoader(),new Class[] { SqlSession.class },new SqlSessionInterceptor());

}

最后一步比较重要,用java动态代理生成了一个sqlSessionFactory。代理的类是:

 /*** Proxy needed to route MyBatis method calls to the proper SqlSession got
* from Spring's Transaction Manager
* It also unwraps exceptions thrown by {@code Method#invoke(Object, Object...)} to
* pass a {@code PersistenceException} to the {@code PersistenceExceptionTranslator}.
*/
private class SqlSessionInterceptor implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory,SqlSessionTemplate.this.executorType,SqlSessionTemplate.this.exceptionTranslator);try {Object result = method.invoke(sqlSession, args);if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {// force commit even on non-dirty sessions because some databases require// a commit/rollback before calling close()sqlSession.commit(true);}return result;} catch (Throwable t) {Throwable unwrapped = unwrapThrowable(t);if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {// release the connection to avoid a deadlock if the translator is no loaded. See issue #22closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);sqlSession = null;Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);if (translated != null) {unwrapped = translated;}}throw unwrapped;} finally {if (sqlSession != null) {closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);}}
}

}

在sqlSession执行sql的时候就会用这个代理类。isSqlSessionTransactional 这个会判断是不是有Transactional,没有则直接提交。如果有则不提交,在最外层进行提交。

其中

getSqlSession(SqlSessionTemplate.this.sqlSessionFactory,SqlSessionTemplate.this.executorType,SqlSessionTemplate.this.exceptionTranslator);

这个方法用来获取sqlSession。具体实现如下:

  /*** Gets an SqlSession from Spring Transaction Manager or creates a new one if needed.* Tries to get a SqlSession out of current transaction. If there is not any, it creates a new one.* Then, it synchronizes the SqlSession with the transaction if Spring TX is active and* <code>SpringManagedTransactionFactory</code> is configured as a transaction manager.** @param sessionFactory a MyBatis {@code SqlSessionFactory} to create new sessions* @param executorType The executor type of the SqlSession to create* @param exceptionTranslator Optional. Translates SqlSession.commit() exceptions to Spring exceptions.* @throws TransientDataAccessResourceException if a transaction is active and the*             {@code SqlSessionFactory} is not using a {@code SpringManagedTransactionFactory}* @see SpringManagedTransactionFactory*/public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED);SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);SqlSession session = sessionHolder(executorType, holder);if (session != null) {return session;}if (LOGGER.isDebugEnabled()) {LOGGER.debug("Creating a new SqlSession");}session = sessionFactory.openSession(executorType);registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);return session;}

这个sqlSession的创建其实看他方法解释就够了,“从Spring事务管理器中获取一个sqlsession,如果没有,则创建一个新的”,这句话的意思其实就是如果有事务,则sqlSession用一个,如果没有,就给你个新的咯。
再通俗易懂一点:如果在事务里,则Spring给你的sqlSession是一个,否则,每一个sql给你一个新的sqlSession。这里生成的sqlSession其实就是DefaultSqlSession了。后续可能仍然有代理,如Mybatis分页插件等,不在此次讨论的范围内。

3,第二步的 sqlSession 一样不一样到底有什么影响?

在2中,我们看到如果是事务,sqlSession 一样,如果不是,则每次都不一样,且每次都会提交。这是最重要的。

sqlSession,顾名思义,就是sql的一个会话,在这个会话中发生的事不影响别的会话,如果会话提交,则生效,不提交不生效。

来看下sqlSession 这个接口的介绍。

/*** The primary Java interface for working with MyBatis.* Through this interface you can execute commands, get mappers and manage transactions.*  为Mybatis工作最重要的java接口,通过这个接口来执行命令,获取mapper以及管理事务* @author Clinton Begin*/

注释很明白了,来一一看看怎么起的这些作用。

3.1,执行命令。

在第一个小标题中 执行sql最重要的方法就是 this.sqlSessionProxy. selectOne(statement, parameter); 这个方法,而在第二个小标题中我们看到是通过代理来执行的,最后实际上没有事务则提交sql。这就是执行sql的基本动作了。获取sqlsession,提交执行Sql。

3.2,获取mapper。

在我们日常的代码中可能不会这么写,但是实际上,如果必要我们是可以这么做的,如:

XxxxxMapper xxxxxMapper = session.getMapper(xxxxxMapper.class);

一般情况下,如果要这么做,首先需要注入 sqlSessionFactory,然后利用

sqlSessionFactory.openSession()。

即可获取session。

3.3,事务管理

上面我一直提到一点,sqlSession 那个代理类里有个操作,判断这个是不是事务管理的sqlSession,如果是,则不提交,不是才提交,这个就是事务管理了,那么有个问题,在哪里提交这个事务呢????

4,事务从哪里拦截,就从哪里提交

Spring中,如果一个方法被 @Transactional 注解标注,在生效的情况下(不生效的情况见我写动态代理的那篇博客),则最终会被TransactionInterceptor 这个类所代理,执行的方法实际上是这样的:

@Override
public Object invoke(final MethodInvocation invocation) throws Throwable {// Work out the target class: may be {@code null}.// The TransactionAttributeSource should be passed the target class// as well as the method, which may be from an interface.Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);// Adapt to TransactionAspectSupport's invokeWithinTransaction...return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() {@Overridepublic Object proceedWithInvocation() throws Throwable {return invocation.proceed();}});
}

继续看invokeWithinTransaction这个方法:

/*** General delegate for around-advice-based subclasses, delegating to several other template* methods on this class. Able to handle {@link CallbackPreferringPlatformTransactionManager}* as well as regular {@link PlatformTransactionManager} implementations.* @param method the Method being invoked* @param targetClass the target class that we're invoking the method on* @param invocation the callback to use for proceeding with the target invocation* @return the return value of the method, if any* @throws Throwable propagated from the target invocation*/
protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)throws Throwable {// If the transaction attribute is null, the method is non-transactional.final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);final PlatformTransactionManager tm = determineTransactionManager(txAttr);final String joinpointIdentification = methodIdentification(method, targetClass);//基本上我们的事务管理器都不是一个CallbackPreferringPlatformTransactionManager,所以基本上都是会从这个地方进入,下面的else情况暂不讨论。if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {// Standard transaction demarcation with getTransaction and commit/rollback calls.//获取具体的TransactionInfo ,如果要用编程性事务,则把这块的代码可以借鉴一下。TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);Object retVal = null;try {// This is an around advice: Invoke the next interceptor in the chain.// This will normally result in a target object being invoked.retVal = invocation.proceedWithInvocation(); //执行被@Transactional标注里面的具体方法。}catch (Throwable ex) {// target invocation exception//异常情况下,则直接完成了,因为在sqlsession执行完每一条指令都没有提交事务,所以表现出来的就是回滚事务。completeTransactionAfterThrowing(txInfo, ex);throw ex;}finally {cleanupTransactionInfo(txInfo);}//正常执行完成的提交事务方法 跟进可以看到实际上执行的是:(编程性事务的提交)// ==============txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());===========commitTransactionAfterReturning(txInfo);return retVal;}// =======================else情况不讨论================================else {// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.try {Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr,new TransactionCallback<Object>() {@Overridepublic Object doInTransaction(TransactionStatus status) {TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);try {return invocation.proceedWithInvocation();}catch (Throwable ex) {if (txAttr.rollbackOn(ex)) {// A RuntimeException: will lead to a rollback.if (ex instanceof RuntimeException) {throw (RuntimeException) ex;}else {throw new ThrowableHolderException(ex);}}else {// A normal return value: will lead to a commit.return new ThrowableHolder(ex);}}finally {cleanupTransactionInfo(txInfo);}}});// Check result: It might indicate a Throwable to rethrow.if (result instanceof ThrowableHolder) {throw ((ThrowableHolder) result).getThrowable();}else {return result;}}catch (ThrowableHolderException ex) {throw ex.getCause();}}
}

5,小结,SqlSession 还在别的地方有用到吗?

其实,Mybatis的一级缓存就是 SqlSession 级别的,只要SqlSession 不变,则默认缓存生效,也就是说,如下的代码,实际上只会查一次库的:

XxxxxMapper xxxxxMapper = session.getMapper(xxxxxMapper.class);
//对应的sql为: select id from test_info;
xxxxxMapper.selectFromDb();
xxxxxMapper.selectFromDb();
xxxxxMapper.selectFromDb();

实际上只会被执行一次,感兴趣的朋友们可以试试。

但是,在日常使用中,我们都是使用spring来管理Mapper,在执行selectFromDb 这个操作的时候,其实每次都会有一个新的SqlSession,所以,Mybatis的一级缓存是用不到的。

Mybatis的核心——SqlSession解读相关推荐

  1. MyBatis四大核心概念

    本文讲解 MyBatis 四大核心概念(SqlSessionFactoryBuilder.SqlSessionFactory.SqlSession.Mapper). MyBatis 作为互联网数据库映 ...

  2. 第一章:ORM框架发展历程和MyBatis的核心应用

    ORM框架的发展历史与MyBatis的高级应用 一.ORM框架的发展历程 1. JDBC操作 1.1 JDBC操作的特点 最初的时候我们肯定是直接通过jdbc来直接操作数据库的,本地数据库我们有一张t ...

  3. mybatis使用map插入数据和Mybatis的核心配置文件LOG4J

    mybatis使用map插入数据 UserMapper.java包中写入 int addUser2(Map<Object,String> map); Usermapper.xml < ...

  4. Mybatis四大核心对象及四大内置对象

    一.MyBatis四大核心对象 MyBatis完成一次数据库操作需要经过的步骤,如下: 1.加载配置文件 2.获取SqlSessionFactoryBuiler对象 3.通过SqlSessionFac ...

  5. 【Java进阶营】MyBatis四大核心概念

    本文讲解 MyBatis 四大核心概念(SqlSessionFactoryBuilder.SqlSessionFactory.SqlSession.Mapper). 图片描述 MyBatis 作为互联 ...

  6. Spring整合mybatis中的sqlSession是如何做到线程隔离的?

    转载自  Spring整合mybatis中的sqlSession是如何做到线程隔离的? 项目中常常使用mybatis配合spring进行数据库操作,但是我们知道,数据的操作是要求做到线程安全的,而且按 ...

  7. 鱼眼图像自监督深度估计原理分析和Omnidet核心代码解读

    作者丨苹果姐@知乎 来源丨https://zhuanlan.zhihu.com/p/508090405 编辑丨3D视觉工坊 在自动驾驶实际应用中,对相机传感器的要求之一是拥有尽可能大的视野范围,鱼眼相 ...

  8. Capsule 核心代码解读

    原文地址 Capsule核心代码解读 前几天,Sara Sabour 开源了一份 Capsule 代码,该代码是论文 Dynamic Routing between Capsules 中所采用的实现. ...

  9. MyBatis 实际使用案例-核心配置解读

    第一个是config 文件.大部分时候我们只需要很少的配置就可以让MyBatis 运行起来.其实MyBatis 里面提供的配置项非常多,我们没有配置的时候使用的是系统的默认值. mybatis-3 的 ...

最新文章

  1. oss生成唯一文件名_根据结构化自然语言规范自动生成精确预言
  2. mongo code 96
  3. SAP PP CS01使用ECR去创建BOM主数据,报错:System status: ECR is not yet approved.
  4. springboot连接容器内mysql_docker部署springboot项目,连接mysql容器
  5. 并查集 - 由斜杠划分区域
  6. liferay 如何使用javascript
  7. Visual Basic团队透露将为VB添加迭代器
  8. eureka 集群失败的原因_Eureka集群的那些坑
  9. 面试精讲之面试考点及大厂真题 - 分布式专栏 09 缓存必问:Reids持久化,高可用集群
  10. 基于LSTM+Attention机制的IMDB影评数据分类学习实践
  11. promise重新认识
  12. c语言 stm32结构体赋值,STM32学习笔记9——结构体赋值问题
  13. 游戏产业链:客户端游戏/网页游戏
  14. 如何将md文件完美转化为 PDF?
  15. 攻防世界web练习5
  16. 转载一篇不错的文章:谈谈“野生”Java程序员学习的道路
  17. matlab成功安装libsvm后,运行程序仍报错“svmtrain has been removed”解决方法记录
  18. 高校体育场地预约管理系统(Java Web毕业设计)
  19. 输入某年某月某日,计算是该年的第几天
  20. IDEA 远程调试springboot

热门文章

  1. 小学毕业能学php吗,将来没出息的娃,多半从小就有这些缺点,小学毕业前还不改就晚了...
  2. mysql 5.6 5.7 并存_centos同时运行mysql5.6和mysql5.7
  3. pythondir什么意思_Python之dir()与__dict__的区别
  4. 处理之后的图像句柄传到显示框_深度学习目标检测与预处理模块
  5. centos7开发java体验_centos7之Java开发环境构建
  6. 六十五、SpringBoot配置拦截器拦截静态资源和区域解析器实现登陆功能
  7. 四、爬虫中的urllib库使用
  8. 大连海事学院计算机研究生多少分,大连海事大学计算机或软件工程研究生多少分能录取...
  9. 比SOTA模型更全能!商汤科技和上海人工智能实验室联手打造统一模型架构UniFormer...
  10. 实录分享 | 计算未来轻沙龙:图神经网络前沿研讨会