2.1 四种映射语句的完整CRUD手术


文档摘要

2.1 四种映射语句:一台完整的 CRUD 手术 本节摘要:select、insert、update、delete 四种映射语句覆盖全部数据操作。本节用一套完整的用户模块把四类语句写齐,讲清语句级通用属性(parameterType、resultType、flushCache、timeout 等),弄清各语句的返回值约定,最后用"订单状态更新"病例演示返回值的实际用法。 进入第 2 章,站在手术的入口端。四种映射语句就是四种基础术式,先把它们一次写全,后续小节再往深里打。 病例背景:用户模块的完整 CRUD 沿用第 1 章的 users 表,这次把增、查、改、删四类操作全部做完。接口先行: 注意返回类型的约定:查询返回对象或对象集合;增删改返回 int,值是受影响行数。

2.1 四种映射语句:一台完整的 CRUD 手术

本节摘要:select、insert、update、delete 四种映射语句覆盖全部数据操作。本节用一套完整的用户模块把四类语句写齐,讲清语句级通用属性(parameterType、resultType、flushCache、timeout 等),弄清各语句的返回值约定,最后用"订单状态更新"病例演示返回值的实际用法。

进入第 2 章,站在手术的入口端。四种映射语句就是四种基础术式,先把它们一次写全,后续小节再往深里打。

病例背景:用户模块的完整 CRUD

沿用第 1 章的 users 表,这次把增、查、改、删四类操作全部做完。接口先行:

public interface UserMapper { User findById(int id); // 按 id 查单条 List<User> findAll(); // 查全部 int insert(User user); // 插入,返回受影响行数 int updateEmail(User user); // 更新,返回受影响行数 int deleteById(int id); // 删除,返回受影响行数 }

注意返回类型的约定:查询返回对象或对象集合;增删改返回 int,值是受影响行数。这个 int 不是主键、不是成功标志,是数据库报告的行数——后面马上会看到它的正确用法。

四条语句的完整定义

<mapper namespace="com.example.mapper.UserMapper"> <!-- 查询单条:resultType 指定结果装进哪个类 --> <select id="findById" parameterType="int" resultType="com.example.entity.User"> SELECT id, username, email FROM users WHERE id = #{id} </select> <!-- 查询全部:同一 resultType,返回 List 由接口签名决定 --> <select id="findAll" resultType="com.example.entity.User"> SELECT id, username, email FROM users ORDER BY id </select> <!-- 插入:属性值从参数对象的 getter 取 --> <insert id="insert" parameterType="com.example.entity.User"> INSERT INTO users (username, email) VALUES (#{username}, #{email}) </insert> <!-- 更新:只改邮箱,条件用主键 --> <update id="updateEmail" parameterType="com.example.entity.User"> UPDATE users SET email = #{email} WHERE id = #{id} </update> <!-- 删除:单参数直接用 --> <delete id="deleteById" parameterType="int"> DELETE FROM users WHERE id = #{id} </delete> </mapper>

调用与输出:

try (SqlSession session = factory.openSession()) { UserMapper mapper = session.getMapper(UserMapper.class); User u = mapper.findById(1); // 输出:User{id=1, username='zhangsan', email='zhangsan@example.com'} System.out.println(u); int inserted = mapper.insert(new User("lisi", "lisi@example.com")); session.commit(); // 写操作记得提交(1.4 节的教训) // 输出:1,表示一行被写入 System.out.println(inserted); List<User> all = mapper.findAll(); // 输出:2 System.out.println(all.size()); }

语句级通用属性

四种语句共享一批可选属性,都不填也有合理默认:

属性 作用 典型用法
parameterType 入参全限定名或别名 单参数可省略,框架自动推断
resultType / resultMap 结果去向(二选一) 查询必填其一,第 3 章展开
flushCache 执行前是否清缓存 查询默认 false,写语句默认 true
timeout 语句超时秒数 个别慢语句单独限时的精手段
statementType 语句处理器类型 PREPARED 默认; Callable 用于存储过程
useGeneratedKeys 回填自增主键 2.3 节展开
databaseId 多数据库方言 同一 id 写多版语句,按当前库选择

两个默认值值得多看一眼:写语句默认 flushCache 为 true,意味着任何增删改都会清掉本命名空间的一二级缓存(5.1 节把它和脏读问题连起来讲);parameterType 在多数场景可以省略,因为框架从方法签名就能推断入参类型——写了也不参与匹配,只是文档作用。

CRUD 手术的全流程

CRUD 手术的全流程

返回值的正确用法:订单状态更新病例

背景:运营要求"只允许把待支付订单改成已支付",接口需要拒绝已经支付过的重复请求。

错误做法是先查再改(两次数据库往返,且两步之间存在并发缝隙)。正确做法是让条件更新自己说话:

public interface OrderMapper { int payIfWaiting(Long orderId); }
<update id="payIfWaiting" parameterType="long"> UPDATE orders SET status = 'PAID', paid_at = NOW() WHERE id = #{orderId} AND status = 'WAITING' </update>
int rows = mapper.payIfWaiting(orderId); if (rows == 1) { // 更新成功:订单确实从待支付变成了已支付 System.out.println("支付成功"); } else { // 行数为 0:没有待支付订单被更新(已支付、已取消或不存在) System.out.println("状态不允许支付,拒绝请求"); }

解读:WHERE 条件带上状态前置校验,行数直接回答"这条规则有没有被满足"。一次往返、无并发缝隙。这就是受影响行数的正确打开方式——把业务断言压进 WHERE,而不是先查后改。

变式:乐观锁同理。给表加 version 列,UPDATE 的 WHERE 带上 version 条件,行数为 0 即说明期间有并发修改,重试或报冲突。4.2 节的 set 标签会把这类"动态字段加版本号"的更新写成完整案例。

⚠️ 常见坑:把 insert 的返回值当主键用。返回的 1 是行数,主键回填要用 useGeneratedKeys 或 selectKey(下一节展开)。另一个坑是查不到数据:selectOne 返回 null 而不是抛异常,多行时才会抛 TooManyResultsException。

💡 关键直觉:增删改是"带着 WHERE 断言的动作",行数是断言结果的回执。想让接口回答"做没做成",先问 WHERE 够不够硬。

本节要点回顾

  • 四种术式各就各位:查询管结果映射,写语句管行数回执,statementType 决定底层处理器
  • 返回值约定:查询返回对象或集合(查不到是 null),增删改返回受影响行数
  • 通用属性:flushCache 写语句默认清缓存、timeout 可单语句限流、parameterType 多可省略
  • 行数即断言:条件更新加行数判断,可替代"先查后改"这类有并发缝隙的写法
  • 语句与缓存:写语句默认清空本命名空间缓存,为 5.1 节的脏读讨论埋下伏笔

四种术式的骨架立住了,下一节钻进参数装配的核心辨析:井号占位与美元拼接,一条分界线两侧是完全不同的执行通道。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U