int类型参数无法保存形参会变成arg0代替
秒杀接口暴露可以防止提前记录接口后由插件程序自动填写参数执行秒杀
update语句的行级锁说明
有多個參數的時候,要告訴 Mybatis 哪個位置的參數必須叫什麼名字
補上 @Param
投影片~~~~~~~~~~~~~
DAO接口實現類 mapper
有個技巧,如果出現重複,這 insert into 會報錯
我們可以忽略錯誤 insert ignore into ...
有兩種實現
1.分兩個SQL來完成
2. 一個SQL完成
mybatis-config.xml
SeckKillDao.xml
投影片~~~~~~~
防止用戶重複秒殺,基於這唯一性可以為我們過濾
success_killed 的 primary key
show tables;
show create table seckill\G => 可以看這表示如何創建的 (DDL)
=========
-- 数据库初始化脚本 -- 创建数据库 CREATE DATABASE seckill; -- 使用数据库 USE seckill; -- 创建秒杀库存表 CREATE TABLE seckill ( `seckill_id` bigint NOT NULL AUTO_INCREMENT COMMENT '商品库存id', `name` varchar(120) NOT NULL COMMENT '商品名称', `number` int NOT NULL COMMENT '库存数量', `start_time` timestamp NOT NULL COMMENT '秒杀开启时间', `end_time` timestamp NOT NULL COMMENT '秒杀结束时间', `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (seckill_id), KEY idx_start_time (start_time), KEY idx_end_time (end_time), KEY idx_create_time (create_time) ) ENGINE = InnoDB AUTO_INCREMENT = 1000 DEFAULT CHARSET=utf8 COMMENT="秒杀库存表"; -- 初始化数据 INSERT INTO seckill (name, number, start_time, end_time) VALUES ('100元秒杀iPhone6', 100, '2020-03-28 00:00:00', '2020-03-29 00:00:00'), ('100元秒杀iPhone7', 200, '2020-03-28 00:00:00', '2020-03-29 00:00:00'), ('100元秒杀iPhone8', 300, '2020-03-28 00:00:00', '2020-03-29 00:00:00'), ('100元秒杀iPhoneX', 400, '2020-03-28 00:00:00', '2020-03-29 00:00:00'), ('100元秒杀iPhoneXS', 500, '2020-03-28 00:00:00', '2020-03-29 00:00:00'); -- 秒杀成功明细表 CREATE TABLE success_killed ( `seckill_id` bigint NOT NULL COMMENT '秒杀商品ID', `user_phone` bigint NOT NULL COMMENT '用户手机号', `state` tinyint NOT NULL DEFAULT -1 COMMENT '状态标志:-1:无效 0:成功 1:已付款', `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (seckill_id, user_phone), KEY idx_create_time (create_time) ) ENGINE = InnoDB DEFAULT CHARSET=utf8 COMMENT='秒杀成功明细表'; -- 连接数据库控制台 mysql -uroot -p77777777 -- 为什么手写DDL -- 记录每次的上线DDL -- 上线V1.1 ALTER TABLE seckill; DROP INDEX idx_start_time; -- 上线V1.2 INSERT INTO seckill (name, number, start_time, end_time) VALUES ('100元秒杀ViVO', 100, '2020-03-31 00:00:00', '2020-04-01 00:00:00'), ('100元秒杀ViVO', 100, '2020-04-01 00:00:00', '2020-04-02 00:00:00'), ('100元秒杀OPPO', 200, '2020-04-02 00:00:00', '2020-04-03 00:00:00');
https://github.com/yoyo185644/seckill/tree/master
投影片~~~~~~~~~~
這麼複雜?
我們只實現秒殺相關功能。
投影片~~~~~~~
競爭是發生在 Update 扣庫存數
投影片~~~~~
為什麼需要事務
maven 命令創建 web 骨架項目
web.xml 要修改成 3.1
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> </web-app>
把沒有的目錄補全
ex: src/main/java、src/test/java、src/test/resources
## SecKillDao.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="cn.dangkei.dao.SecKillDao"> <!--目的:为dao接口方法提供sql语句配置 即针对dao接口中的方法编写我们的sql语句--> <update id="reduceNumber"> update seckill set number = number -1 where seckill_id = #{seckillId} AND start_time <![CDATA[ <=]]> #{killTime} AND end_time >=#{killTime} AND number > 0 </update> <select id="queryById" resultType="SecKill" parameterType="long"> select * from seckill where seckill_id = #{seckillId} </select> <select id="queryAll" resultType="SecKill"> select * from seckill ORDER BY create_time DESC limit #{offset},#{limit} </select> </mapper>
## SuccessKilledMapper.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="cn.dangkei.dao.SuccessKillDao"> <insert id="insertSuccessKilled"> <!--当出现主键冲突(即重复秒杀),报错,如果不想让程序报错,加入ignore--> Insert ignore INTO success_killed(seckill_id,user_phone,state,) VALUES(#{seckillId},#{userPhone},0) </insert> <select id="queryByIdWithSeckill" resultType="cn.dangkei.entity.SuccessKilled"> <!--根据seckillId查询SuceessKilled对象,并携带Seckill对象--> <!--如何告诉mybatis把结果映射到SuccessKill属性同时映射到secKill属性--> <!--可以自由控制SQL语句--> SELECT sk.seckill_id, sk.user_phone, sk.create_time, sk.state, s.seckill_id "seckill.seckill_id", s.name "seckill.name", s.number "seckill.number", s.start_time "seckill.start_time", s.end_time "seckill.end_time", s.create_time "seckill.create_time" FROM success_killed sk INNER JOIN seckill s ON sk.seckill_id=s.seckill_id WHERE sk.seckill_id=#{seckillId} and sk.user_phone=#{userPhone} </select> </mapper>
mvn创建的最后一个参数表示使用mvn的原型
webapp的原型来创建项目
用Maven命令创建web项目
MyBatis和HIBERNATE都是用来ORM映射的
MyBatis的特点:参数 + SQL = Entity/List (提供参数和SQL语句)
两种方式提供SQL:1、XML提供SQL (推荐) 2、注解提供SQL
如何DAO接口?
1、Mapper自动实现DAO接口 (推荐)
2、API编程方式实现DAO接口
减库存和记录购买明细构成一个事务,事务机制依然是目前最可靠的落地方案
NoSQL对于事务的支持不是很好
目录结构
mapper写法
配置全局属性:
使用jdbc的getGeneratedKeys 获取数据库自增主键
使用列别名替代列名,自动转换为entity里的属性 useColumnLabel
开启驼峰命名转换 mapUnderscoreCamelCase
ctrl+shift+t 生成测试类
spring官网文档:
https://docs.spring.io/spring-framework/docs/
更少的配置--别名
https://mybatis.org/mybatis-3/zh/getting-started.html
天猫秒杀库存系统
秒杀的难点是如何高效的处理竞争
行级锁::
dao具体SQL实现