Java高併發秒殺API之業務分析與DAO層

根據慕課網上關於java高併發秒殺API的課程講解用maven+ssm+redis實現的一個秒殺系統html

參考了codingXiaxw's blog,很詳細:http://codingxiaxw.cn/2016/11/27/53-maven-ssm-seckill-dao/前端

 

這是第一個部分:業務分析與DAO層java

1.相關技術介紹

MySQL:1.這裏咱們採用手寫代碼建立相關表,掌握這種能力對咱們之後的項目二次上線會有很大的幫助;2.SQL技巧;3.事務和行級鎖的理解和一些應用。mysql

MyBatis:1.DAO層的設計與開發。2.MyBatis的合理使用,使用Mapper動態代理的方式進行數據庫的訪問。3.MyBatis和Spring框架的整合:如何高效的去整合MyBatis和Spring框架。git

Spring:1.Spring IOC幫咱們整合Service以及Service全部的依賴。2.聲明式事務。對Spring聲明式事務作一些分析以及它的行爲分析。github

Spring MVC:1.Restful接口設計和使用。Restful如今更多的被應用在一些互聯網公司Web層接口的應用上。2.框架運做流程。3.Spring Controller的使用技巧。web

前端:1.交互設計。2.bootstrap。3.JQuery。設計到前端的頁面代碼咱們直接拷貝。redis

高併發:1.高併發點和高併發分析。2.優化思路並實現。spring

 

2.用maven建立項目sql

工程結構以下:

 

 

main包下進行咱們項目的代碼編寫及相關配置文件,test包下進行咱們項目的測試。

打開WEB-INF下的web.xml,它默認爲咱們建立servlet版本爲2.3,須要修改它的根標籤爲:

 

<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
                      http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         version="3.0"
         metadata-complete="true">
<!--用maven建立的web-app須要修改servlet的版本爲3.0-->


</web-app>

 

 

 

 

而後在pom.xml裏面添加咱們須要的第三方jar包的座標配置信息,如SSM框架、數據庫、日誌,以下:

 

 

<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/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>cn.codingxiaxw.seckill</groupId>
  <artifactId>seckill</artifactId>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>seckill Maven Webapp</name>
  <url>http://maven.apache.org</url>
  <dependencies>
    <dependency>
      <!--3.0的junit是使用編程的方式來進行測試,而junit4是使用註解的方式來運行junit-->
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>


    <!--補全項目依賴-->
    <!--1.日誌 java日誌有:slf4j,log4j,logback,common-logging
        slf4j:是規範/接口
        日誌實現:log4j,logback,common-logging
        使用:slf4j+logback
    -->
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.7.12</version>
    </dependency>
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-core</artifactId>
      <version>1.1.1</version>
    </dependency>
    <!--實現slf4j接口並整合-->
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-classic</artifactId>
      <version>1.1.1</version>
    </dependency>


    <!--1.數據庫相關依賴-->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.35</version>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>c3p0</groupId>
      <artifactId>c3p0</artifactId>
      <version>0.9.1.1</version>
    </dependency>

    <!--2.dao框架:MyBatis依賴-->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.3.0</version>
    </dependency>
    <!--mybatis自身實現的spring整合依賴-->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>1.2.3</version>
    </dependency>

    <!--3.Servlet web相關依賴-->
    <dependency>
      <groupId>taglibs</groupId>
      <artifactId>standard</artifactId>
      <version>1.1.2</version>
    </dependency>
    <dependency>
      <groupId>jstl</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.5.4</version>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
    </dependency>

    <!--4:spring依賴-->
    <!--1)spring核心依賴-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>4.1.7.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
      <version>4.1.7.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>4.1.7.RELEASE</version>
    </dependency>
    <!--2)spring dao層依賴-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>4.1.7.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>4.1.7.RELEASE</version>
    </dependency>
    <!--3)springweb相關依賴-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>4.1.7.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>4.1.7.RELEASE</version>
    </dependency>
    <!--4)spring test相關依賴-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>4.1.7.RELEASE</version>
    </dependency>

  </dependencies>

  <build>
    <finalName>seckill</finalName>
  </build>
