秒杀项目(1)

## SpringBoot秒杀体统(一)Dao 层

秒杀系统业务流程

1、问题在于如何不超卖、使用户如何不超买。

1565170527739

1565170547522

可能出现的问题:

1565170563866

难点分析:

使用事务+ 行级锁来约束购买

事务:

数据库管理系统执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成。

通过开始事务、更新库存数量、插入购买记录、提交 来完成对一个用户的秒杀商品过程。

行级锁:

当一个用户完成购买之后,下一个用户才能继续使用这条秒杀商品记录。

1565268327019

秒杀功能实现

1565268360773

整体项目分类一览

1565267588447

Dao层编写

Dao层,也就是数据层,在ssm的项目中常用dao 这个包来存放相关数据,在springboot项目中,使用mapper来进行数据的交互。

关于如何 创建一个springboot+mybatis 项目,可以查看这个:Spring Boot + Mybatis+druid 整合

1、数据库编写

seckill 表 用来记录 需要秒杀的商品

1565267048617

sucess_killed 表 用来记录成功秒杀的记录

1565267076965

SQL 语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
DROP TABLE IF EXISTS `seckill`;
CREATE TABLE `seckill` (
   `seckill_id` int ( 11 ) NOT NULL,
   `name` varchar( 255 ) DEFAULT NULL,
   `number` int ( 11 ) DEFAULT NULL,
   `start_time` datetime DEFAULT NULL,
   `end_time` datetime DEFAULT NULL,
   `create_time` datetime DEFAULT NULL,
   PRIMARY KEY (`seckill_id`),
   KEY `idx_start_time` (`start_time`),
   KEY `idx_end_time` (`end_time`),
   KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
 
DROP TABLE IF EXISTS `success_killed`;
CREATE TABLE `success_killed` (
   `seckill_id` int ( 11 ) NOT NULL COMMENT '秒杀商品id' ,
   `user_phone` varchar( 255 ) NOT NULL,
   `state` tinyint( 4 ) DEFAULT '-1' COMMENT '-1:无效,0:成功,1:已付款' ,
   `create_time` datetime DEFAULT NULL,
   PRIMARY KEY (`seckill_id`,`user_phone`),
   KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

bean 的实体类就不贴代码了。

需要注意的是在 SuccessKilled 这个类中加一行

1
private Seckill seckill;

SeckillMapper:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
  *  根据id查看当前秒杀的商品
  * @param seckillId
  * <a href="/profile/547241" data-card-uid="547241" class="js-nc-card" target="_blank" style="color: #25bb9b">@return
  */
Seckill queryById( @Param ( "seckillId" ) Integer seckillId);
 
/**
  * 根据所给参数查询 秒杀的商品
  * @param offset
  * @param limit
  * </a><a href="/profile/547241" data-card-uid="547241" class="js-nc-card" target="_blank" style="color: #25bb9b">@return
  */
List<Seckill> queryAll( @Param ( "offset" )Integer offset , @Param ( "limit" )Integer limit);
 
/**
  *  给当前商品的库存 -1
  * @param seckillId
  * @param killTime
  * </a><a href="/profile/547241" data-card-uid="547241" class="js-nc-card" target="_blank" style="color: #25bb9b">@return
  */
int reduceNumber( @Param ( "seckillId" ) long seckillId, @Param ( "killTime" ) Date killTime);</a>

SeckillMapper.xml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<? xml version = "1.0" encoding = "UTF-8" ?>
<!DOCTYPE mapper
         PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
< mapper namespace = "com.example.demo3.mapper.SeckillMapper" >
 
     <!--    //注意修改 namespace 的包位置-->
     < select id = "queryById" resultType = "com.example.demo3.bean.Seckill" >
   select * from seckill where seckill_id = #{seckillId}
</ select >
 
     < select id = "queryAll" resultType = "com.example.demo3.bean.Seckill" >
   select * from seckill
   order by create_time desc limit #{offset},#{limit}
</ select >
 
 
     < 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 >
 
</ mapper >

SuccessKilledMapper:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
     * 插入一条秒杀记录
     * @param seckillId
     * @param userPhone
     * <a href="/profile/547241" data-card-uid="547241" class="js-nc-card" target="_blank" style="color: #25bb9b">@return
     */
    int insertSuccessKilled( @Param ( "seckillId" ) Integer seckillId, @Param ( "userPhone" ) String userPhone);
 
    /**
     * 根据seckillId查询SuccessKilled对象,并携带Seckill对象
     * @param seckillId
     * @param userPhone
     * </a><a href="/profile/547241" data-card-uid="547241" class="js-nc-card" target="_blank" style="color: #25bb9b">@return
     */
    SuccessKilled queryByIdWithSeckill( @Param ( "seckillId" ) Integer seckillId, @Param ( "userPhone" ) String userPhone);
 
</a>

SuccessKilledMapper.xml

主要是两个sql语句的编写,第一个是很多人都抢购一个商品时,容易出现重复秒杀的情况。此时使用ignore 使程序不报错。

对于第二个语句,是将 购买成功记录查询出来时 带上 秒杀商品的相关属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<? xml version = "1.0" encoding = "UTF-8" ?>
<!DOCTYPE mapper
         PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
< mapper namespace = "com.example.demo3.mapper.SuccessKilledMapper" >
 
     < insert id = "insertSuccessKilled" >
         <!--当出现主键冲突时(即重复秒杀时),会报错;不想让程序报错,加入ignore-->
         INSERT ignore INTO success_killed(seckill_id,user_phone,state)
         VALUES (#{seckillId},#{userPhone},0)
     </ insert >
 
     < select id = "queryByIdWithSeckill" resultType = "com.example.demo3.bean.SuccessKilled" >
 
         <!--根据seckillId查询SuccessKilled对象,并携带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 >
2、相关测试:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
@RunWith (SpringRunner. class )
@SpringBootTest
public class Demo3ApplicationTests {
 
     @Autowired
     EmployeeMapper employeeMapper;
 
     @Autowired
     SeckillMapper seckillMapper;
 
     @Test
     public void reduceNumber() {
         Date date = new Date();
         int up = seckillMapper.reduceNumber( 1 ,date);
         System.out.println(up);
     }
 
     @Test
     public void queryById() {
         seckillMapper.queryById( 1 );
         System.out.println( 1 );
     }
     @Test
     public void queryAll() {
         List<Seckill > seckills = seckillMapper.queryAll( 1 , 10 );
         for ( int i = 0 ; i <seckills.size() ; i++) {
             System.out.println(seckills.get(i).toString());
         }
     }
  @Test
     public void queryAll() {
         List<Seckill > seckills = seckillMapper.queryAll( 1 , 10 );
         for ( int i = 0 ; i <seckills.size() ; i++) {
             System.out.println(seckills.get(i).toString());
         }
     }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值