全部学科
NodeJS全栈
nodejs
Python全栈
python
小程序首页
📅 2026-05-20 8 分钟 ✍️ juanwangdev

SqlSession 生命周期

SqlSession 是 MyBatis 的核心操作接口,理解其生命周期对正确使用连接池、事务管理至关重要。

生命周期概览

Java
创建              执行              提交/回滚            关闭
  |                |                  |                  |
  v                v                  v                  v
openSession() -> select/insert -> commit()/rollback() -> close()
  ↑                ↑                  ↑                  ↑
获取Connection  获取Statement      事务边界             释放Connection

SqlSession 创建过程

openSession 方法族

Java
public class DefaultSqlSessionFactory implements SqlSessionFactory {
  
  @Override
  public SqlSession openSession() {
    // 使用默认配置:自动提交=false,从数据源获取连接
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
  }
  
  @Override
  public SqlSession openSession(boolean autoCommit) {
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, autoCommit);
  }
  
  @Override
  public SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level) {
    return openSessionFromDataSource(execType, level, false);
  }
  
  @Override
  public SqlSession openSession(Connection connection) {
    // 使用外部传入的连接,不管理连接生命周期
    return openSessionFromConnection(configuration.getDefaultExecutorType(), false);
  }
}

从 DataSource 创建 Session

Java
private SqlSession openSessionFromDataSource(ExecutorType execType, 
                                              TransactionIsolationLevel level, 
                                              boolean autoCommit) {
  Transaction tx = null;
  try {
    // 1. 获取 Environment 配置
    final Environment environment = configuration.getEnvironment();
    final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
    
    // 2. 从数据源获取连接
    tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
    
    // 3. 创建 Executor(应用插件拦截)
    final Executor executor = configuration.newExecutor(tx, execType);
    
    // 4. 包装为 DefaultSqlSession
    return new DefaultSqlSession(configuration, executor, autoCommit);
  } catch (Exception e) {
    closeTransaction(tx); // 发生异常时关闭事务
    throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
  } finally {
    ErrorContext.instance().reset();
  }
}

关键点:创建 SqlSession 时获取数据库连接,但连接的实际获取是延迟到第一次执行 SQL 时(由数据源连接池控制)。

SqlSession 内部结构

Java
public class DefaultSqlSession implements SqlSession {
  private final Configuration configuration;   // 全局配置(线程安全)
  private final Executor executor;             // 执行器(非线程安全)
  private final boolean autoCommit;            // 是否自动提交
  private boolean dirty;                       // 是否有未提交的修改
  private List<Cursor<?>> cursorList;          // 持有的游标列表
}
字段线程安全说明
configuration全局单例,只读配置
executor持有 Connection,包含状态
autoCommit创建时确定,不变
dirty标记是否有写操作未提交
cursorList管理查询游标生命周期

执行方法

查询操作

Java
@Override
public <T> T selectOne(String statement, Object parameter) {
  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 but found " + list.size());
  }
  return null;
}

@Override
public <E> List<E> selectList(String statement, Object parameter) {
  return this.selectList(statement, parameter, RowBounds.DEFAULT);
}

@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
  try {
    // 通过 MappedStatement ID 获取 SQL 定义
    MappedStatement ms = configuration.getMappedStatement(statement);
    
    // 委托给 Executor 执行
    return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
  } catch (Exception e) {
    throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
  } finally {
    ErrorContext.instance().reset();
  }
}

更新操作

Java
@Override
public int insert(String statement, Object parameter) {
  return update(statement, parameter);
}

@Override
public int update(String statement, Object parameter) {
  try {
    dirty = true;  // 标记为 dirty,表示有未提交的修改
    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();
  }
}

dirty 标记作用:执行 insert/update/delete 后设置 dirty=true,commit 时根据此标记判断是否需要实际提交事务。

事务提交与回滚

commit 流程

Java
@Override
public void commit(boolean force) {
  try {
    // force=true 强制提交,dirty=true 有修改时提交
    executor.commit(isCommitOrRollbackRequired(force));
    dirty = false;  // 重置 dirty 标记
  } catch (Exception e) {
    throw ExceptionFactory.wrapException("Error committing transaction. Cause: " + e, e);
  } finally {
    ErrorContext.instance().reset();
  }
}

private boolean isCommitOrRollbackRequired(boolean force) {
  // autoCommit=true 时需要手动提交
  // dirty=true 且有 force 标记时需要提交
  return (!autoCommit && dirty) || force;
}

Executor.commit 内部

Java
public void commit(boolean required) throws SQLException {
  if (closed) throw new ExecutorException("Cannot commit, session is closed.");
  
  // 1. 清理本地缓存(防止脏读)
  clearLocalCache();
  
  // 2. 刷新批量执行语句
  flushStatements();
  
  // 3. 提交事务
  if (required) {
    transaction.commit();
  }
}

rollback 流程

Java
@Override
public void rollback(boolean force) {
  try {
    executor.rollback(isCommitOrRollbackRequired(force));
    dirty = false;  // 重置 dirty 标记
  } catch (Exception e) {
    throw ExceptionFactory.wrapException("Error rolling back transaction. Cause: " + e, e);
  } finally {
    ErrorContext.instance().reset();
  }
}

close 流程

