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

SqlSession 获取 Mapper

SqlSession.getMapper() 是获取 Mapper 代理对象的核心方法。理解其工作机制和生命周期管理,有助于写出更健壮的代码。

getMapper 方法使用

基础用法

Java
// 获取 SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
    // 获取 Mapper 代理对象
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);

    // 调用接口方法
    User user = mapper.selectById(1L);
    System.out.println(user);

    sqlSession.commit();
} finally {
    sqlSession.close();
}

多个 Mapper 操作

Java
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class);

    // 同一事务中的操作
    User user = new User();
    user.setUsername("test");
    userMapper.insert(user);

    Order order = new Order();
    order.setUserId(user.getId());
    orderMapper.insert(order);

    sqlSession.commit();
} finally {
    sqlSession.close();
}

注意:同一个 SqlSession 获取的多个 Mapper 共享同一事务,任一操作失败需整体回滚。

getMapper 内部工作原理

getMapper() 的调用链如下:

Java
sqlSession.getMapper(UserMapper.class)
    ↓
Configuration.getMapper(type, this)
    ↓
MapperRegistry.getMapper(type, this)
    ↓
MapperProxyFactory.newInstance(sqlSession)
    ↓
Proxy.newProxyInstance(classLoader, interfaces, mapperProxy)
    ↓
返回代理对象

核心源码分析

Java
// 1. SqlSession 调用
public <T> T getMapper(Class<T> type) {
    return configuration.getMapper(type, this);
}

// 2. Configuration 委托给 MapperRegistry
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
}

// 3. MapperRegistry 查找工厂并创建代理
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    final MapperProxyFactory<T> factory = knownMappers.get(type);
    if (factory == null) {
        throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    return factory.newInstance(sqlSession);
}

// 4. MapperProxyFactory 创建代理
public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    return (T) Proxy.newProxyInstance(
        mapperInterface.getClassLoader(),
        new Class[] { mapperInterface },
        mapperProxy
    );
}

MapperProxy 拦截器

代理对象的方法调用被 MapperProxy.invoke() 拦截:

Java
public class MapperProxy<T> implements InvocationHandler {
    private final SqlSession sqlSession;
    private final Class<T> mapperInterface;
    private final Map<Method, MapperMethod> methodCache;

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // Object 类方法直接调用,不走 SQL 逻辑
        if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, args);
        }

        // 获取或缓存 MapperMethod
        final MapperMethod mapperMethod = cachedMapperMethod(method);

        // 执行 SQL
        return mapperMethod.execute(sqlSession, args);
    }

    private MapperMethod cachedMapperMethod(Method method) {
        return methodCache.computeIfAbsent(method, key ->
            new MapperMethod(mapperInterface, method, sqlSession.getConfiguration())
        );
    }
}

生命周期管理

SqlSession 生命周期

范围说明推荐做法
请求级别每个 HTTP 请求一个 SqlSession推荐,线程安全
方法级别每个方法内创建和关闭最安全,推荐
类级别整个类共享一个 SqlSession不推荐,线程不安全
全局级别全局单例 SqlSession错误,严重线程安全问题
Java
// 推荐:方法级别管理(try-finally)
public User getUserById(Long id) {
    SqlSession sqlSession = sqlSessionFactory.openSession();
    try {
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        return mapper.selectById(id);
    } finally {
        sqlSession.close();
    }
}

Spring 环境下的生命周期

在 Spring 环境中,SqlSession 由 Spring 容器管理,无需手动创建和关闭:

Java
@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;  // Spring 自动注入代理对象

    public User getUserById(Long id) {
        return userMapper.selectById(id);
    }
}

Spring 通过 SqlSessionTemplate 管理 SqlSession 的生命周期,每次方法调用时获取 SqlSession,执行后自动释放。

线程安全问题

SqlSession 不是线程安全的,不能在多线程间共享:

Java
// 错误:多线程共享 SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 0; i < 10; i++) {
    executor.submit(() -> {
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        mapper.selectById(1L);  // 线程不安全!
    });
}

// 正确:每个线程独立 SqlSession
ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 0; i < 10; i++) {
    executor.submit(() -> {
        SqlSession session = sqlSessionFactory.openSession();
        try {
            UserMapper mapper = session.getMapper(UserMapper.class);
            mapper.selectById(1L);
        } finally {
            session.close();
        }
    });
}

SqlSessionFactory 与 SqlSession 关系

text
SqlSessionFactory(线程安全,单例)
    ↓ openSession()
SqlSession(非线程安全,每次请求创建)
    ↓ getMapper()
Mapper 代理对象(无状态,可复用)
    ↓ 调用方法
执行 SQL
text
// SqlSessionFactory 通常在应用启动时创建一次
public class MyBatisUtil {
    private static SqlSessionFactory sqlSessionFactory;

    static {
        try {
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    // 获取 SqlSession(非单例)
    public static SqlSession getSqlSession() {
        return sqlSessionFactory.openSession();
    }
}

要点总结

  • getMapper 通过 JDK 动态代理创建 Mapper 接口的代理对象
  • 代理对象的方法调用被 MapperProxy 拦截,转为 SQL 执行
  • SqlSession 不是线程安全的,不能在多线程间共享
  • 推荐在方法级别管理 SqlSession 生命周期,使用 try-finally 确保关闭
  • Spring 环境下由 SqlSessionTemplate 自动管理生命周期,无需手动操作
  • SqlSessionFactory 是线程安全的单例,SqlSession 是每次请求创建的新实例
  • 同一个 SqlSession 获取的多个 Mapper 共享同一事务

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

← 上一篇 Mapper 接口定义
下一篇 → 接口绑定原理
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

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

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