resultType和resultMap的区别:
resultType
- 简单映射:直接返回对应的Java类型(如基本类型,实体类或Map),要求数据库字段名和属性名完全匹配(或通过查询别名匹配)
- 驼峰支持:开启驼峰映射后,Mybatis会自动将列名中的下划线去除并更改为驼峰命名形式
- 自动映射:Mybatis会自动根据列名填充对象属性,无需额外配置
- 限制:无法处理字段名与属性名不匹配的情况,不支持嵌套对象(对象内部包含其他对象)、一对多嵌套(对象内部包含其他对象的集合)等复杂关联映射
<select id="getUser" resultType="User">SELECT user_name FROM user <!-- 自动映射到 userName -->
</select>
resultMap
- 复杂映射:需要显式定义字段与属性的映射关系,支持以下功能:
- 字段名与属性名不一致:通过<result property="属性名" column="列名"/>配置
- 嵌套关系:使用<association property="表名" javaType="类名">处理一对一嵌套关系,<collection>处理一对多嵌套关系
- 联表查询:可映射联合查询结果到多层级嵌套实体类
<resultMap id="userWithDeptMap" type="User"><id property="id" column="user_id"/><result property="name" column="user_name"/><!-- 嵌套映射部门对象 --><association property="dept" javaType="Dept"><result property="name" column="dept_name"/></association>
</resultMap>
- 灵活性:适用于复杂业务场景,如连表查询、动态字段等。
Mybatis原生注解支持字段映射和嵌套关系:
@Results({@Result(property = "orders", column = "user_id", many = @Many(select = "selectOrdersByUserId"))
})
@Select("SELECT * FROM user WHERE id = #{id}")
User getUserWithOrders(Long id);
@Results({@Result(property = "userName", column = "user_name"), // 字段别名映射@Result(property = "age", column = "age")
})
@Select("SELECT user_name, age FROM user WHERE id = #{id}")
User getUserById(int id);
MybatisPlus基于单表的增强式注解(不支持嵌套关系):
@TableName:指定表名和实体类的映射,解决类名和表名不一致的问题
@TableField:指定字段名和属性名的映射,支持自动驼峰映射
一级缓存和二级缓存:
对比项 | SqlSession(会话) | Connection(连接) |
---|---|---|
定义 | MyBatis 的核心接口,表示一次数据库交互的上下文 | JDBC 的物理连接,直接与数据库通信 |
生命周期 | 由 SqlSessionFactory 创建,手动或框架关闭 | 从连接池获取,用完后归还(如 Druid、Hikari) |
作用范围 | 一个业务逻辑单元(如 HTTP 请求) | 单个 SQL 执行周期 |
线程安全 | 非线程安全(需每个线程独立实例) | 通常非线程安全(需避免多线程共享) |
关联关系 | 一个 SqlSession 可包含多个 Connection | 一个 Connection 仅属于一个 SqlSession |
三者协作流程
- 请求进入:业务代码通过
SqlSession
获取Mapper
代理实例(如UserMapper
)。 - SQL 执行:
Mapper
方法触发时,SqlSession
从连接池获取Connection
执行 SQL。 - 缓存与事务:
- 开启二级缓存后所有Mapper代理实例共享缓存
- 事务由
SqlSession
控制(提交/回滚后释放Connection
)。
一级缓存
- 作用域:SqlSession会话级别(同一个业务会话内有效)
- 默认开启:无需配置,自动开启
- 存储位置:SqlSession对象内部
- 缓存策略:键值存储,基于SQL+参数+分页条件作为Key缓存结果
- 生效条件:同一个SqlSession中查询完全一样的Sql语句时命中缓存
SqlSession session = sqlSessionFactory.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);User user1 = mapper.selectById(1); // 第一次查询数据库
User user2 = mapper.selectById(1); // 从一级缓存获取session.close(); // 缓存失效
- 失效条件:
- 事务执行了增删改操作(无论是否提交)
- 手动清理了session缓存或关闭session
- 事务提交或回滚
- 跨SqlSession查询
二级缓存
- 作用域:Mapper级别(跨SqlSession)
- 需手动开启:XML或注解配置
<mapper namespace="com.example.UserMapper"><cache/> <!-- 启用二级缓存 -->
</mapper>
@CacheNamespace
public interface UserMapper { ... }
- 存储位置:内存或Redis缓存中
- 缓存策略:序列化存储,需要实体类实现Serializable接口启用序列化
- 生效场景:跨SqlSession查询(多用户查询同一个Sql语句)
- 失效场景:
- 事务进行了增删改操作且提交
- 手动清理了缓存
#{}和${}的区别:
- #{}在底层使用PreparedStatement预编译为占位符,防止SQL注入
- ${}在底层使用原始Statement直接进行字符串替换
对比项 | #{} | ${} |
---|---|---|
安全性 | 高(防注入) | 低(需手动过滤) |
底层实现 | 预编译(PreparedStatement ) | 字符串替换(Statement ) |
参数类型 | 自动类型转换 | 原样输出(字符串) |
适用场景 | 动态值(WHERE/INSERT/UPDATE) | 动态表名/列名/SQL 关键 |