SpringMVC+Mybatis,@Transactional注解会影响Mybatis的一级缓存

项目使用的是SpringMVC+Mybatis,如果service的方法里面全都是查询,那么加不加@Transactional有什么影响呢?
SpringMVC集成Mybatis的常见配置如下:


<bean id="dataSource" 。。。>
bean>

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
	<property name="dataSource" ref="dataSource" />
	<property name="mapperLocations" value="classpath*:com/**/*Mapper.xml" />
	<property name="configLocation" value="classpath:sqlMapping.xml" />
bean>

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
	<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
	<property name="basePackage" value="****" />
bean>

<bean id="txManager"
	class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<property name="dataSource" ref="dataSource" />
bean>

首先我们来看下spring是如何跟mybatis关联到一起的,看源码:
从MapperScannerConfigurer开始,它会扫描basePackage下面的接口,并且把接口注册到Spring容器:

public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
	。。。
	  @Override
	  public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
		    if (this.processPropertyPlaceHolders) {
		      processPropertyPlaceHolders();
		    }

		    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
		    scanner.setAddToConfig(this.addToConfig);
		    scanner.setAnnotationClass(this.annotationClass);
		    scanner.setMarkerInterface(this.markerInterface);
		    scanner.setSqlSessionFactory(this.sqlSessionFactory);
		    scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
		    scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
		    scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
		    scanner.setResourceLoader(this.applicationContext);
		    scanner.setBeanNameGenerator(this.nameGenerator);
		    scanner.registerFilters();
		    scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
  	}
	。。。
}
//接着看ClassPathMapperScanner:
public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
	。。。
	private MapperFactoryBean<?> mapperFactoryBean = new MapperFactoryBean<Object>();
	@Override
	  public Set<BeanDefinitionHolder> doScan(String... basePackages) {
	    Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);

	    if (beanDefinitions.isEmpty()) {
	      logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
	    } else {
	      processBeanDefinitions(beanDefinitions);
	    }

	    return beanDefinitions;
	  }
	private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
	    GenericBeanDefinition definition;
	    for (BeanDefinitionHolder holder : beanDefinitions) {
	      definition = (GenericBeanDefinition) holder.getBeanDefinition();

	      if (logger.isDebugEnabled()) {
	        logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName() 
	          + "' and '" + definition.getBeanClassName() + "' mapperInterface");
	      }

	      // the mapper interface is the original class of the bean
	      // but, the actual class of the bean is MapperFactoryBean
	      definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue #59
	       //重点看这里,这里的意思是每一个Mapper接口被注入到Spring容器以后,实际上是一个mapperFactoryBean
	      definition.setBeanClass(this.mapperFactoryBean.getClass());
	       //给这个mapperFactoryBean注入了其他的属性:addToConfig
	      definition.getPropertyValues().add("addToConfig", this.addToConfig);

	      boolean explicitFactoryUsed = false;
	     //给这个mapperFactoryBean注入了其他的属性:sqlSessionFactory
	      if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
	        definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
	        explicitFactoryUsed = true;
	      } else if (this.sqlSessionFactory != null) {
	        definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
	        explicitFactoryUsed = true;
	      }
          //给这个mapperFactoryBean注入了其他的属性:sqlSessionTemplate
	      if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
	        if (explicitFactoryUsed) {
	          logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
	        }
	        definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
	        explicitFactoryUsed = true;
	      } else if (this.sqlSessionTemplate != null) {
	        if (explicitFactoryUsed) {
	          logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
	        }
	        definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
	        explicitFactoryUsed = true;
	      }

	      if (!explicitFactoryUsed) {
	        if (logger.isDebugEnabled()) {
	          logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
	        }
	        definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
	      }
	    }
	  }
}
//下面我们来看MapperFactoryBean
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
}
//继续看SqlSessionDaoSupport
public abstract class SqlSessionDaoSupport extends DaoSupport {

  private SqlSession sqlSession;

  private boolean externalSqlSession;

  public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
    if (!this.externalSqlSession) {
      this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
    }
  }
 
  public SqlSession getSqlSession() {
    return this.sqlSession;
  }
}
//这里可以看出来,spring操作mybatis的时候使用的SqlSession,并不是mybatis默认的DefaultSqlSession,而是使用自定义的SqlSessionTemplate。
//下面就到了SqlSessionTemplate如何获取SqlSession:
public class SqlSessionTemplate implements SqlSession, DisposableBean {
	。。。
	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;
	    //这里是生成了SqlSession的一个代理类,具体的操作还得去看SqlSessionInterceptor
	    this.sqlSessionProxy = (SqlSession) newProxyInstance(
	        SqlSessionFactory.class.getClassLoader(),
	        new Class[] { SqlSession.class },
	        new SqlSessionInterceptor());
	  }
}
//SqlSessionInterceptor:
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) {
         。。。
      } finally {
        if (sqlSession != null) {
          closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
        }
      }
    }
  }
//我们来跟一下getSqlSession,因为拿到了SqlSession就可以操作数据库了:
 public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
    //首先从TransactionSynchronizationManager中查找holder,然后从holder中查找SqlSession
    SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
    SqlSession session = sessionHolder(executorType, holder);
    if (session != null) {//如果找到了就直接返回了
      return session;
    }
    //如果找不到,再去新建一个SqlSession
    session = sessionFactory.openSession(executorType);
    registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
    return session;
  }
//holder只有在开启了事务才会有值,因此如果在service的方法上加上了@Transactional的注解,那么框架就会把holder给缓存到TransactionSynchronizationManager中。
//如果同一个SqlSession的两个查询操作,是可以使用mybatis的一级缓存的,因此,加了@Transactional以后,里面的两个同样的查询语句只会查询数据库一次,而如果不加,每次都是新建一个SqlSession,因此一级缓存也就不起作用了!
//可以写代码验证一下:
@Service
public class ProductService {
	@Autowired
	ProductDao productDao;
	@Transactional
	public Product getByBm(String bankBm, String pdctBm) {
		productDao.selectByBm(bankBm, pdctBm);
		return productDao.selectByBm(bankBm, pdctBm);
	}
}
//getByBm中调用了2次selectByBm,如果不加@Transactional会查询数据库2次,加上只会查询一次,很有意思!

你可能感兴趣的:(java)