7.1 MyBatis核心组件
面试重要程度:⭐⭐⭐⭐⭐
常见提问方式:SqlSession生命周期、Mapper接口代理机制
预计阅读时间:20分钟
开场白
兄弟,MyBatis的核心组件是面试中的高频考点!特别是SqlSession的生命周期管理和Mapper接口的代理机制,这些都是体现你对MyBatis底层原理理解的关键点。
今天我们就把这些核心组件的工作原理彻底搞清楚,让你在面试中展现出对ORM框架的深度理解。
🏗️ SqlSession生命周期
核心组件架构
面试必问:
面试官:"说说MyBatis的核心组件,它们之间是什么关系?"
标准回答框架:
// MyBatis核心组件层次结构 SqlSessionFactoryBuilder // 构建器(一次性使用) ↓ SqlSessionFactory // 会话工厂(应用级单例) ↓ SqlSession // 会话(线程不安全,请求级) ↓ Mapper // 映射器(代理对象)
详细实现:
// 1. SqlSessionFactoryBuilder - 构建SqlSessionFactory public class MyBatisConfig { public SqlSessionFactory createSqlSessionFactory() throws IOException { // 读取配置文件 String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); // 构建SqlSessionFactory(只需要构建一次) SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder() .build(inputStream); return sqlSessionFactory; } } // 2. SqlSessionFactory - 创建SqlSession的工厂 @Configuration public class SpringMyBatisConfig { @Bean @Singleton // 单例模式,整个应用共享 public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { SqlSessionFactoryBean factory = new SqlSessionFactoryBean(); factory.setDataSource(dataSource); factory.setMapperLocations(new PathMatchingResourcePatternResolver() .getResources("classpath:mapper/*.xml")); // 配置MyBatis设置 org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration(); configuration.setMapUnderscoreToCamelCase(true); configuration.setCacheEnabled(true); factory.setConfiguration(configuration); return factory.getObject(); } } // 3. SqlSession - 执行SQL的会话 public class UserService { @Autowired private SqlSessionFactory sqlSessionFactory; // 手动管理SqlSession(不推荐,仅用于理解原理) public User findUserById(Long id) { SqlSession sqlSession = null; try { // 创建SqlSession sqlSession = sqlSessionFactory.openSession(); // 获取Mapper UserMapper mapper = sqlSession.getMapper(UserMapper.class); // 执行查询 return mapper.selectById(id); } finally { // 必须关闭SqlSession if (sqlSession != null) { sqlSession.close(); } } } // 事务管理示例 public void transferBalance(Long fromId, Long toId, BigDecimal amount) { SqlSession sqlSession = null; try { // 开启手动提交事务 sqlSession = sqlSessionFactory.openSession(false); UserMapper mapper = sqlSession.getMapper(UserMapper.class); // 业务操作 User fromUser = mapper.selectById(fromId); User toUser = mapper.selectById(toId); fromUser.setBalance(fromUser.getBalance().subtract(amount)); toUser.setBalance(toUser.getBalance().add(amount)); mapper.updateById(fromUser); mapper.updateById(toUser); // 手动提交事务 sqlSession.commit(); } catch (Exception e) { // 回滚事务 if (sqlSession != null) { sqlSession.rollback(); } throw new RuntimeException("转账失败", e); } finally { if (sqlSession != null) { sqlSession.close(); } } } }
Spring集成后的生命周期管理
最佳实践:
// Spring管理SqlSession生命周期 @Service @Transactional public class UserServiceImpl implements UserService { // Spring自动注入Mapper代理对象 @Autowired private UserMapper userMapper; @Override public User findUserById(Long id) { // Spring自动管理SqlSession // 1. 从连接池获取数据库连接 // 2. 创建SqlSession并绑定到当前线程 // 3. 执行SQL // 4. 方法结束后自动关闭SqlSession return userMapper.selectById(id); } @Override @Transactional(rollbackFor = Exception.class) public void batchUpdateUsers(List<User> users) { // 同一个事务中使用同一个SqlSession for (User user : users) { userMapper.updateById(user); // 所有操作在同一个SqlSession中执行 // 事务结束时统一提交或回滚 } } @Override @Transactional(propagation = Propagation.REQUIRES_NEW) public void auditLog(String operation) { // 新事务,使用新的SqlSession auditMapper.insertLog(operation); } }
SqlSession线程安全性:
// 错误示例:SqlSession不是线程安全的 @Component public class BadExample { @Autowired private SqlSessionFactory sqlSessionFactory; // ❌ 错误:SqlSession作为成员变量 private SqlSession sqlSession = sqlSessionFactory.openSession(); public User getUser(Long id) { // 多线程访问会出现问题 UserMapper mapper = sqlSession.getMapper(UserMapper.class); return mapper.selectById(id); } } // 正确示例:每次使用时创建新的SqlSession @Component public class GoodExample { @Autowired private SqlSessionFactory sqlSessionFactory; public User getUser(Long id) { // ✅ 正确:每次创建新的SqlSession try (SqlSession sqlSession = sqlSessionFactory.openSession()) { UserMapper mapper = sqlSession.getMapper(UserMapper.class); return mapper.selectById(id); } } }
🎭 Mapper接口代理机制
代理机制原理
面试深入:
面试官:"MyBatis是如何通过接口就能执行SQL的?JDK动态代理是怎么工作的?"
核心实现分析:
// 1. MapperRegistry - Mapper注册中心 public class MapperRegistry { private final Configuration config; // 存储Mapper接口和对应的代理工厂 private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>(); public <T> void addMapper(Class<T> type) { if (type.isInterface()) { if (hasMapper(type)) { throw new BindingException("Type " + type + " is already known to the MapperRegistry."); } boolean loadCompleted = false; try { // 为每个Mapper接口创建代理工厂 knownMappers.put(type, new MapperProxyFactory<>(type)); // 解析Mapper接口的注解和XML配置 MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type); parser.parse(); loadCompleted = true; } finally { if (!loadCompleted) { knownMappers.remove(type); } } } } @SuppressWarnings("unchecked") public <T> T getMapper(Class<T> type, SqlSession sqlSession) { final MapperProxyF
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
Java面试圣经 文章被收录于专栏
Java面试圣经,带你练透java圣经