Spring集成MyBatis

Spring集成MyBatis

使用

  1. 配置数据源
<!--配置dataSource-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC"/>
        <property name="username" value="xxx"/>
        <property name="password" value="xxx"/>
    </bean>
  1. 配置SqlSessionFactoryBean
<!--配置sqlSessionFactory, 这里使用了SqlSessionFacotryBean,它继承了FactoryBean接口,可以让mybatis自定义生成我们的sqlSessionFactory
-->
    <bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="typeAliasesPackage" value="myBatis_Spring.entity"/>
    </bean>
  1. 扫描mapper接口,并为每个mapper接口代理对象
<!--扫描所有mapper,并生成代理对象-->
    <bean id="mapperScannerConfigurer"		class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="myBatis_Spring.mapper"/>
    </bean>
对象说明

FactoryBean: 工厂Bean用于自定义生成Bean对象。当在ioc 中配置FactoryBean 的实例时,最终通过bean id 对应的是FactoryBean.getObject()实例,而非FactoryBean 实例本身

SqlSessionFactoryBean: 生成SqlSessionFactory 实例,该为单例对像,作用于整个应用生命周期。

MapperScannerConfigurer: 扫描我们写的mapper接口,并在spring容器初始化时为我们创建一个单例的Mapper代理对象。

线程安全问题

​ 和原生mybatis不同的是,创建出来的mapper是一个单例对象,按理说会存在线程安全问题。但是spring帮我们解决了这一点,当spring容器初始化的时候,会生成mapper的一个代理对象。每当mapper调用它的任何一个方法,都会经过代理对象的invoke方法。在这个invoke方法中,每次都会创建一个新的SqlSession,当这个方法调用完成后SqlSession方法也会随之销毁或者释放。

/*
SqlSessionTemplate > SqlSessionInterceptor > invoke()
*/
private class SqlSessionInterceptor implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      	//获取SqlSession对象
        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;
    ....
    ....

一级缓存还会生效吗

如果每次执行一个查询都会创建一个新的SqlSession,那么一级缓存不是不起作用了吗?

确实如此,但是当我们Service层方法加了@Transactional后,在这个有事务的方法内,第一次查询数据库,会把新建的SqlSession对象存入ThreadLoacl中,下一次进行查询时会先判断这个ThreadLocal中是否有对应的SqlSession对象。

/*
SqlSessionUtils > getSqlSession()
*/
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);

    //从ThreadLoacl中获取SqlSession
    SqlSession session = sessionHolder(executorType, holder);
    if (session != null) {
      return session;
    }

    LOGGER.debug(() -> "Creating a new SqlSession");
    //如果ThreadLoacl中没有的话就创建一个新的SqlSession
    session = sessionFactory.openSession(executorType);

    registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);

    return session;
  }
posted @ 2020-11-25 17:08  zcr小翟  阅读(99)  评论(0编辑  收藏  举报