一、MyBatis基本概念
1.1 什么是MyBatis?
MyBatis是一款优秀的半自动化ORM(对象关系映射)框架,它消除了几乎所有的JDBC代码和参数的手动设置,以及结果集的检索。MyBatis使用简单的XML或注解来配置和映射原生类型、接口和Java POJOs(Plain Old Java Objects)为数据库中的记录。
核心特点:
SQL与代码分离:SQL语句存储在XML文件或注解中,与Java代码解耦
灵活的结果映射:支持将复杂的查询结果映射到对象图
动态SQL:提供强大的动态SQL功能来应对复杂查询需求
轻量级:框架本身小巧且简单,没有第三方依赖
1.2 MyBatis的发展历程
起源:最初是Apache的一个开源项目iBatis,2010年迁移到Google Code后改名为MyBatis
版本演进:
MyBatis 3.x(当前主流版本):全面支持注解配置,改进API设计
MyBatis-Spring:与Spring框架深度集成
MyBatis-Plus:国内团队开发的增强工具包
二、MyBatis核心架构解析
2.1 体系结构组成
基础层:
SqlSessionFactoryBuilder:根据配置构建SqlSessionFactory
SqlSessionFactory:创建SqlSession实例的工厂
SqlSession:包含执行SQL命令的所有方法
数据处理层:
Executor:SQL执行器,负责动态SQL生成和缓存维护
StatementHandler:处理JDBC Statement操作
ParameterHandler:将Java对象转换为JDBC参数
ResultSetHandler:将JDBC结果集转换为Java对象
支撑层:
Configuration:所有MyBatis配置信息的容器
MappedStatement:存储映射语句信息
TypeHandler:Java类型与JDBC类型转换处理器
2.2 核心配置文件
mybatis-config.xml示例:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
<typeAliases>
<typeAlias alias="User" type="com.example.model.User"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test"/>
<property name="username" value="root"/>
<property name="password" value="password"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/example/mapper/UserMapper.xml"/>
</mappers>
</configuration>
三、MyBatis的核心优势
3.1 相比JDBC的优势
比较维度 | 原生JDBC | MyBatis |
---|---|---|
代码量 | 需要大量重复代码 | 减少约60%的代码量 |
SQL管理 | 混杂在Java代码中 | 集中管理,支持动态SQL |
结果映射 | 手动处理ResultSet | 自动对象映射 |
事务控制 | 手动管理 | 声明式事务管理 |
性能优化 | 需要手动实现 | 内置缓存机制 |
3.2 相比Hibernate的优势
特性 | Hibernate | MyBatis |
---|---|---|
SQL控制 | 自动生成,难优化 | 完全掌控,可手写优化 |
学习曲线 | 较陡峭 | 相对平缓 |
灵活性 | 面向对象,复杂关联处理强 | 面向SQL,复杂查询更灵活 |
性能 | 自动优化可能不理想 | 可精细控制SQL,更易优化 |
适用场景 | 标准CRUD | 复杂查询、报表系统 |
3.3 具体优势体现
精确的SQL控制:
<!-- 可编写精确优化的SQL -->
<select id="selectUserWithRole" resultMap="userRoleMap">
SELECT u.*, r.role_name
FROM users u
LEFT JOIN user_roles ur ON u.id = ur.user_id
LEFT JOIN roles r ON ur.role_id = r.id
WHERE u.status = #{status}
ORDER BY u.create_time DESC
LIMIT #{offset}, #{limit}
</select>
动态SQL能力:
<select id="findActiveUsers" resultType="User">
SELECT * FROM users
<where>
<if test="name != null">
AND name like #{name}
</if>
<if test="email != null">
AND email = #{email}
</if>
<choose>
<when test="role != null">
AND role = #{role}
</when>
<otherwise>
AND role = 'USER'
</otherwise>
</choose>
</where>
</select>
灵活的结果映射:
<resultMap id="detailedUserMap" type="User">
<id property="id" column="user_id"/>
<result property="name" column="user_name"/>
<collection property="roles" ofType="Role">
<id property="id" column="role_id"/>
<result property="name" column="role_name"/>
</collection>
</resultMap>
四、MyBatis的高级特性
4.1 插件机制(Interceptor)
自定义插件示例:
@Intercepts({
@Signature(type= Executor.class,
method="query",
args={
MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class QueryMonitorPlugin implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
long start = System.currentTimeMillis();
try {
return invocation.proceed();
} finally {
long time = System.currentTimeMillis() - start;
MappedStatement ms = (MappedStatement) invocation.getArgs()[0];
System.out.println("执行SQL: " + ms.getId() + " 耗时: " + time + "ms");
}
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
}
注册插件:
<plugins>
<plugin interceptor="com.example.plugin.QueryMonitorPlugin">
<property name="threshold" value="500"/>
</plugin>
</plugins>
4.2 缓存机制
二级缓存配置:
<cache eviction="LRU"
flushInterval="60000"
size="512"
readOnly="true"/>
缓存策略对比:
策略 | 描述 | 适用场景 |
---|---|---|
LRU | 最近最少使用 | 通用场景 |
FIFO | 先进先出 | 顺序访问模式 |
SOFT | 软引用,内存不足时回收 | 大数据量缓存 |
WEAK | 弱引用,GC时回收 | 临时缓存 |
4.3 类型处理器(TypeHandler)
自定义类型处理器:
public class JsonTypeHandler extends BaseTypeHandler<Map<String, Object>> {
private final ObjectMapper objectMapper = new ObjectMapper();
@Override
public void setNonNullParameter(PreparedStatement ps, int i,
Map<String, Object> parameter,
JdbcType jdbcType) throws SQLException {
ps.setString(i, objectMapper.writeValueAsString(parameter));
}
@Override
public Map<String, Object> getNullableResult(ResultSet rs, String columnName)
throws SQLException {
return parseJson(rs.getString(columnName));
}
// 其他方法实现...
}
注册类型处理器:
<typeHandlers>
<typeHandler handler="com.example.handler.JsonTypeHandler"
javaType="java.util.Map"
jdbcType="VARCHAR"/>
</typeHandlers>
五、MyBatis与Spring集成
5.1 Spring Boot配置
application.yml:
mybatis:
mapper-locations: classpath:mapper/**/*.xml
type-aliases-package: com.example.model
configuration:
map-underscore-to-camel-case: true
default-fetch-size: 100
default-statement-timeout: 30
主类配置:
@MapperScan("com.example.mapper")
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
5.2 事务管理
声明式事务:
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
@Transactional
public void createUserWithProfile(User user, Profile profile) {
userMapper.insert(user);
profile.setUserId(user.getId());
userMapper.insertProfile(profile);
}
}
六、MyBatis最佳实践
6.1 SQL编写规范
XML格式化:
<!-- 良好的格式 -->
<select
id="selectComplexData"
parameterType="map"
resultMap="detailedResultMap">
SELECT ...
</select>
避免使用${}:
<!-- 不安全的方式 -->
ORDER BY ${columnName}
<!-- 安全的方式 -->
<choose>
<when test="orderBy == 'name'">ORDER BY name</when>
<when test="orderBy == 'date'">ORDER BY create_date</when>
<otherwise>ORDER BY id</otherwise>
</choose>
6.2 性能优化建议
批量操作:
@Insert("<script>" +
"INSERT INTO users (name, email) VALUES " +
"<foreach collection='list' item='user' separator=','>" +
"(#{user.name}, #{user.email})" +
"</foreach>" +
"</script>")
void batchInsert(@Param("list") List<User> users);
分页优化:
<select id="selectWithPage" resultType="User">
SELECT * FROM users
<where>
<include refid="queryConditions"/>
</where>
ORDER BY id
LIMIT #{offset}, #{pageSize}
</select>
6.3 项目结构组织
推荐目录结构:
src/main/java
├── com.example
│ ├── config/ # 配置类
│ ├── model/ # 实体类
│ ├── mapper/ # Mapper接口
│ ├── service/ # 业务逻辑
│ └── controller/ # 控制器
src/main/resources
├── mapper/ # XML映射文件
│ ├── UserMapper.xml
│ └── OrderMapper.xml
└── application.yml
七、MyBatis适用场景分析
7.1 理想使用场景
复杂查询系统:
报表生成
数据分析
多表关联查询
遗留系统改造:
已有复杂SQL的系统
需要逐步替换JDBC的项目
性能敏感应用:
高并发查询
需要SQL级优化的系统
7.2 不适用场景
纯CRUD简单应用:
考虑JPA或Hibernate更高效
无DBA团队的小项目:
可能需要更多SQL知识
需要完全面向对象设计的系统:
复杂对象关系更适合Hibernate
八、MyBatis生态扩展
8.1 MyBatis-Plus
增强功能:
通用Mapper
强大的条件构造器
自动分页
逻辑删除
示例代码:
public interface UserMapper extends BaseMapper<User> {
// 自动获得CRUD方法
}
// 条件查询
QueryWrapper<User> query = new QueryWrapper<>();
query.like("name", "张").between("age", 20, 30).orderByDesc("create_time");
List<User> users = userMapper.selectList(query);
8.2 MyBatis Generator
代码生成配置:
<table tableName="users" domainObjectName="User"
enableCountByExample="false"
enableUpdateByExample="false"
enableDeleteByExample="false"
enableSelectByExample="false"
selectByPrimaryKeyQueryId="false">
<generatedKey column="id" sqlStatement="MySQL" identity="true"/>
</table>
8.3 MyBatis Dynamic SQL
动态SQL构建:
public List<AnimalData> findAnimals(String name, Date fromDate) {
SelectStatementProvider selectStatement = select(id, animalName, bodyWeight, brainWeight)
.from(animalData)
.where(animalName, isLike(name).when(Objects::nonNull))
.and(birthDate, isGreaterThanOrEqualTo(fromDate).when(Objects::nonNull))
.orderBy(id)
.build()
.render(RenderingStrategies.MYBATIS3);
return mapper.selectMany(selectStatement);
}
MyBatis作为Java持久层框架中的中流砥柱,凭借其灵活的SQL控制能力和相对简单的学习曲线,在需要精细控制SQL的企业级应用中占据重要地位。通过合理利用其特性并结合最佳实践,可以构建出既高效又易于维护的数据访问层。
暂无评论内容