</project>

項目的初始化工做完成。

 

3.秒殺系統業務分析

參考

 

4.DAO層的設計和開發

首先建立數據庫,在main包下的sql包中建立數據庫初始化腳本,相關表的sql語句以下:

-- 數據庫初始化腳本

-- 建立數據庫
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
  ('1000元秒殺iphone6',100,'2016-01-01 00:00:00','2016-01-02 00:00:00'),
  ('800元秒殺ipad',200,'2016-01-01 00:00:00','2016-01-02 00:00:00'),
  ('6600元秒殺mac book pro',300,'2016-01-01 00:00:00','2016-01-02 00:00:00'),
  ('7000元秒殺iMac',400,'2016-01-01 00:00:00','2016-01-02 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:已付款 2:已發貨',
  `create_time` TIMESTAMP NOT NULL COMMENT '建立時間',
  PRIMARY KEY(seckill_id,user_phone),/*聯合主鍵*/
  KEY idx_create_time(create_time)
)ENGINE=INNODB DEFAULT CHARSET=utf8 COMMENT='秒殺成功明細表';

  -- SHOW CREATE TABLE seckill;#顯示錶的建立信息

而後建立對應表的實體類,在java包下建立org.myseckill.entity包,建立一個Seckill.java實體類,用於存放秒殺商品的相關信息,代碼以下:

public class Seckill
{
    private long seckillId;
    private String name;
    private int number;
    private Date startTime;
    private Date endTime;
    private Date createTime;

    public long getSeckillId() {
        return seckillId;
    }

    public void setSeckillId(long seckillId) {
        this.seckillId = seckillId;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }

    public Date getStartTime() {
        return startTime;
    }

    public void setStartTime(Date startTime) {
        this.startTime = startTime;
    }

    public Date getEndTime() {
        return endTime;
    }

    public void setEndTime(Date endTime) {
        this.endTime = endTime;
    }

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    @Override
    public String toString() {
        return "Seckill{" +
                "seckillId=" + seckillId +
                ", name='" + name + '\'' +
                ", number=" + number +
                ", startTime=" + startTime +
                ", endTime=" + endTime +
                ", createTime=" + createTime +
                '}';
    }
}

同一個包下建立SuccessKilled.java,用於存放秒殺成功的用戶信息和相應商品信息,代碼以下:

public class SuccessKilled
{
    private long seckillId;
    private long userPhone;
    private short state;
    private Date createTime;

    //多對一,由於一件商品在庫存中有不少數量,對應的購買明細也有不少。
    private Seckill seckill;

    public long getSeckillId() {
        return seckillId;
    }

    public void setSeckillId(long seckillId) {
        this.seckillId = seckillId;
    }

    public long getUserPhone() {
        return userPhone;
    }

    public void setUserPhone(long userPhone) {
        this.userPhone = userPhone;
    }

    public short getState() {
        return state;
    }

    public void setState(short state) {
        this.state = state;
    }

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    public Seckill getSeckill() {
        return seckill;
    }

    public void setSeckill(Seckill seckill) {
        this.seckill = seckill;
    }

    @Override
    public String toString() {
        return "SuccessKilled{" +
                "seckillId=" + seckillId +
                ", userPhone=" + userPhone +
                ", state=" + state +
                ", createTime=" + createTime +
                '}';
    }
}

而後針對實體建立出對應dao層的接口,在org.myseckill.dao包下建立Seckill.java:

public interface SeckillDao
{

    /**
     * 減庫存
     * @param seckillId
     * @param killTime
     * @return 若是影響行數>1,表示更新庫存的記錄行數
     */
    int reduceNumber(long seckillId, Date killTime);

    /**
     * 根據id查詢秒殺的商品信息
     * @param seckillId
     * @return
     */
    Seckill queryById(long seckillId);

    /**
     * 根據偏移量查詢秒殺商品列表
     * @param off
     * @param limit
     * @return
     */
    List<Seckill> queryAll(int off,int limit);

}

