作者:nnngu
項(xiàng)目源代碼:https://github.com/nnngu/nguSeckill
這是一個(gè)整合IDEA+Maven+SSM框架的高并發(fā)的商品秒殺項(xiàng)目画髓。我們將分為以下幾篇文章來進(jìn)行詳細(xì)的講解:
- 01 Java高并發(fā)秒殺項(xiàng)目之業(yè)務(wù)分析與DAO層
- 02 Java高并發(fā)秒殺項(xiàng)目之Service層
- 03 Java高并發(fā)秒殺項(xiàng)目之web層
- 04 Java高并發(fā)秒殺項(xiàng)目之高并發(fā)優(yōu)化
項(xiàng)目的效果圖
秒殺商品列表

開始秒殺提示界面

秒殺結(jié)束提示界面

新建一個(gè)Maven項(xiàng)目
以IntelliJ IDEA為例净宵,點(diǎn)擊File > New > Project > Maven

然后點(diǎn)擊Next繼續(xù)竣稽;

填寫相關(guān)信息,點(diǎn)擊Next律秃;

最后點(diǎn)擊Finish,完成創(chuàng)建船惨。
如果右下角彈出下面這個(gè)提示柜裸,點(diǎn)擊Enable Auto-Import

創(chuàng)建webapp目錄
點(diǎn)擊File > Project Structure
步驟1

步驟2

步驟3

步驟4

步驟5

步驟6

步驟7

步驟8

構(gòu)建pom文件
Maven項(xiàng)目創(chuàng)建好了疙挺,接下來我們要添加一些jar包的依賴扛邑,也就是在pom.xml
中添加各種開源組件的坐標(biāo)。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.nnngu</groupId>
<artifactId>nguSeckill</artifactId>
<version>1.0-SNAPSHOT</version>
<!-- 代碼省略铐然,請(qǐng)參照項(xiàng)目的源代碼 -->
... ...
完整的代碼蔬崩,請(qǐng)查看項(xiàng)目里的pom.xml
文件
到此,我們項(xiàng)目的初始化工作完成搀暑。
秒殺系統(tǒng)業(yè)務(wù)分析
秒殺系統(tǒng)業(yè)務(wù)流程如下:

由圖可以發(fā)現(xiàn)沥阳,整個(gè)系統(tǒng)其實(shí)是針對(duì)庫存做的系統(tǒng)。用戶成功秒殺商品自点,對(duì)于我們系統(tǒng)的操作就是:
減庫存
記錄用戶的購買明細(xì)桐罕。下面看看我們用戶對(duì)庫存的業(yè)務(wù)分析:

記錄用戶的秒殺成功信息,我們需要記錄:1桂敛、誰購買成功了功炮。2、購買成功的時(shí)間/有效期术唬。3死宣、付款/發(fā)貨信息。這些數(shù)據(jù)組成了用戶的秒殺成功信息碴开,也就是用戶的購買行為毅该。
為什么我們的系統(tǒng)需要事務(wù)?看如下這些故障:1潦牛、若是用戶成功秒殺商品我們記錄了其購買明細(xì)卻沒有減庫存眶掌。導(dǎo)致商品的超賣。2巴碗、減了庫存卻沒有記錄用戶的購買明細(xì)朴爬。導(dǎo)致商品的少賣。對(duì)于上述兩個(gè)故障橡淆,若是沒有事務(wù)的支持召噩,損失最大的無疑是我們的用戶和商家。在MySQL中逸爵,它內(nèi)置的事務(wù)機(jī)制具滴,可以準(zhǔn)確的幫我們完成減庫存和記錄用戶購買明細(xì)的過程。
MySQL實(shí)現(xiàn)秒殺的難點(diǎn)分析:當(dāng)用戶A秒殺id為10的商品時(shí)师倔,此時(shí)MySQL需要進(jìn)行的操作是:1构韵、開啟事務(wù)。2、更新商品的庫存信息疲恢。3凶朗、添加用戶的購買明細(xì),包括用戶秒殺的商品id以及唯一標(biāo)識(shí)用戶身份的信息如電話號(hào)碼等显拳。4棚愤、提交事務(wù)。若此時(shí)有另一個(gè)用戶B也在秒殺這件id為10的商品杂数,他就需要等待宛畦,等待到用戶A成功秒殺到這件商品然后MySQL成功的提交了事務(wù)他才能拿到這個(gè)id為10的商品的鎖從而進(jìn)行秒殺,而同一時(shí)間是不可能只有用戶B在等待耍休,肯定是有很多很多的用戶都在等待拿到這個(gè)行級(jí)鎖。秒殺的難點(diǎn)就在這里货矮,如何高效的處理這些競爭羊精?如何高效的完成事務(wù)?在后面第4個(gè)模塊如何進(jìn)行高并發(fā)的優(yōu)化為大家講解囚玫。
我們這個(gè)系統(tǒng)需要完成秒殺的哪些功能喧锦?先來看看天貓的一個(gè)秒殺庫存系統(tǒng):

