MyBatis SqlSession 的作用,以及如何使用 SqlSession 执行 SQL 语句
SqlSession
是 MyBatis 中非常重要的一个接口,它代表了与数据库的一次会话(session)。 可以将 SqlSession
理解为 JDBC 中的 Connection
对象加上一系列操作数据库的方法。 它负责:
SqlSession 的作用:
- 执行 SQL 语句:
SqlSession
提供了执行 SQL 语句的方法,包括select
、insert
、update
和delete
操作。 - 获取 Mapper 接口的实例:
SqlSession
可以获取 Mapper 接口的代理对象,从而通过 Mapper 接口来执行 SQL 语句 (这是推荐的方式)。 - 管理事务:
SqlSession
可以控制事务的提交(commit)和回滚(rollback)。 - 维护数据库连接:
SqlSession
内部持有数据库连接(Connection
),并在会话结束时负责关闭连接(除非使用了外部事务管理器,如 Spring 的事务管理)。 - 缓存管理(一级缓存):
SqlSession
拥有一个本地缓存(一级缓存),可以缓存查询结果,减少数据库访问次数(默认开启)。
如何使用 SqlSession 执行 SQL 语句:
SqlSession
的使用通常遵循以下步骤:
-
获取
SqlSessionFactory
:SqlSessionFactory
是SqlSession
的工厂,负责创建SqlSession
对象。通常情况下,SqlSessionFactory
在应用程序的生命周期内只需要创建一个实例(单例模式)。SqlSessionFactory
可以通过 XML 配置文件或 Java 代码来创建。 -
通过
SqlSessionFactory
打开SqlSession
: 使用SqlSessionFactory
的openSession()
方法打开一个新的SqlSession
。 -
执行 SQL 语句:
- 直接使用
SqlSession
的 API (不推荐): 使用SqlSession
的selectOne()
、selectList()
、insert()
、update()
、delete()
等方法直接执行 SQL 语句。 这种方式需要传入 SQL 语句的 ID(在 XML 映射文件中定义)和参数。 - 获取 Mapper 接口的代理对象 (推荐): 使用
SqlSession
的getMapper()
方法获取 Mapper 接口的代理对象,然后调用 Mapper 接口的方法来执行 SQL 语句。
- 直接使用
-
提交或回滚事务 (如果需要): 如果进行了数据库修改操作(
insert
、update
、delete
),需要手动提交事务(commit()
)或回滚事务(rollback()
)。 如果只进行了查询操作(select
),则不需要。 -
关闭
SqlSession
: 使用SqlSession
的close()
方法关闭会话,释放资源。 务必在finally
块中关闭SqlSession
,以确保资源被正确释放。
代码示例 (两种方式):
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import com.example.mapper.UserMapper; // 假设的 Mapper 接口
import com.example.model.User; // 假设的 User 实体类
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class MyBatisExample {
public static void main(String[] args) throws IOException {
// 1. 获取 SqlSessionFactory
String resource = "mybatis-config.xml"; // MyBatis 配置文件路径
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 方式一:直接使用 SqlSession 的 API (不推荐)
SqlSession session1 = null;
try {
session1 = sqlSessionFactory.openSession();
// 执行查询
User user1 = session1.selectOne("com.example.mapper.UserMapper.selectUserById", 1); // namespace + id
System.out.println("方式一 (selectOne): " + user1);
// 执行插入
User newUser = new User("testuser", "testpassword");
int rowsAffected = session1.insert("com.example.mapper.UserMapper.insertUser", newUser);
System.out.println("方式一 (insert): 插入了 " + rowsAffected + " 行");
// 执行更新
newUser.setPassword("newpassword");
session1.update("com.example.mapper.UserMapper.updateUser", newUser);
//需要手动提交
session1.commit();
} finally {
if (session1 != null) {
session1.close();
}
}
// 方式二:获取 Mapper 接口的代理对象 (推荐)
SqlSession session2 = null;
try {
session2 = sqlSessionFactory.openSession();
// 获取 Mapper 接口的实例
UserMapper userMapper = session2.getMapper(UserMapper.class);
// 执行查询
User user2 = userMapper.selectUserById(1);
System.out.println("方式二 (Mapper 接口): " + user2);
// 执行查询
List<User> users = userMapper.getUsersByName("%test%"); //假设定义了一个getUsersByName的方法
System.out.println("方式二 (Mapper 接口, List): " + users);
// 执行插入
User newUser2 = new User("testuser2", "testpassword2");
int rowsAffected2 = userMapper.insertUser(newUser2);
System.out.println("方式二 (Mapper 接口, insert): 插入了 " + rowsAffected2 + " 行, 新用户 ID: " + newUser2.getId()); //假设使用了自增主键
//执行删除
userMapper.deleteUserById(newUser2.getId()); //假设定义了一个deleteUserById方法
// 提交事务
session2.commit();
} catch (Exception e){
//出现异常回滚
if(session2 != null){
session2.rollback();
}
e.printStackTrace();
} finally {
if (session2 != null) {
session2.close();
}
}
}
}
重要说明:
- 线程安全:
SqlSession
对象不是线程安全的,每个线程应该拥有自己的SqlSession
实例。 不要将SqlSession
实例作为类的成员变量或静态变量,否则会导致并发问题。 应该在方法内部创建和使用SqlSession
。 - 事务管理:
openSession()
方法有多个重载版本:openSession()
: 默认不自动提交事务。openSession(true)
: 自动提交事务(一般不推荐,除非你确定每次操作都立即提交)。openSession(ExecutorType executorType)
: 指定执行器类型(SIMPLE
、REUSE
、BATCH
)。openSession(Connection connection)
: 使用外部提供的数据库连接。openSession(TransactionIsolationLevel level)
: 设置事务隔离级别。
- 与 Spring 集成: 在 Spring 项目中,通常不需要手动创建和关闭
SqlSession
,Spring 会通过SqlSessionTemplate
或MapperFactoryBean
来管理SqlSession
的生命周期,并自动处理事务。 - 一级缓存: 默认情况下,MyBatis 会为每个
SqlSession
开启一级缓存。在同一个SqlSession
中,如果执行相同的 SQL 查询(相同的 SQL 语句和参数),MyBatis 会直接从缓存中获取结果,而不会再次访问数据库。 一级缓存的范围是SqlSession
级别的。 可以通过session.clearCache()
方法清除一级缓存。
总而言之,SqlSession
是 MyBatis 中执行 SQL 语句、管理事务和获取 Mapper 接口实例的核心接口。推荐使用 Mapper 接口的方式来执行 SQL 语句,这种方式更加简洁、类型安全,并且易于维护。