1. 程式人生 > 實用技巧 >I/O輸出輸入流

I/O輸出輸入流

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;
  }