和SuccessKilled.java:

public interface SuccessKilledDao {

    /**
     * 插入購買明細,可過濾重複
     * @param seckillId
     * @param userPhone
     * @return插入的行數
     */
    int insertSuccessKilled(long seckillId,long userPhone);


    /**
     * 根據秒殺商品的id查詢明細SuccessKilled對象(該對象攜帶了Seckill秒殺產品對象)
     * @param seckillId
     * @return
     */
    SuccessKilled queryByIdWithSeckill(long seckillId,long userPhone);
}

接下來基於MyBatis來實現咱們以前設計的Dao層接口。首先須要配置咱們的MyBatis,在resources包下建立MyBatis全局配置文件mybatis-config.xml文件,在瀏覽器中輸入http://mybatis.github.io/mybatis-3/zh/index.html打開MyBatis的官網文檔,點擊左邊的」入門」欄框,找到mybatis全局配置文件,在這裏有xml的一個規範,也就是它的一個xml約束,拷貝:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">

到咱們的項目mybatis全局配置文件中,而後在全局配置文件中加入以下配置信息:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!--配置全局屬性-->
    <settings>
        <!--使用jdbc的getGeneratekeys獲取自增主鍵值-->
        <setting name="useGeneratedKeys" value="true"/>
        <!--使用列別名替換列名  默認值爲true
        select name as title(實體中的屬性名是title) form table;
        開啓後mybatis會自動幫咱們把表中name的值賦到對應實體的title屬性中
        -->
        <setting name="useColumnLabel" value="true"/>

        <!--開啓駝峯命名轉換Table:create_time到 Entity(createTime)-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>

</configuration>

配置文件建立好後咱們須要關注的是Dao接口該如何實現,mybatis爲咱們提供了mapper動態代理開發的方式爲咱們自動實現Dao的接口。在mapper包下建立對應Dao接口的xml映射文件,裏面用於編寫咱們操做數據庫的sql語句,SeckillDao.xml和SuccessKilledDao.xml。既然又是一個xml文件,咱們確定須要它的dtd文件,在官方文檔中,點擊左側」XML配置」,在它的一些事例中,找到它的xml約束:

<!DOCTYPE mapper
    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

加入到兩個mapper映射xml文件中,而後對照Dao層方法編寫咱們的映射文件內容以下:

SeckillDao.xml:

<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.codingxiaxw.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>

SuccessKilledDao.xml:

