数据库 connection 跟session 的概念
数据库连接(Connection)和会话(Session)是数据库交互中的两个核心概念,它们密切相关但又有明确的区别。
数据库连接(Connection)
数据库连接是应用程序与数据库服务器之间的物理通信通道(如 TCP 连接)。它通过驱动建立,常见的驱动有jdbc,连接负责传输 SQL 语句和返回结果。
为提高性能,常用连接池复用连接,避免频繁打开关闭连接。
配置示例:
# Druid 连接池配置
#连接池初始化时创建的 物理连接数量
spring.datasource.druid.initial-size=5
#连接池中始终保持的 最小空闲连接数。
spring.datasource.druid.min-idle=5
#连接池支持的 最大活跃连接数
spring.datasource.druid.max-active=20
#从连接池获取连接时 最大等待时间(单位:毫秒),超时后抛异常
spring.datasource.druid.max-wait=60000
#用于检测连接是否有效的 验证 SQL 语句,如果执行失败,连接会被丢弃并重建
spring.datasource.druid.validation-query=SELECT 1
#检测连接有效性是否启用,Druid 会对空闲连接定期检查(默认每隔 1 分钟),确保连接池中的连接可用
spring.datasource.druid.test-while-idle=true
#每次获取连接时都会执行 validation-query,确保连接有效,默认 false,频繁检测会影响性能
spring.datasource.druid.test-on-borrow=false
# 是否在归还连接时检测其有效性,确保连接未被破坏
spring.datasource.druid.test-on-return=false
会话(Session)
会话是数据库服务器为单个客户端连接创建的逻辑工作环境,用于跟踪和管理用户的操作状态。
每个会话独立,操作不会直接影响其他会话(除非显式提交事务)。
会话与连接的关系
ession 的生命周期会受到 Connection 的控制,在 Connection 连接关闭或超时终止,Session 被销毁,当连接被连接池回收时,重置 Session 的状态,而不是销毁。
会话与事务的关系
Session 是事务的载体,一个 Session 可以串行执行多个事务。
Mybatis 的 Sqlssion 是怎么管理session 的
创建SqlSession:
通过SqlSessionFactory的openSession()方法创建SqlSession实例。
在创建过程中,MyBatis会加载配置文件,建立数据库连接等。
执行数据库操作:
使用SqlSession的selectOne()、selectList()、insert()、update()、delete()等方法执行SQL语句。
MyBatis将SQL语句映射为Java对象,执行数据库操作,并处理结果。
事务管理:
SqlSession支持事务管理,可以通过commit()方法提交事务,或通过rollback()方法回滚事务。
关闭SqlSession:
完成数据库操作后,应调用SqlSession的close()方法关闭SqlSession。
关闭SqlSession会释放数据库连接等资源,避免资源泄露。
部分源码说明 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) {
Throwable unwrapped = unwrapThrowable(t);
if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
// release the connection to avoid a deadlock if the translator is no loaded. See issue #22
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
sqlSession = null;
Throwable translated = SqlSessionTemplate.this.exceptionTranslator
.translateExceptionIfPossible((PersistenceException) unwrapped);
if (translated != null) {
unwrapped = translated;
}
}
throw unwrapped;
} finally {
if (sqlSession != null) {
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
}
}
-
对于自动提交的事务,每条sql 是独立的事务,调用一次mapper里的方法将会新建一个 sqlSession 来执行方法。
-
同一事务中 不管调用多少次 mapper里的方法 ,最终都是用得同一个 sqlSession,即一个事务中使用的是同一个sqlSession,底层是由 ThreadLocal 保证的。
TransactionSynchronizationManager
private static final ThreadLocal<Map<Object, Object>> resources = new NamedThreadLocal("Transactional resources");
-
如果没有开启事务,调用一次mapper里的方法将会新建一个 sqlSession 来执行方法。
总结
使用连接池时,多个 HTTP 请求可能共享同一物理连接,但每个请求会绑定到独立会话(通过重置会话状态实现隔离)。
连接是物理层面的,可以类比为马路,关注 传输效率(如网络延迟、带宽),会话是逻辑层面, 关注的是操作正确性,可以类比为车。
举一个生活中的示例:
Connection 就像你和客服之间的电话线路(物理通道);
负责传输声音(数据),线路本身不记录对话内容;
如果挂断电话(关闭连接),线路会被释放。
Session 就像客服的工作台(逻辑环境):
记录当前对话的上下文(如你的账户信息、问题进展等);
如果线路保持畅通(连接未关闭),客服可以处理你的多个请求(多个事务);
如果换人接听(连接池复用),新客服会重置工作台(清理前一次会话状态);
如果电话线拆了(连接关闭),工作台也就不要了(销毁会话状态)。
参考:
https://juejin.cn/post/6844904002421800967