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圣经