<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.codingxiaxw.dao.SuccessKilledDao">

    <insert id="insertSuccessKilled">
        <!--當出現主鍵衝突時(即重複秒殺時),會報錯;不想讓程序報錯,加入ignore-->
        INSERT ignore INTO success_killed(seckill_id,user_phone,state)
        VALUES (#{seckillId},#{userPhone},0)
    </insert>

    <select id="queryByIdWithSeckill" resultType="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",
            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>

 

接下來咱們開始MyBatis和Spring的整合,整合目標:1.更少的編碼:只寫接口,不寫實現類。2.更少的配置:別名、配置掃描映射xml文件、dao實現。3.足夠的靈活性:自由定製SQL語句、自由傳結果集自動賦值。

在resources包下建立一個spring包,裏面放置spring對Dao、Service、transaction的配置文件。在瀏覽器中輸入http://docs.spring.io/spring/docs/進入到Spring的官網中下載其pdf官方文檔,在其官方文檔中找到它的xml的定義內容頭部:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

在spring包下建立一個spring配置dao層對象的配置文件spring-dao.xml,加入上述dtd約束,而後添加兩者整合的配置,內容以下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!--配置整合mybatis過程
    1.配置數據庫相關參數-->
    <context:property-placeholder location="classpath:jdbc.properties"/>

    <!--2.數據庫鏈接池-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!--配置鏈接池屬性-->
        <property name="driverClass" value="${driver}" />

        <!-- 基本屬性 url、user、password -->
        <property name="jdbcUrl" value="${url}" />
        <property name="user" value="${username}" />
        <property name="password" value="${password}" />

        <!--c3p0私有屬性-->
        <property name="maxPoolSize" value="30"/>
        <property name="minPoolSize" value="10"/>
        <!--關閉鏈接後不自動commit-->
        <property name="autoCommitOnClose" value="false"/>

        <!--獲取鏈接超時時間-->
        <property name="checkoutTimeout" value="1000"/>
        <!--當獲取鏈接失敗重試次數-->
        <property name="acquireRetryAttempts" value="2"/>
    </bean>

    <!--約定大於配置-->
    <!--3.配置SqlSessionFactory對象-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--往下才是mybatis和spring真正整合的配置-->
        <!--注入數據庫鏈接池-->
        <property name="dataSource" ref="dataSource"/>
        <!--配置mybatis全局配置文件:mybatis-config.xml-->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <!--掃描entity包,使用別名,多個用;隔開-->
        <property name="typeAliasesPackage" value="cn.codingxiaxw.entity"/>
        <!--掃描sql配置文件:mapper須要的xml文件-->
        <property name="mapperLocations" value="classpath:mapper/*.xml"/>
    </bean>

    <!--4:配置掃描Dao接口包,動態實現DAO接口,注入到spring容器-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!--注入SqlSessionFactory-->
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
        <!-- 給出須要掃描的Dao接口-->
        <property name="basePackage" value="cn.codingxiaxw.dao"/>
    </bean>
</beans>

在resources包下建立jdbc.properties用於配置數據庫的鏈接信息,內容以下:

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/seckill?useUnicode=true&characterEncoding=utf-8
jdbc.username=root
password=123456

這樣咱們便完成了Dao層編碼的開發,接下來就能夠利用junit進行咱們Dao層編碼的測試了。生成JUnit測試框架:在Eclipse中用右鍵點擊類SeckillDao彈出菜單,選擇「New JUnit Test Case」。

 

測試類SeckillDaoTest.java,內容以下:

/**
 * 配置spring和junit整合,這樣junit在啓動時就會加載spring容器
 */
@RunWith(SpringJUnit4ClassRunner.class)
//告訴junit spring的配置文件
@ContextConfiguration({"classpath:spring/spring-dao.xml"})
public class SeckillDaoTest {

    //注入Dao實現類依賴
     @Resource
    private SeckillDao seckillDao;
     
    @Test
    public void testQueryById() {
        long seckillId=1000;
        Seckill seckill=seckillDao.queryById(seckillId);
        System.out.println(seckill.getName());
        System.out.println(seckill);
    }

    @Test
    public void testQueryAll() {
        
        /**
         * java沒有保存形參的記錄,要加上@Param讓mybatis知道是哪一個參數,
         * queryAll(int offset,int limit)-->queryAll(arg0,arg1)
         * SeckillDao中改成queryAll(@Param("offset")int offset,@Param("limit")int limit)
         * 用來匹配SeckillDao中的limit #{offset},#{limit}
         */
        List<Seckill> seckills = seckillDao.queryAll(0, 100);
        for(Seckill seckill:seckills) {
        System.out.println(seckill);
        }
    }
    
    @Test
    public void testReduceNumber() {
        Date killTime = new Date();
        int updateCount = seckillDao.reduceNumber(1000L, killTime);
        System.out.println("updatecount=" + updateCount);
    }


}

運行該方法,程序報錯,報錯信息以下:

Caused by: org.apache.ibatis.binding.BindingException: Parameter 'offset' not found. Available parameters are [1, 0, param1, param2]

意思就是沒法完成offset參數的綁定,這也是咱們java編程語言的一個問題,也就是java沒有保存行參的記錄,java在運行的時候會把List<Seckill> queryAll(int offset,int limit);中的參數變成這樣:queryAll(int arg0,int arg1),這樣咱們就沒有辦法去傳遞多個參數。須要在SeckillDao接口中修改方法:

List<Seckill> queryAll(@Param("offset") int offset,@Param("limit") int limit);

這樣才能使咱們的MyBatis識別offset和limit兩個參數,將Dao層方法中的這兩個參數與xml映射文件中sql語句的傳入參數完成映射。

通過調試,測試成功,

testQueryById()輸出以下:

1000元秒殺iphone6
Seckill{seckillId=1000, name='1000元秒殺iphone6', number=100, startTime=Mon Jan 01 00:00:00 CST 2018, endTime=Tue Jan 02 00:00:00 CST 2018, createTime=Fri Dec 29 23:04:08 CST 2017}

 

testQueryAll()輸出以下:

Seckill{seckillId=1000, name='1000元秒殺iphone6', number=100, startTime=Mon Jan 01 00:00:00 CST 2018, endTime=Tue Jan 02 00:00:00 CST 2018, createTime=Fri Dec 29 23:04:08 CST 2017}
Seckill{seckillId=1001, name='800元秒殺ipad', number=200, startTime=Mon Jan 01 00:00:00 CST 2018, endTime=Tue Jan 02 00:00:00 CST 2018, createTime=Fri Dec 29 23:04:08 CST 2017}
Seckill{seckillId=1002, name='6600元秒殺mac book pro', number=300, startTime=Mon Jan 01 00:00:00 CST 2018, endTime=Tue Jan 02 00:00:00 CST 2018, createTime=Fri Dec 29 23:04:08 CST 2017}
Seckill{seckillId=1003, name='7000元秒殺iMac', number=400, startTime=Mon Jan 01 00:00:00 CST 2018, endTime=Tue Jan 02 00:00:00 CST 2018, createTime=Fri Dec 29 23:04:08 CST 2017}

 

testReduceNumber()輸出以下 :

updatecount=0

修改數據的數目爲0,,該庫存表的商品數量沒有減小,是由於咱們當前時間沒有達到秒殺商品要求的時間,因此不會成功秒殺。

 

接下來進行SuccessKilledDao接口相關方法的測試

 

@RunWith(SpringJUnit4ClassRunner.class)
//告訴junit spring的配置文件
@ContextConfiguration({"classpath:spring/spring-dao.xml"})
public class SuccessKilledDaoTest {

  @Resource
  private SuccessKilledDao successKilledDao;

  @Test
  public void insertSuccessKilled() throws Exception     {

      long seckillId=1000;
      long userPhone=13476191877L;
      int insertCount=successKilledDao.insertSuccessKilled(seckillId,userPhone);
      System.out.println("insertCount="+insertCount);
  }
  
  @Test
  public void queryByIdWithSeckill() throws Exception     {
      long seckillId=1000L;
      long userPhone=13476191877L;
      SuccessKilled successKilled=successKilledDao.queryByIdWithSeckill(seckillId,userPhone);
      System.out.println(successKilled);
      System.out.println(successKilled.getSeckill());


  }
}

通過調試測試成功

insertSuccessKilled()輸出以下:

insertCount=1

即咱們修改了表中的一條記錄,這時查看秒殺成功明細表,發現該用戶的信息已經被插入。而後再次運行該測試方法,程序沒有報主鍵異常的錯,是由於咱們在編寫咱們的明細表的時候添加了一個聯合主鍵的字段,它保證咱們明細表中的seckillId和userPhone不能重複插入,另外在SuccessDao.xml中寫的插入語句的ignore關鍵字也保證了這點。控制檯輸出再次運行的話insertCount=0,表示沒有對明細表作插入操做。

queryByIdWithSeckill()輸出以下:

SuccessKilled{seckillId=1000, userPhone=13476191877, state=0, createTime=Sat Dec 30 00:32:22 CST 2017}
Seckill{seckillId=1000, name='1000元秒殺iphone6', number=0, startTime=Mon Jan 01 00:00:00 CST 2018, endTime=Tue Jan 02 00:00:00 CST 2018, createTime=Fri Dec 29 23:04:08 CST 2017}

運行,成功查詢出咱們明細表中id爲1000且手機號碼爲13476191877的用戶信息,並將表中對應的信息映射到SuccessKilled對象和Seckill對象的屬性中。

到此,咱們成功完成了Dao層開發及測試,接下來咱們將進行Service層的開發工做

相關文章
相關標籤/搜索