一、该秒杀实现哪些功能
1.列出秒杀的商品
2.秒杀接口的暴露(到了秒杀的时间,把秒杀的地址暴露出来)
3.执行秒杀
4.相关查询(查询秒杀成功的商品等)
二、使用的技术
前端:Bootstrap,jquery,Ajax
IOC容器:Spring
Web框架:SpringMVC
ORM框架:Mybatis
日志:slf4j + logback
数据库:MySQL
项目构建工具:maven
三、代码开发的流程
1.DAO层设计编码
2.Service层设计编码
3.Web层设计编码
4.优化
四、DAO层的设计(使用Mybatis)
(1)数据表的设计
共创建了两张表,分别是秒杀的库存表seckill,秒杀成功的明细表success_killed
-- 创建秒杀库存表
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 index_start_time(`start_time`),
key index_end_time(`end_time`),
key index_create_time(`create_time`)
)ENGINE=InnoDB auto_increment=1000 default charset=utf8 comment='秒杀库存表';
-- 初始化数据
insert into seckill(name,number,start_time,end_time) values
('1000元秒杀iPhoneX',100,'2018-06-13 00:00:00','2018-06-13 01:00:00'),
('800元秒杀ipad3',100,'2018-06-13 00:00:00','2018-06-13 01:00:00'),
('500元秒杀AirPods',100,'2018-06-13 00:00:00','2018-06-13 01:00:00'),
('200元秒杀小米8',100,'2018-06-13 00:00:00','2018-06-13 01: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表示已付款,2表示已发货',
`create_time` timestamp not null comment '创建时间',
primary key(`seckill_id`,`user_phone`),
key index_create_time(`create_time`)
)ENGINE=InnoDB default charset=utf8 comment='秒杀成功的明细表';
(2)DAO实体和接口的编码
/**
* 对应数据库中秒杀的库存表
* @author liu
*/
public class Seckill {
// 库存商品的id
private long seckillId;
// 库存商品的名称
private String name;
// 库存商品的数量
private int number;
// 秒杀开始时间
private Date startTime;
// 秒杀结束时间
private Date endTime;
// 创建时间
private Date createTime;
......set方法和get方法
}
/**
* 对应数据库中秒杀成功的明细表
* @author liu
*/
public class SuccessKilled {
// 秒杀商品的id
private long seckillId;
// 秒杀人的手机号
private long userPhone;
// 秒杀状态
private short state;
// 创建时间
private Date createTime;
// 多对一
private Seckill seckill;
......set方法和get方法
}
相应的接口
public interface SeckillDao {
/**
* 秒杀成功后库存减少
* @param seckillId 商品的id
* @param killTime 秒杀的时间
* 注解@Param是参数校正
* @return 商品减少的数量
*/
int reduceNumber(@Param("seckillId") long seckillId, @Param("killTime") Date killTime);
/**
* 根据id查询商品
* @param seckillId 商品的id
* @return 对应的商品
*/
Seckill queryById(long seckillId);
/**
* 根据偏移量查询商品的列表
* @param offset
* @param limit
* @return
*/
List<Seckill> queryAll(@Param("offset") int offset, @Param("limit") int limit);
}
public interface SuccessKilledDao {
/**
* 秒杀成功后插入一条记录,seckillId和userPhone一起构成了主键,能防止同一个人秒杀多次
* @param seckillId 秒杀成功的商品id
* @param userPhone 秒杀人的手机号
* @return 插入的行数
*/
int insertSuccessKilled(@Param("seckillId") long seckillId, @Param("userPhone") long userPhone);
/**
* 根据商品的id查询SuccessKilled,SuccessKilled携带Seckill
* @param seckillId
* @param userPhone
* @return
*/
SuccessKilled queryByIdWithSeckill(@Param("seckillId") long seckillId, @Param("userPhone") long userPhone);
}
五、遇到的错误
(1)junit测试时报initializationError,除了这个没有其他任何的提示。
解决办法:首先得确保你的代码没有问题,比如方法上加了@Test,方法的修饰符为public void并且没有参数,如果代码没有问题,那就是junit jar的问题,我换成junit5后问题解决。
(2)java.lang.IllegalStateException: Failed to load ApplicationContext
紧接着测试又报这个错误,查看控制台的错误提示(只要给了错误提示,就一定有解决的办法,就怕什么提示都没有)
发现是手误把password写错了
(3)Loading class ‘com.mysql.jdbc.Driver’. This is deprecated. The new driver class is `com.mysql.cj.jdb
按照要求改好,错误消失。这个其实不改也能跑,只是会有警告。
(4)java.sql.SQLException: The server time zone value ‘�й���ʱ��’ is unrecognize…
一开始我的jdbc的url写成了下面的形式
jdbc.url=jdbc:mysql://127.0.0.1:3306/seckill?useUnicode=true&characterEncoding=utf-8
连接jdbc的url改成如下
jdbc.url=jdbc:mysql://127.0.0.1:3306/seckill?useUnicode=true&characterEncoding=utf-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
错误消失,我记得我之前一直写成第一种形式,没报过错。
上面3和4出现错误的原因是因为我使用的mysql-connector-java是 6.0.6版本,6版本以上的driver和url必须写成上面这样的形式才行。
出现错误不要紧,只要它有提示,肯定就有解决的办法,另外很多时候写的快了,就会有一些手误,这个也得慢慢排查。
(5)org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.binding.BindingException: Parameter ‘seckillId’ not found. Available parameters are [arg1, arg0, param1, param2]
接口中的方法有两个参数以上时,使用@Param注解进行参数校正
(6)错误如下图
(7)mybatis接口不能注入
6和7的原因是我建测试类的时候选错了类型
得选框框里的,不能选右边那个。。。。太丢人了。
六、总结
发现自己编码的能力太差了。。。好多小毛病,太粗心了。
可以看到我们和数据库交互时,并不像之前用jdbc一样写大量的代码,我们只需要写接口和在配置文件里写sql语句即可,大大减轻了我们的工作量,但同时配置文件也比较多,搞不好就容易出错。这是框架的好处也是它的坏处。同时我们只需要写接口,并不用给出它的实现类,这样也减少了我们维护的成本。
同时也可以看到,这里我们总共执行了5条sql语句
-
查询所有的秒杀商品
-
根据商品id查询相应的秒杀商品
-
秒杀成功后,相应的秒杀商品数量减1
-
秒杀成功后,插入一条秒杀成功的记录
-
根据商品id查询秒杀成功的记录