- MyBatis 框架概述与设计哲学
1.1 ORM 框架演进历程
对象关系映射(ORM)技术经历了多个发展阶段:
全自动映射:Hibernate 等框架尝试完全抽象数据库细节
半自动映射:MyBatis 采用 SQL 与对象分离的方式
微ORM:Lightweight ORM 框架提供更简单的解决方案
1.2 MyBatis 设计理念
MyBatis 的核心设计哲学体现在以下几个方面:
SQL 灵活性:开发者完全控制 SQL 语句的编写和执行
简化JDBC:消除繁琐的JDBC样板代码,保持底层控制力
结果集映射:提供强大的对象-关系映射能力
可扩展架构:通过插件机制支持功能扩展
1.3 适用场景分析
MyBatis 在以下场景中表现尤为出色:
复杂SQL需求:需要编写优化过的复杂SQL查询
遗留系统集成:与现有数据库结构和存储过程集成
性能敏感应用:需要对数据库操作进行精细控制
SQL技能丰富团队:开发团队具备较强的SQL能力
核心架构与配置详解
2.1 基础配置架构
xml
<mapper resource="mapper/UserMapper.xml"/> <mapper class="com.example.mapper.UserMapper"/> <package name="com.example.mapper"/>
2.2 SqlSessionFactory 构建
java
@Configuration
public class MyBatisConfig {@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean(); sessionFactory.setDataSource(dataSource); sessionFactory.setConfigLocation( new ClassPathResource("mybatis-config.xml")); sessionFactory.setMapperLocations( new PathMatchingResourcePatternResolver() .getResources("classpath:mapper/*.xml")); // 配置插件 sessionFactory.setPlugins(new PageInterceptor()); return sessionFactory.getObject();}
@Bean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {return new SqlSessionTemplate(sqlSessionFactory);}
}映射器与SQL映射
3.1 XML 映射器配置
xml
SELECT * FROM users WHERE user_id = #{id}
INSERT INTO users (user_name, email, create_time)
VALUES (#{username}, #{email}, #{createTime})
UPDATE users
SET user_name = #{username}, email = #{email}
WHERE user_id = #{id}
DELETE FROM users WHERE user_id = #{id}
3.2 注解方式映射
java
public interface UserMapper {@Select("SELECT * FROM users WHERE user_id = #{id}")
@Results({@Result(property = "id", column = "user_id"), @Result(property = "username", column = "user_name"), @Result(property = "email", column = "email")})
User selectById(Long id);@Insert("INSERT INTO users (user_name, email, create_time) " +
"VALUES (#{username}, #{email}, #{createTime})")@Options(useGeneratedKeys = true, keyProperty = "id")
int insert(User user);@Update("UPDATE users SET user_name = #{username}, email = #{email} " +
"WHERE user_id = #{id}")int update(User user);
@Delete("DELETE FROM users WHERE user_id = #{id}")
int delete(Long id);// Provider注解方式
@SelectProvider(type = UserSqlProvider.class, method = "selectByCondition")
List selectByCondition(User condition);
}- 动态SQL与高级查询
4.1 动态SQL标签
xml
SELECT FROM users
AND user_name LIKE CONCAT('%', #{username}, '%')
AND email = #{email}
AND create_time >= #{startTime}
AND create_time <= #{endTime}
AND status IN
#{status}
ORDER BY ${orderBy}
ORDER BY create_time DESC
4.2 复杂查询处理
xml
SELECT FROM users
LIMIT #{offset}, #{pageSize}
SELECT u.*, d.dept_name
FROM users u
LEFT JOIN departments d ON u.dept_id = d.dept_id
WHERE u.user_id = #{userId}
INSERT INTO users (user_name, email, create_time) VALUES
(#{user.username}, #{user.email}, #{user.createTime})
UPDATE users
SET user_name = #{user.username}, email = #{user.email}
WHERE user_id = #{user.id}
- 缓存机制与性能优化
5.1 缓存配置策略
xml
5.2 自定义缓存实现
java
public class RedisCache implements Cache {
private final String id;
private final RedisTemplate<String, Object> redisTemplate;
public RedisCache(String id) {
this.id = id;
this.redisTemplate = createRedisTemplate();
}
@Override
public String getId() {
return id;
}
@Override
public void putObject(Object key, Object value) {
redisTemplate.opsForValue().set(key.toString(), value, 1, TimeUnit.HOURS);
}
@Override
public Object getObject(Object key) {
return redisTemplate.opsForValue().get(key.toString());
}
@Override
public Object removeObject(Object key) {
redisTemplate.delete(key.toString());
return null;
}
@Override
public void clear() {
Set<String> keys = redisTemplate.keys(id + "*");
if (keys != null) {
redisTemplate.delete(keys);
}
}
@Override
public int getSize() {
Set<String> keys = redisTemplate.keys(id + "*");
return keys != null ? keys.size() : 0;
}
private RedisTemplate<String, Object> createRedisTemplate() {
// 创建并配置RedisTemplate
return new RedisTemplate<>();
}
}
插件开发与扩展
6.1 自定义插件开发
java
@Intercepts({
@Signature(type = Executor.class, method = "query",args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),@Signature(type = Executor.class, method = "update",
args = {MappedStatement.class, Object.class})})
public class PerformanceInterceptor implements Interceptor {private static final Logger logger = LoggerFactory.getLogger(PerformanceInterceptor.class);
@Override
public Object intercept(Invocation invocation) throws Throwable {MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0]; String methodName = mappedStatement.getId(); long startTime = System.currentTimeMillis(); try { return invocation.proceed(); } finally { long endTime = System.currentTimeMillis(); long duration = endTime - startTime; if (duration > 1000) { logger.warn("Slow SQL detected: {} - {}ms", methodName, duration); } logger.debug("SQL executed: {} - {}ms", methodName, duration); }}
@Override
public Object plugin(Object target) {return Plugin.wrap(target, this);}
@Override
public void setProperties(Properties properties) {// 从配置中获取参数}
}
6.2 分页插件实现
java
public class PageInterceptor implements Interceptor {private static final String PAGE_PARAM = "page";
private Dialect dialect;@Override
public Object intercept(Invocation invocation) throws Throwable {Object[] args = invocation.getArgs(); MappedStatement ms = (MappedStatement) args[0]; Object parameter = args[1]; // 检查是否需要进行分页处理 Page page = getPageParameter(parameter); if (page == null) { return invocation.proceed(); } // 执行分页查询 return executePagedQuery(invocation, ms, parameter, page);}
private Page getPageParameter(Object parameter) {
if (parameter instanceof Map) { return (Page) ((Map<?, ?>) parameter).get(PAGE_PARAM); } return null;}
private Object executePagedQuery(Invocation invocation,
MappedStatement ms, Object parameter, Page page) throws Throwable { // 获取原始SQL BoundSql boundSql = ms.getBoundSql(parameter); String originalSql = boundSql.getSql(); // 生成分页SQL String pagedSql = dialect.getLimitString(originalSql, page.getOffset(), page.getPageSize()); // 创建新的MappedStatement MappedStatement newMs = createNewMappedStatement(ms, pagedSql); invocation.getArgs()[0] = newMs; // 执行分页查询 List<?> result = (List<?>) invocation.proceed(); page.setData(result); // 查询总记录数 if (page.isNeedTotal()) { int total = queryTotalCount(ms, parameter); page.setTotal(total); } return result;}
private int queryTotalCount(MappedStatement ms, Object parameter) throws SQLException {
// 执行COUNT查询获取总记录数 return 0;}
}与Spring框架集成
7.1 Spring Boot 集成配置
yamlapplication.yml
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.example.model
configuration:
map-underscore-to-camel-case: true
cache-enabled: true
lazy-loading-enabled: true
default-executor-type: reuse
java
@Configuration
@MapperScan("com.example.mapper")
public class MyBatisSpringConfig {@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean(); factoryBean.setDataSource(dataSource); // 配置VFS,解决Spring Boot打包后资源加载问题 factoryBean.setVfs(SpringBootVFS.class); // 配置类型处理器 factoryBean.setTypeHandlers(new ExampleTypeHandler()); return factoryBean.getObject();}
@Bean
public DataSourceTransactionManager transactionManager(DataSource dataSource) {return new DataSourceTransactionManager(dataSource);}
}
7.2 事务管理配置
java
@Service
@Transactional
public class UserService {private final UserMapper userMapper;
private final DepartmentMapper departmentMapper;@Transactional(rollbackFor = Exception.class)
public void createUserWithDepartment(User user, Department department) {departmentMapper.insert(department); user.setDepartmentId(department.getId()); userMapper.insert(user); // 业务逻辑...}
@Transactional(readOnly = true)
public User getUserWithDepartment(Long userId) {User user = userMapper.selectById(userId); if (user != null && user.getDepartmentId() != null) { Department department = departmentMapper.selectById(user.getDepartmentId()); user.setDepartment(department); } return user;}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void auditUserOperation(User user) {// 在新的独立事务中执行审计操作 auditMapper.logOperation(user);}
}- 最佳实践与性能优化
8.1 SQL 优化策略
xml
SELECT
u.user_id,
u.user_name,
u.email,
d.dept_name,
COUNT(o.order_id) as order_count
FROM users u
LEFT JOIN departments d ON u.dept_id = d.dept_id
LEFT JOIN orders o ON u.user_id = o.user_id
u.status = 'ACTIVE'
HAVING order_count >= #{minOrderCount}
ORDER BY u.create_time DESC
LIMIT #{limit}
SELECT /+ INDEX(users idx_user_status) / *
FROM users
WHERE status = #{status}
8.2 批量操作优化
java
public class BatchOperationService {
private final SqlSessionTemplate sqlSessionTemplate;
@Autowired
public BatchOperationService(SqlSessionTemplate sqlSessionTemplate) {
this.sqlSessionTemplate = sqlSessionTemplate;
}
public void batchInsertUsers(List<User> users) {
SqlSession sqlSession = sqlSessionTemplate.getSqlSessionFactory()
.openSession(ExecutorType.BATCH, false);
try {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
for (int i = 0; i < users.size(); i++) {
mapper.insert(users.get(i));
if (i % 1000 == 0 || i == users.size() - 1) {
sqlSession.commit();
sqlSession.clearCache();
}
}
} finally {
sqlSession.close();
}
}
public void parallelBatchProcess(List<User> users) {
int batchSize = 1000;
List<CompletableFuture<Void>> futures = new ArrayList<>();
for (int i = 0; i < users.size(); i += batchSize) {
List<User> batch = users.subList(i, Math.min(i + batchSize, users.size()));
futures.add(CompletableFuture.runAsync(() -> processBatch(batch)));
}
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
}
private void processBatch(List<User> batch) {
try (SqlSession sqlSession = sqlSessionTemplate.getSqlSessionFactory()
.openSession(ExecutorType.BATCH, false)) {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
for (User user : batch) {
mapper.update(user);
}
sqlSession.commit();
}
}
}
- 总结
MyBatis 作为一个强大而灵活的持久层框架,在 SQL 控制和对象映射之间取得了很好的平衡。通过其丰富的功能和可扩展的架构,MyBatis 能够满足从简单CRUD操作到复杂企业级应用的各种数据访问需求。
在实际开发中,开发者应该充分利用 MyBatis 的动态SQL能力、缓存机制和插件系统,同时注意SQL性能优化和资源管理。与 Spring 框架的深度集成使得 MyBatis 在现代Java应用中仍然保持着重要的地位。
随着微服务和云原生架构的普及,MyBatis 也在不断演进,与新的技术栈和部署模式保持兼容。掌握 MyBatis 不仅能够提升数据访问层的开发效率,更能为构建高性能、可维护的应用程序奠定坚实基础。