MyBatis
基础
什么是MyBatis?
答案: MyBatis是一个优秀的持久层框架,支持自定义SQL、存储过程和高级映射。
特点:
- SQL与代码分离:SQL写在XML或注解中
- 灵活:可以编写复杂SQL
- 半自动ORM:需要手动编写SQL
- 缓存机制:一级缓存、二级缓存
- 动态SQL:if、choose、foreach等标签
MyBatis和Hibernate的区别?
答案:
| 特性 | MyBatis | Hibernate |
|---|---|---|
| ORM | 半自动 | 全自动 |
| SQL | 手动编写 | 自动生成 |
| 灵活性 | 高 | 低 |
| 学习成本 | 低 | 高 |
| 性能优化 | 容易 | 困难 |
| 适用场景 | 复杂SQL、性能要求高 | 简单CRUD |
MyBatis的核心组件?
答案:
1. SqlSessionFactory
- 创建SqlSession的工厂
- 全局唯一,应用启动时创建
2. SqlSession
- 执行SQL的会话
- 线程不安全,用完即关闭
3. Mapper接口
- 定义数据库操作方法
4. Mapper XML
- 编写SQL语句
java
// 1. 创建SqlSessionFactory
SqlSessionFactory factory = new SqlSessionFactoryBuilder()
.build(Resources.getResourceAsStream("mybatis-config.xml"));
// 2. 获取SqlSession
SqlSession session = factory.openSession();
// 3. 获取Mapper
UserMapper mapper = session.getMapper(UserMapper.class);
// 4. 执行SQL
User user = mapper.selectById(1);
// 5. 关闭会话
session.close();配置
MyBatis配置文件结构?
答案:
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>
<!-- 属性 -->
<properties resource="db.properties"/>
<!-- 设置 -->
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<!-- 类型别名 -->
<typeAliases>
<typeAlias type="com.example.User" alias="User"/>
<package name="com.example.entity"/>
</typeAliases>
<!-- 类型处理器 -->
<typeHandlers>
<typeHandler handler="com.example.MyTypeHandler"/>
</typeHandlers>
<!-- 环境配置 -->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!-- Mapper映射 -->
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
<package name="com.example.mapper"/>
</mappers>
</configuration>Spring Boot整合MyBatis?
答案:
1. 添加依赖
xml
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>2. 配置数据源
yaml
spring:
datasource:
url: jdbc:mysql://localhost:3306/test
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.example.entity
configuration:
map-underscore-to-camel-case: true3. 创建Mapper接口
java
@Mapper
public interface UserMapper {
User selectById(Long id);
List<User> selectAll();
int insert(User user);
int update(User user);
int delete(Long id);
}4. 创建Mapper XML
xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.UserMapper">
<select id="selectById" resultType="User">
SELECT * FROM user WHERE id = #{id}
</select>
<select id="selectAll" resultType="User">
SELECT * FROM user
</select>
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
INSERT INTO user (name, age) VALUES (#{name}, #{age})
</insert>
<update id="update">
UPDATE user SET name = #{name}, age = #{age} WHERE id = #{id}
</update>
<delete id="delete">
DELETE FROM user WHERE id = #{id}
</delete>
</mapper>动态SQL
MyBatis的动态SQL标签?
答案:
1. if标签
xml
<select id="selectByCondition" resultType="User">
SELECT * FROM user
WHERE 1=1
<if test="name != null">
AND name = #{name}
</if>
<if test="age != null">
AND age = #{age}
</if>
</select>2. where标签(自动处理AND/OR)
xml
<select id="selectByCondition" resultType="User">
SELECT * FROM user
<where>
<if test="name != null">
AND name = #{name}
</if>
<if test="age != null">
AND age = #{age}
</if>
</where>
</select>3. set标签(自动处理逗号)
xml
<update id="update">
UPDATE user
<set>
<if test="name != null">name = #{name},</if>
<if test="age != null">age = #{age},</if>
</set>
WHERE id = #{id}
</update>4. choose/when/otherwise(类似switch)
xml
<select id="selectByCondition" resultType="User">
SELECT * FROM user
WHERE 1=1
<choose>
<when test="name != null">
AND name = #{name}
</when>
<when test="age != null">
AND age = #{age}
</when>
<otherwise>
AND status = 1
</otherwise>
</choose>
</select>5. foreach标签(批量操作)
xml
<!-- IN查询 -->
<select id="selectByIds" resultType="User">
SELECT * FROM user
WHERE id IN
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</select>
<!-- 批量插入 -->
<insert id="batchInsert">
INSERT INTO user (name, age) VALUES
<foreach collection="list" item="user" separator=",">
(#{user.name}, #{user.age})
</foreach>
</insert>6. trim标签(自定义前缀后缀)
xml
<select id="selectByCondition" resultType="User">
SELECT * FROM user
<trim prefix="WHERE" prefixOverrides="AND |OR ">
<if test="name != null">
AND name = #{name}
</if>
<if test="age != null">
AND age = #{age}
</if>
</trim>
</select>高级特性
MyBatis的缓存机制?
答案:
一级缓存(SqlSession级别)
- 默认开启
- 同一个SqlSession内有效
- 执行增删改或手动清空会失效
java
SqlSession session = factory.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User user1 = mapper.selectById(1); // 查询数据库
User user2 = mapper.selectById(1); // 从缓存获取
session.close();二级缓存(Mapper级别)
- 需要手动开启
- 同一个Mapper下的所有SqlSession共享
- 需要实体类实现Serializable
xml
<!-- 开启二级缓存 -->
<cache/>
<!-- 或配置参数 -->
<cache
eviction="LRU"
flushInterval="60000"
size="512"
readOnly="true"/>yaml
# application.yml
mybatis:
configuration:
cache-enabled: true缓存失效:
- 执行增删改操作
- 手动清空:
session.clearCache() - 配置
flushCache="true"
MyBatis的ResultMap?
答案: ResultMap用于复杂的结果映射。
基础映射:
xml
<resultMap id="userResultMap" type="User">
<id property="id" column="user_id"/>
<result property="name" column="user_name"/>
<result property="age" column="user_age"/>
</resultMap>
<select id="selectById" resultMap="userResultMap">
SELECT user_id, user_name, user_age FROM user WHERE user_id = #{id}
</select>一对一映射(association):
xml
<resultMap id="userResultMap" type="User">
<id property="id" column="id"/>
<result property="name" column="name"/>
<association property="card" javaType="Card">
<id property="id" column="card_id"/>
<result property="number" column="card_number"/>
</association>
</resultMap>
<select id="selectById" resultMap="userResultMap">
SELECT u.*, c.id as card_id, c.number as card_number
FROM user u
LEFT JOIN card c ON u.id = c.user_id
WHERE u.id = #{id}
</select>一对多映射(collection):
xml
<resultMap id="userResultMap" type="User">
<id property="id" column="id"/>
<result property="name" column="name"/>
<collection property="orders" ofType="Order">
<id property="id" column="order_id"/>
<result property="orderNo" column="order_no"/>
</collection>
</resultMap>
<select id="selectById" resultMap="userResultMap">
SELECT u.*, o.id as order_id, o.order_no
FROM user u
LEFT JOIN `order` o ON u.id = o.user_id
WHERE u.id = #{id}
</select>MyBatis的延迟加载?
答案: 延迟加载(懒加载):需要时才加载关联对象。
配置:
yaml
mybatis:
configuration:
lazy-loading-enabled: true
aggressive-lazy-loading: falsexml
<!-- 延迟加载 -->
<resultMap id="userResultMap" type="User">
<id property="id" column="id"/>
<result property="name" column="name"/>
<association property="card" javaType="Card"
select="selectCardByUserId"
column="id"
fetchType="lazy"/>
</resultMap>
<select id="selectById" resultMap="userResultMap">
SELECT * FROM user WHERE id = #{id}
</select>
<select id="selectCardByUserId" resultType="Card">
SELECT * FROM card WHERE user_id = #{userId}
</select>插件
MyBatis的插件机制?
答案: MyBatis允许拦截以下方法:
- Executor(执行器)
- ParameterHandler(参数处理)
- ResultSetHandler(结果集处理)
- StatementHandler(SQL语句处理)
自定义插件:
java
@Intercepts({
@Signature(
type = Executor.class,
method = "update",
args = {MappedStatement.class, Object.class}
)
})
public class MyPlugin implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
System.out.println("执行前");
Object result = invocation.proceed();
System.out.println("执行后");
return result;
}
}常用插件:
- PageHelper:分页插件
- MyBatis-Plus:增强工具
练习题
- #{}和${}的区别?
- MyBatis如何防止SQL注入?
- MyBatis如何实现批量操作?
- MyBatis-Plus有哪些优势?