类型 | 描述 |
---|---|
POOLED |
使用连接池(默认) |
UNPOOLED |
不使用连接池(适合简单场景) |
JNDI |
通过JNDI获取外部连接池 |
配置示例:
<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"/>
dataSource>
动态拼接查询条件,避免手动拼接SQL字符串。
<select id="findByWhere" resultType="User">
SELECT * FROM user
<where>
<if test="username != null">AND username LIKE #{username}if>
<if test="sex != null">AND sex = #{sex}if>
where>
select>
自动处理WHERE
后的AND/OR
冗余,替代WHERE 1=1
。
<where>
<if test="username != null">username LIKE #{username}if>
where>
遍历集合生成条件,支持IN
或OR
拼接。
<foreach collection="ids" item="i" open="id IN (" separator="," close=")">
#{i}
foreach>
<foreach collection="ids" item="i" open="id=" separator=" OR id=">
#{i}
foreach>
提取公共SQL片段,减少重复代码。
<sql id="baseSelect">SELECT * FROM usersql>
<select id="findAll">
<include refid="baseSelect"/>
select>
需求:查询账户信息并关联用户信息。
JavaBean:
public class Account {
private Integer id;
private Double money;
private User user; // 关联用户
}
XML配置:
<resultMap id="accountMap" type="Account">
<id property="id" column="id"/>
<association property="user" javaType="User">
<result property="username" column="username"/>
association>
resultMap>
<select id="findAll" resultMap="accountMap">
SELECT a.*, u.username FROM account a JOIN user u ON a.uid = u.id
select>
需求:查询用户及其所有账户。
JavaBean:
public class User {
private List<Account> accounts; // 用户拥有多个账户
}
XML配置:
<resultMap id="userMap" type="User">
<collection property="accounts" ofType="Account">
<result property="money" column="money"/>
collection>
resultMap>
<select id="findOneToMany" resultMap="userMap">
SELECT u.*, a.money FROM user u LEFT JOIN account a ON u.id = a.uid
select>
延迟加载,也叫懒加载,简单来说就是在真正需要数据的时候才去加载数据,而不是在一开始就把所有关联数据都加载出来。这可以有效减少资源的浪费,尤其是在处理复杂对象关系时,避免一次性加载大量无用数据。
开启全局延迟加载:
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
settings>
一对一延迟加载示例:
假设有 User
和 Address
两个实体,一个用户对应一个地址。在传统的查询方式中,可能会一次性将用户及其地址信息都查询出来。但在延迟加载模式下,查询用户时并不会立即查询地址信息。
然后在 User
的映射文件中配置:
<resultMap id="userMap" type="User">
<id column="user_id" property="id"/>
<result column="username" property="username"/>
<association property="address"
select="cn.tx.mapper.AddressMapper.findAddressByUserId"
column="user_id"
fetchType="lazy"/>
resultMap>
这样,当查询用户时,只有在访问 user.getAddress()
时才会去查询地址信息。
多对一延迟加载示例:
比如多个订单对应一个用户,在查询订单时,用户信息可以延迟加载。在 Order
的映射文件中配置:
<resultMap id="orderMap" type="Order">
<id column="order_id" property="id"/>
<result column="order_no" property="orderNo"/>
<association property="user"
select="cn.tx.mapper.UserMapper.findUserByOrderId"
column="user_id"
fetchType="lazy"/>
resultMap>
当访问 order.getUser()
时才会加载用户信息。
一对多延迟加载示例:
在 User
的映射文件中配置如下:
<resultMap id="userMap" type="User">
<id column="user_id" property="id"/>
<result column="username" property="username"/>
<collection property="orders"
select="cn.tx.mapper.OrderMapper.findOrdersByUserId"
column="user_id"
fetchType="lazy"/>
resultMap>
只有在调用 user.getOrders()
时,才会执行查询订单的SQL语句。
多对多延迟加载示例:
假设 User
和 Role
是多对多关系,需要通过中间表 user_role
来关联。
首先在 User
的映射文件中配置:
<resultMap id="userMap" type="User">
<id column="user_id" property="id"/>
<result column="username" property="username"/>
<collection property="roles"
select="cn.tx.mapper.RoleMapper.findRolesByUserId"
column="user_id"
fetchType="lazy"/>
resultMap>
在 Role
的映射文件中也做类似配置:
<resultMap id="roleMap" type="Role">
<id column="role_id" property="id"/>
<result column="role_name" property="roleName"/>
<collection property="users"
select="cn.tx.mapper.UserMapper.findUsersByRoleId"
column="role_id"
fetchType="lazy"/>
resultMap>
这样在查询用户或角色时,相关联的多对多关系数据只有在实际访问时才会加载。
1. 定义与特性
SqlSession
级别,默认开启。SqlSession
绑定,Session 关闭或执行 commit()
/rollback()
时清空。SqlSession session = sqlSessionFactory.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User user1 = mapper.findById(1); // 第一次查询,从数据库获取数据并放入一级缓存
User user2 = mapper.findById(1); // 第二次查询,从一级缓存中获取数据
2. 缓存命中与失效
SQL
+ 相同的参数 + 相同的环境。INSERT/UPDATE/DELETE
操作。sqlSession.clearCache()
。SqlSession
的查询不会共享缓存。3. 实战注意点
SqlSession
,一级缓存不共享。1. 定义与特性
Mapper
级别(跨 SqlSession
),需手动开启。clear()
时清空。Serializable
)。2. 缓存策略与配置
<settings>
<setting name="cacheEnabled" value="true"/>
settings>
<mapper namespace="...">
<cache eviction="LRU" flushInterval="60000" size="512"/>
mapper>
eviction
):
LRU
:最近最少使用(默认)。FIFO
:先进先出。SOFT
:软引用,基于垃圾回收器状态回收。3. 工作流程
SqlSession
关闭时,一级缓存数据同步到二级缓存。维度 | 一级缓存 | 二级缓存 |
---|---|---|
作用范围 | SqlSession 内 |
跨 SqlSession (同一 Mapper) |
默认状态 | 开启 | 需手动配置 |
存储位置 | 内存(JVM 堆) | 可配置为磁盘或第三方缓存(如 Redis) |
数据共享 | 不共享 | 多个 Session 共享 |
生命周期 | 随 Session 销毁 | 长期存在,需主动管理 |
1. 常见问题
2. 优化建议
flushInterval
)。@CacheNamespace
和 @CacheNamespaceRef
精细化管理。掌握这些核心技能,可以高效利用MyBatis构建健壮的数据库应用。实际开发中,需根据业务场景选择合适策略,平衡性能与功能需求。