likes
comments
collection
share

mybatis源码探索之SqlSession

作者站长头像
站长
· 阅读数 26

SqlSessionFactory

SqlSessionFactory 使用了工厂模式创建SqlSession的,它的默认实现类是DefaultSqlSessionFactory。

方法

它共有以下几个方法:

public interface SqlSessionFactory {
  SqlSession openSession();
  SqlSession openSession(boolean autoCommit);
  SqlSession openSession(Connection connection);
  SqlSession openSession(TransactionIsolationLevel level);
  SqlSession openSession(ExecutorType execType);
  SqlSession openSession(ExecutorType execType, boolean autoCommit);
  SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);
  SqlSession openSession(ExecutorType execType, Connection connection);
  Configuration getConfiguration();
}

可以根据参数的不同创建不同特性的SqlSession,具体实现可以参考DefaultSqlSessionFactory源码

DefaultSqlSessionFactory

它的核心方法是openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit),从数据源中获取SqlSession。

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      final Environment environment = configuration.getEnvironment();
      //获取事务工厂,默认是ManagedTransactionFactory
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      // 获取执行器,默认是SimpleExecutor
      final Executor executor = configuration.newExecutor(tx, execType);
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      closeTransaction(tx); // may have fetched a connection so lets call close()
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

常用的方法是openSession(),但是调用的还是核心方法。通过无参的openSession()方法创建SqlSession时,事务默认不自动提交,事务隔离级别null,默认执行器类型是ExecutorType.REUSE

@Override
  public SqlSession openSession() {
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
  }

SqlSession

SqlSession 是 MyBaits 对外提供的最关键的核心接口,通过它可以执行数据库读写命令、获取映射器、管理事务等; SqlSession 也意味着客户端与数据库的一次连接,客户端对数据库的访问请求都是由SqlSession来处理的,SqlSession 由 SqlSessionFactory 创建,每个 SqlSession 都会引用 SqlSessionFactory 中全局唯一单例存在的 configuration 对象。 SqlSession的默认实现类是DefaultSqlSession

方法

SqlSession的方法主要分为以下6类:

1、SQL语句执行方法 2、立即批量更新方法 3、事务控制方法 4、本地缓存 5、映射器获取 6、SqlSession关闭

对应的主要方法如下: SqlSession的方法中参数statement,都是**Mapper接口中的方法的全限定名,例如:mapperClasses.UserInfoMapper.selectByPrimaryKey。

1、SQL语句执行方法


int delete(String statement, Object parameter);
int insert(String statement, Object parameter);
int update(String statement, Object parameter);

void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler);
<T> T selectOne(String statement, Object parameter);
 <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds);
 <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler);
 <T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds);

delete、insert方法最终调用的还是update方法,update源码如下:

public int update(String statement, Object parameter) {
    try {
      dirty = true;
      // 获取MappedStatement 
      MappedStatement ms = configuration.getMappedStatement(statement);
      return executor.update(ms, wrapCollection(parameter));
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error updating database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

select、selectOne最终调用的是selectList方法,selectMap方法也是通过selectList方法查询到结果的,只不过再查询之后又转换成了map对象。 selectList方法源码如下:

private <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
    try {
      MappedStatement ms = configuration.getMappedStatement(statement);
      return executor.query(ms, wrapCollection(parameter), rowBounds, handler);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

selectOne源码:查询结果集如果是一个就取这个,多个则抛异常

public <T> T selectOne(String statement, Object parameter) {
    // Popular vote was to return null on 0 results and throw exception on too many.
    List<T> list = this.selectList(statement, parameter);
    if (list.size() == 1) {
      return list.get(0);
    } else if (list.size() > 1) {
      throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
    } else {
      return null;
    }
  }

selectCursor提供了与List相同的结果,只是它使用迭代器惰性地获取数据。 selectCursor 应该很少使用(应该吧,至少我没有用过)

public <T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds) {
    try {
      MappedStatement ms = configuration.getMappedStatement(statement);
      Cursor<T> cursor = executor.queryCursor(ms, wrapCollection(parameter), rowBounds);
      registerCursor(cursor);
      return cursor;
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

2、立即批量更新方法

只有这个方法: 可以使用这个方法清除(执行)缓存在 JDBC 驱动类中的批量更新语句。 List flushStatements();

public List<BatchResult> flushStatements() {
    try {
      return executor.flushStatements();
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error flushing statements.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

3、事务控制方法

//刷新缓存在 JDBC 驱动类中的批量语句并提交数据库连接,如果数据库连接没有updates/deletes/inserts被调用则不会提交
void commit();
//commit()升级版,可以根据参数来决定是否强制提交
void commit(boolean force);

//丢弃挂起的批处理语句并回滚数据库连接,如果数据库连接没有updates/deletes/inserts被调用则不会回滚
void rollback();
//rollback()的升级版,可以根据参数来决定是否强制回滚
void rollback(boolean force);

4、本地缓存

//清空本地缓存,只是清空本地缓存,跟二级缓存没关系
void clearCache();

5、映射器获取

//从configuration对象中的mapperRegistry(mapper注册表)中获取对应的*mapper对象
public <T> T getMapper(Class<T> type) {
    return configuration.getMapper(type, this);
  }

6、SqlSession关闭方法

//关闭session会话
void close();

举个栗子

public class UserTest {

  private final static SqlSessionFactory sqlSessionFactory;
  static {
    String resource = "mybatis-config.xml";
    Reader reader = null;
    try {
      reader = Resources.getResourceAsReader(resource);
    } catch (IOException e) {
      System.out.println(e.getMessage());
    }
    sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
  }
  @Test
  public void test(){
    SqlSession sqlSession = sqlSessionFactory.openSession();

    UserInfoMapper mapper = sqlSession.getMapper(UserInfoMapper.class);
    UserInfo userInfos = sqlSession.selectOne("mapperClasses.UserInfoMapper.selectByPrimaryKey" , 1L);
    System.out.println(userInfos);
    UserInfo userInfo = mapper.selectByPrimaryKey(4L);
    System.out.println(userInfo);
  }
}

使用mybatis进行crud有两种方式:

1、通过sqlSession提供的操作进行crud,这种是最原始的使用方式。这种方式可读性、可维护性都很差,一般很少使用。

UserInfo userInfos = sqlSession.selectOne("mapperClasses.UserInfoMapper.selectByPrimaryKey" , 1L);

2、通过mapper进行crud,最终调用的还是第一种的方式。这种方式是ibatis被谷歌收购后,谷歌进行升级的

UserInfoMapper mapper = sqlSession.getMapper(UserInfoMapper.class);
UserInfo userInfo = mapper.selectByPrimaryKey(4L);

能力有限,水平一般,如有错误,请多指出。