大家看了是不是覺得很復(fù)雜?當(dāng)然不用擔(dān)心抓督,我們只是實(shí)現(xiàn)秒殺的一些功能:1燃少、秒殺接口的暴露。2铃在、執(zhí)行秒殺的操作阵具。3、相關(guān)查詢定铜,比如說列表查詢阳液,詳情頁查詢。我們實(shí)現(xiàn)這三個(gè)功能即可揣炕。接下來進(jìn)行具體的編碼工作帘皿,首先是建立數(shù)據(jù)庫以及Dao層的編碼。
建立數(shù)據(jù)庫
-- 創(chuàng)建一個(gè)數(shù)據(jù)庫
CREATE DATABASE ngu_seckill;
-- 使用數(shù)據(jù)庫
USE ngu_seckill;
-- 省略...
... ...
完整的數(shù)據(jù)庫sql代碼畸陡,在項(xiàng)目的sql
文件夾里的ngu_seckill.sql
創(chuàng)建實(shí)體類
先創(chuàng)建秒殺商品類com/nnngu/entity/Seckill.java
package com.nnngu.entity;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* 秒殺商品
*/
public class Seckill implements Serializable {
private static final long serialVersionUID = 2912164127598660137L;
/* 主鍵ID*/
private long seckillId;
/* 秒殺商品名字 */
private String name;
/* 代碼省略鹰溜,請(qǐng)參照項(xiàng)目的源代碼 */
... ...
創(chuàng)建秒殺狀態(tài)類com/nnngu/entity/SuccessKilled.java
package com.nnngu.entity;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* 秒殺后的狀態(tài)
*/
public class SuccessKilled implements Serializable {
private static final long serialVersionUID = 1834437127882846202L;
private long seckillId;
/* 用戶的手機(jī)號(hào)碼*/
private long userPhone;
/* 代碼省略,請(qǐng)參照項(xiàng)目的源代碼 */
... ...
為實(shí)體類創(chuàng)建對(duì)應(yīng)的mapper接口丁恭,也就是dao接口
com/nnngu/dao/SeckillMapper.java
package com.nnngu.dao;
import com.nnngu.entity.Seckill;
import org.apache.ibatis.annotations.Param;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
public interface SeckillMapper {
/* 代碼省略曹动,請(qǐng)參照項(xiàng)目的源代碼 */
... ...
}
com/nnngu/dao/SuccessKilledMapper.java
package com.nnngu.dao;
import com.nnngu.entity.SuccessKilled;
import org.apache.ibatis.annotations.Param;
public interface SuccessKilledMapper {
/* 代碼省略,請(qǐng)參照項(xiàng)目的源代碼 */
... ...
}
創(chuàng)建對(duì)應(yīng)的mapper.xml
在resources
目錄下創(chuàng)建com.nnngu.dao
包牲览,然后創(chuàng)建SeckillMapper.xml
和 SuccessKilledMapper.xml
仁期,如下圖:

com.nnngu.dao/SeckillMapper.xml
<!-- 這里的代碼省略 -->
<!-- 請(qǐng)參照項(xiàng)目的源代碼 -->
com.nnngu.dao/SuccessKilledMapper.xml
<!-- 這里的代碼省略 -->
<!-- 請(qǐng)參照項(xiàng)目的源代碼 -->
創(chuàng)建Mybatis的配置文件mybatis-config.xml

mybatis-config.xml
配置文件的內(nèi)容參照項(xiàng)目的源代碼
建立連接數(shù)據(jù)庫的配置文件jdbc.properties

注意: jdbc.properties
里面的屬性要根據(jù)自己的情況進(jìn)行修改。
建立Spring的dao的配置文件
創(chuàng)建applicationContext-dao.xml
如下圖:

applicationContext-dao.xml
文件的具體代碼請(qǐng)參照項(xiàng)目的源代碼。
測試
創(chuàng)建測試類 com/nnngu/dao/SeckillMapperTest.java
如下圖:

該文件的具體代碼請(qǐng)參照項(xiàng)目的源代碼跛蛋。
測試結(jié)果
測試查詢所有商品的方法queryAll()

測試結(jié)果如下:

到此熬的,我們成功完成了Dao層開發(fā)及測試。下篇文章 02 Java高并發(fā)秒殺項(xiàng)目之Service層