Java
@Override
public void close() {
  try {
    // 1. 关闭所有持有的 Cursor
    for (Cursor<?> cursor : cursorList) {
      cursor.close();
    }
    cursorList.clear();
    
    // 2. 关闭 Executor
    executor.close(isCommitOrRollbackRequired(false));
    
    // 3. 重置 dirty
    dirty = false;
  } finally {
    ErrorContext.instance().reset();
  }
}

Executor.close 内部

Java
public void close(boolean forceRollback) {
  try {
    // 1. 回滚未提交的修改
    if (forceRollback) {
      rollback(true);
    }
    
    // 2. 清理缓存
    clearLocalCache();
    
    // 3. 关闭事务,释放连接回连接池
    transaction.close();
    
  } finally {
    this.closed = true;
  }
}

重要:close() 不是简单的资源释放,如果存在未提交的修改且 autoCommit=false,close 会触发隐式回滚,防止数据不一致。

线程安全性分析

为什么 SqlSession 不是线程安全的

Java
线程A                         线程B
  |                             |
  |--executor.query()---------->|
  |                             |--executor.update()---|
  |                             |                      |
  |<--返回结果                   |                      |
  |                    <-------|--共享同一 Connection  |
  |                    事务被意外提交/回滚              |
  |                    查询结果不可预期                 |

根本原因

  1. Connection 共享:SqlSession 持有的 Connection 在同一时刻只能被一个线程使用
  2. 事务状态共享:dirty、autoCommit 等状态会被多线程并发修改
  3. Executor 状态:LocalCache(一级缓存)会被多线程互相污染

正确使用方式

text
// ❌ 错误:多线程共享 SqlSession
SqlSession session = factory.openSession();
new Thread(() -> session.selectList("findAllUsers")).start();
new Thread(() -> session.insert("addUser", user)).start();

// ✅ 正确:每个线程独立 SqlSession
SqlSession session1 = factory.openSession();
SqlSession session2 = factory.openSession();
new Thread(() -> { 
  try { session1.selectList("findAllUsers"); } 
  finally { session1.close(); } 
}).start();
new Thread(() -> { 
  try { session2.insert("addUser", user); session2.commit(); } 
  finally { session2.close(); } 
}).start();

MyBatis-Spring 中的线程安全处理

text
// SqlSessionTemplate 使用动态代理 + ThreadLocal 保证线程安全
public class SqlSessionTemplate implements SqlSession {
  
  private final SqlSessionFactory sqlSessionFactory;
  private final ExecutorType executorType;
  private final SqlSession sqlSessionProxy;  // 动态代理
  
  public SqlSessionTemplate(SqlSessionFactory factory) {
    this.sqlSessionFactory = factory;
    this.executorType = factory.getConfiguration().getDefaultExecutorType();
    
    // 创建代理,每次方法调用都获取新的 SqlSession
    this.sqlSessionProxy = (SqlSession) Proxy.newProxyInstance(
      SqlSessionFactory.class.getClassLoader(),
      new Class[]{SqlSession.class},
      new SqlSessionInterceptor());
  }
  
  private class SqlSessionInterceptor implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      // 每次调用都获取当前线程的 SqlSession
      SqlSession session = getSqlSession(
        SqlSessionTemplate.this.sqlSessionFactory,
        SqlSessionTemplate.this.executorType);
      return method.invoke(session, args);
    }
  }
}

连接管理时序图

text
应用代码              SqlSession           Executor          Transaction         DataSource
  |                      |                   |                   |                  |
  |--openSession()------>|                   |                   |                  |
  |                      |--newTransaction-->|                   |                  |
  |                      |                   |--newTransaction()->|                  |
  |                      |                   |                   |--getConnection()->|
  |                      |                   |                   |<--Connection------|
  |                      |<------------------|<------------------|                  |
  |<--session------------|                   |                   |                  |
  |                      |                   |                   |                  |
  |--selectList()------->|                   |                   |                  |
  |                      |--query()--------->|                   |                  |
  |                      |                   |--执行SQL---------->|                  |
  |                      |                   |<--结果-------------|                  |
  |                      |<--List------------|                   |                  |
  |                      |                   |                   |                  |
  |--commit()----------->|                   |                   |                  |
  |                      |--commit()------->|                   |                  |
  |                      |                   |--commit()-------->|                  |
  |                      |                   |                   |--commit()------->|
  |                      |<------------------|<------------------|<-----------------|
  |                      |                   |                   |                  |
  |--close()------------>|                   |                   |                  |
  |                      |--close()-------->|                   |                  |
  |                      |                   |--close()-------->|                  |
  |                      |                   |                   |--close()-------->|
  |                      |                   |                   |  (返回连接池)    |

要点总结

  • 生命周期四阶段:创建(openSession)→ 执行(select/insert/update/delete)→ 提交(commit/rollback)→ 关闭(close)
  • 非线程安全:SqlSession 内部持有状态的 Executor 和 Connection,多线程共享会导致数据不一致
  • dirty 标记机制:执行写操作时设置,commit/rollback/close 时根据此标记判断是否需要实际提交或回滚
  • 隐式回滚:close() 时如果有未提交的修改且 autoCommit=false,会自动回滚,防止数据不一致
  • 一级缓存清理:commit 和 close 时都会清理 localCache,防止脏读
  • Spring 集成方案:SqlSessionTemplate 使用动态代理 + ThreadLocal 实现线程安全的 SqlSession 代理

📝 发现内容有误?点击此处直接编辑

← 上一篇 ResultSetHandler 结果处理
下一篇 → SqlSessionFactory 构建流程
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库