跳到主要内容

5.2 Mapper API

Mapper API 用 Java 接口组织数据访问层;SQL 可以写在方法注解里,也可以放在 Mapper XML 文件中。

先选写法

你的目标推荐方式入口
SQL 较短,放在接口方法旁边最清楚方法注解方法注解
SQL 较长、动态片段多、需要集中维护Mapper 文件调用文件 Mapper
单表 CRUD、按主键操作、样本查询BaseMapperBaseMapper
条件组合复杂,但仍希望挂在 Mapper 接口下构造器调用调用构造器 API
提示

单表 CRUD 优先使用 BaseMapper。Mapper API 负责将接口方法绑定到注解 SQL、XML SQL、BaseMapper 或构造器能力。

最小示例

UserMapper.java
@SimpleMapper
@RefMapper("/mapper/userMapper.xml")
public interface UserMapper {
// 方法注解:SQL 直接写在接口方法上
@Query("select * from users where email = #{email}")
User selectByEmail(@Param("email") String email);

// Mapper 文件:SQL 写在 userMapper.xml 中,id 对应方法名
List<User> listByCondition(@Param("name") String name,
@Param("age") Integer age);
}
mapper/userMapper.xml
<mapper namespace="net.example.mapper.UserMapper">
<select id="listByCondition">
select * from users
where 1 = 1
@{and, name like concat('%', #{name}, '%')}
@{and, age = #{age}}
</select>
</mapper>
调用 Mapper
Session session = config.newSession(dataSource);
UserMapper mapper = session.createMapper(UserMapper.class);

User user = mapper.selectByEmail("alice@example.com");
List<User> users = mapper.listByCondition("alice", 18);
提示

Session 的获取方式取决于项目架构,详见 框架整合

和其它核心 API 的关系

能力在 Mapper API 中怎么体现
JdbcTemplateMapper 方法最终仍然是一次 SQL 执行,只是省去了手写模板调用。
参数传递Mapper 方法参数通过 @Param、Bean、Map 等方式绑定到 SQL。
结果接收Mapper 方法返回值决定结果如何接收,例如实体、列表、分页结果或影响行数。
Mapper 文件@RefMapper 把接口方法映射到 XML 中的 SQL 语句。
BaseMapper接口可以继承 BaseMapper 获得通用 CRUD,也可以继续声明注解或 XML 方法。
构造器 API继承 BaseMapper 后,可以在接口默认方法中调用 query()update() 等构造器。

方法注解

方法注解把 SQL 写在 Mapper 接口方法上,适合 SQL 较短、语义和方法名强绑定的场景。调用方只依赖 Java 接口,不需要直接接触 JdbcTemplateSession 的执行方法。

你要做什么注解说明
查询并返回结果@Query返回实体、集合、分页结果等。
插入数据@Insert可配合 generated keys 或 @SelectKeySql 回填主键。
更新数据@Update返回影响行数。
删除数据@Delete返回影响行数。
执行任意 SQL@Execute适合 DDL、多语句执行、多结果集等(不等同于 JDBC Batch)。
调用存储过程@Call使用 CallableStatement 调用过程或函数。
复用 SQL 片段@Segment定义可被规则引用的片段。
方法注解示例
@SimpleMapper
public interface UserMapper {
@Query("select * from users where id = #{id}")
User selectById(@Param("id") long id);

@Update("update users set name = #{name} where id = #{id}")
int updateName(@Param("id") long id, @Param("name") String name);
}

注解 SQL 支持 规则,可以写条件拼接、IN 查询、SET 片段等动态逻辑。方法参数可以使用 @Param 命名,也可以传入 Bean、Map;方法返回值决定结果接收方式。详细能力分别见 参数传递结果接收

如果 SQL 很长、动态片段很多,或者需要集中维护 resultMapentity 映射和动态 SQL 标签,请改用 Mapper 文件

Mapper 读写

BaseMapper<T> 基于 对象映射 自动生成单表 CRUD SQL,适合不想手写常规增删改查语句的场景。它通常作为 Mapper 接口的父接口使用,也可以通过 session.createBaseMapper(User.class) 直接创建。

前置条件

BaseMapper 依赖 对象映射 生成 SQL。没有实体映射时,请使用 JdbcTemplate自由 Map 模式

你要做什么推荐方法
根据主键查询、删除、更新selectByIddeleteByIdupdate
新增一条或多条数据insert
按样本对象查询listBySamplecountBySample
单表分页查询pageBySample
条件变复杂mapper.query()mapper.update() 切换到 调用构造器 API
继承 BaseMapper
@SimpleMapper
public interface UserMapper extends BaseMapper<User> {
}
常见 CRUD
UserMapper mapper = session.createMapper(UserMapper.class);

int rows = mapper.insert(user);
User loaded = mapper.selectById(1L);
int updated = mapper.update(user); // 根据主键,只更新非 null 字段
int replaced = mapper.replace(user); // 根据主键替换整行,包括 null 字段
int deleted = mapper.deleteById(1L);
样本查询和分页
User sample = new User();
sample.setStatus("ACTIVE");

List<User> users = mapper.listBySample(sample);

Page page = PageObject.of(0, 20);
PageResult<User> result = mapper.pageBySample(sample, page);
分页排序
Map<String, OrderType> orderBy = new HashMap<>();
orderBy.put("id", OrderType.DESC);

Map<String, OrderNullsStrategy> nulls = new HashMap<>();
nulls.put("name", OrderNullsStrategy.FIRST);

PageResult<User> result = mapper.pageBySample(sample, page, orderBy, nulls);

update 只更新非空字段;replace 表示整行替换;upsert 表示主键不存在时新增、存在时更新。插入后的主键回填请看 @Insert 自增主键回填

主键策略

方法注解可通过 useGeneratedKeys 回填数据库生成的主键,或用 selectKey 先取得主键。BaseMapper 使用实体上的主键生成器配置;复合主键通过多个 primary = true 的字段声明。

Session 管理

同一 Session 可以创建方法注解 Mapper、文件 Mapper 和 BaseMapper,并通过 jdbc()lambda() 使用其它 API。它们共用该 Session 的配置和数据源,不需要重复注册映射。

Session session = configuration.newSession(dataSource);
UserMapper mapper = session.createMapper(UserMapper.class);
BaseMapper<User> baseMapper = session.createBaseMapper(User.class);

由应用创建的 Session 在使用后关闭;由整合框架管理的 Session 交给框架管理生命周期。事务用法见跨 API 事务

深入阅读