分享一個整合SSM框架的高併發和商品秒殺專案

Java團長_發表於2018-12-11

一個整合SSM框架的高併發和商品秒殺專案,學習目前較流行的Java框架組合實現高併發秒殺API

專案開發流程

本專案很適合學習一些技術的基礎,這個專案的開發分為幾個流程,很基礎地教你接觸到一個相對有技術含量的專案

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

  2. Java高併發秒殺API之web層

  3. Java高併發秒殺API之Service層

  4. Java高併發秒殺API之高併發優化


按照上面幾個流程走下去,你要有基本的Maven認識以及Java語法的一些概念,要不然可能不太理解

其實這幾個流程也就是開發的流程,首先從DAO層開始開發,從後往前開發,開始Coding吧!

專案總結可能比較長,由於公眾號文章的字數限制今天只能先講解第一節,如果這篇文章看得人多並且點贊數較高的話明天就繼續更新,哈哈~

專案環境的搭建

  • 作業系統 : Ubuntu 17.04

  • IDE :IntelliJ IDEA 2016.2.5 x64 用Eclipse也一樣的,工具時靠人用的

  • JDK : JDK1.8 建議使用JDK1.7以上版本,有許多語法糖用著挺舒服的

  • Web容器 : Tomcat 8.0

  • 資料庫 :Mysql-5.6.17-WinX64 實驗性的專案用Mysql就足夠啦

  • 依賴管理工具 : Maven 管理jar包真的很方便

    這裡列出的環境不是必須的,你喜歡用什麼就用什麼,這裡只是給出參考,不過不同的版本可能會引起各種不同的問題就需要我們自己去發現以及排查,在這裡使用Maven的話時方便我們管理JAR包,我們不用跑去各種開源框架的官網去下載一個又一個的JAR包,配置好了Maven後新增pom檔案座標就會從中央倉庫下載JAR包,如果哪天替換版本也很方便


專案效果圖

秒殺商品列表


640

秒殺結束提示介面


640

開始秒殺提示介面


640

重複秒殺提示介面


640


秒殺成功提示介面


640

專案的執行

下載

Download Zip或者 git clone

git clone https://github.com/Sunybyjava/seckill.git

匯入到IDE

這裡因為是使用IDEA建立的專案,所以使用IDEA直接開啟是很方便的,提前是你要配置好maven的相關配置,以及專案JDK版本,JDK版本必須在1.8以上,因為在專案中使用了Java8LocalDateTime以及LocalDate,所以低於這個版本編譯會失敗的

  • IDEA
    直接在主介面選擇Open,然後找到專案所在路徑,點選pom.xml開啟就可以了

  • Eclipse 這個專案是基於IDEA建立,我這裡把專案轉成了Eclipse的專案,如果你使用Eclipse的話也可以直接匯入,只是步驟更繁瑣一點,Eclipse匯入步驟

(一)Java高併發秒殺APi之業務分析與DAO層程式碼編寫

構建專案的基本骨架

  • 首先我們要搭建出一個符合Maven約定的目錄來,這裡大致有兩種方式,第一種:

  1. 第一種使用命令列手動構建一個maven結構的目錄,當然我基本不會這樣構建


mvn archetype:generate -DgroupId=com.suny.seckill -DartifactId=seckill -Dpackage=com.suny.seckill -Dversion=1.0-SNAPSHOT -DarchetypeArtifactId=maven-archetype-webapp


這裡要注意的是使用archetype:generate進行建立,在Maven老版本中是使用archetype:create,現在這種方法已經被棄用了,所以使用命令列建立的話注意了,稍微解釋下這段語句的意思,就是構建一個一個maven-archetype-webapp骨架的Webapp專案,然後groupIdcom.suny.seckill ,artifactIdseckill,這裡是Maven相關知識,可以按照自己的情況進行修改

2.第二種直接在IDE中進行建立,這裡以IDEA為例

  • 點選左上角File>New>Project>Maven

  • 然後在裡面勾選Create from archetype,然後再往下拉找到org.apache.cocoon:cocoon-22-archetype-webapp,選中它,注意要先勾選那個選項,否則選擇不了,然後點選Next繼續


640


+然後就填寫你的Maven的那幾個重要的座標了,自己看著填吧


640


+再就配置你的Maven的相關資訊,預設應該是配置好的


640


+之後就是點Finsh,到此不出意外的話就應該建立成功了

構建pom檔案

專案基本的骨架我們就建立出來了,接下來我們要新增一些基本的JAR包的依賴,也就是在pom.xml中新增各種開源元件的三座標了

<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>com.suny.seckill</groupId>
       <artifactId>seckill</artifactId>
       <version>1.0-SNAPSHOT</version>
       <name>seckill Maven Webapp</name>
       <url>http://maven.apache.org</url>
 <dependencies>

     <!--junit測試-->
     <dependency>
         <groupId>junit</groupId>
         <artifactId>junit</artifactId>
         <version>4.12</version>
         <scope>test</scope>
     </dependency>

     <!--配置日誌相關,日誌門面使用slf4j,日誌的具體實現由logback實現-->
     <dependency>
         <groupId>ch.qos.logback</groupId>
         <artifactId>logback-classic</artifactId>
         <version>1.1.7</version>
     </dependency>
     <dependency>
         <groupId>org.slf4j</groupId>
         <artifactId>slf4j-api</artifactId>
         <version>1.7.21</version>
     </dependency>
     <dependency>
         <groupId>org.apache.logging.log4j</groupId>
         <artifactId>log4j-core</artifactId>
         <version>2.6.1</version>
     </dependency>

     <!--資料庫相關依賴-->
     <!--首先匯入連線Mysql資料連線-->
     <dependency>
         <groupId>mysql</groupId>
         <artifactId>mysql-connector-java</artifactId>
         <version>5.1.39</version>
     </dependency>

     <!--匯入資料庫連線池-->
     <dependency>
         <groupId>c3p0</groupId>
         <artifactId>c3p0</artifactId>
         <version>0.9.1.2</version>
     </dependency>

     <!--匯入mybatis依賴-->
     <dependency>
         <groupId>org.mybatis</groupId>
         <artifactId>mybatis</artifactId>
         <version>3.4.2</version>
     </dependency>
     <dependency>
         <groupId>org.mybatis</groupId>
         <artifactId>mybatis-spring</artifactId>
         <version>1.3.1</version>
     </dependency>

     <!--匯入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>
     <!--spring預設的json轉換-->
     <dependency>
         <groupId>com.fasterxml.jackson.core</groupId>
         <artifactId>jackson-databind</artifactId>
         <version>2.8.5</version>
     </dependency>
     <dependency>
         <groupId>javax.servlet</groupId>
         <artifactId>javax.servlet-api</artifactId>
         <version>3.1.0</version>
     </dependency>
     
     <!--匯入spring相關依賴-->
     <dependency>
         <groupId>org.springframework</groupId>
         <artifactId>spring-core</artifactId>
         <version>4.3.6.RELEASE</version>
     </dependency>
     <dependency>
         <groupId>org.springframework</groupId>
         <artifactId>spring-beans</artifactId>
         <version>4.3.6.RELEASE</version>
     </dependency>
     <dependency>
         <groupId>org.springframework</groupId>
         <artifactId>spring-context</artifactId>
         <version>4.3.6.RELEASE</version>
     </dependency>
     <dependency>
         <groupId>org.springframework</groupId>
         <artifactId>spring-jdbc</artifactId>
         <version>4.3.7.RELEASE</version>
     </dependency>
     <dependency>
         <groupId>org.springframework</groupId>
         <artifactId>spring-tx</artifactId>
         <version>4.3.6.RELEASE</version>
     </dependency>
     <dependency>
         <groupId>org.springframework</groupId>
         <artifactId>spring-web</artifactId>
         <version>4.3.6.RELEASE</version>
     </dependency>
     <dependency>
         <groupId>org.springframework</groupId>
         <artifactId>spring-webmvc</artifactId>
         <version>4.3.7.RELEASE</version>
     </dependency>
     
     <!--匯入springTest-->
     <dependency>
         <groupId>org.springframework</groupId>
         <artifactId>spring-test</artifactId>
         <version>4.2.7.RELEASE</version>
     </dependency>
 </dependencies>
 <build>
     <finalName>seckill</finalName>
 </build>
</project>


建立資料庫


在根目錄下有一個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 DEFAULT CURRENT_TIMESTAMP() COMMENT '秒殺開啟的時間',
 `end_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP() 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-5-22 00:00:00','2016-5-23 00:00:00'),
 ('500元秒殺iPad2',200,'2016-5-22 00:00:00','2016-5-23 00:00:00'),
 ('300元秒殺小米4',300,'2016-5-22 00:00:00','2016-5-23 00:00:00'),
 ('200元秒殺紅米note',400,'2016-5-22 00:00:00','2016-5-23 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 COMMENT '建立時間',
 PRIMARY KEY (seckill_id,user_phone), /*聯合主鍵*/
 KEY idx_create_time(create_time)
)ENGINE =InnDB DEFAULT CHARSET =utf8 COMMENT ='秒殺成功明細表'


在建立資料庫的,如果按照我這裡的資料庫指令碼建立的話應該是沒問題的,但是我按照視訊裡面的資料庫指令碼建表的話發生了一個錯誤


640


這個報錯看起來比較的詭異,我仔細檢查sql也沒有錯誤,它總提示我end_time要有一個預設的值,可我記得我以前就不會這樣,然後視訊裡面也沒有執行錯誤,然後我感覺可能時MySQL版本的差異,我檢視了下我資料庫版本,在登入Mysql控制檯後輸入指令,在控制檯的我暫時知道的有兩種方式:


select version();  
select @@version;


我的輸出結果如下: 


640


進控制檯就已經可以看到版本了,我的Mysql是5.7的,以前我用的時5.6的,然後去Google上搜尋了下,找到了幾個答案,參考連結:

  • https://stackoverflow.com/questions/9192027/invalid-default-value-for-create-date-timestamp-field

  • mysql官方的解釋:https://dev.mysql.com/doc/refman/5.7/en/sql-mode.html#sqlmode_no_zero_date

  • https://stackoverflow.com/questions/34570611/mysql-community-5-7-invalid-default-value-datetime-field-type


總結出來一句話就是:


mysql 5.7中,預設使用的是嚴格模式,這裡的日期必須要有時間,所以一定要給出預設值,要麼就修改資料庫設定

然後網友評論裡總結出來的幾種解決辦法,未經測試!:

  • 下次有問題一定要先看一下評論!!!create不了的同學,可以這樣寫:


`start_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '秒殺開始時間',
  `end_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '秒殺結束時間',
  `create_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '建立時間',


  • 關於timestamp的問題,需要先執行 set explicit_defaults_for_timestamp = 1,否則會報invalid default value錯誤

  • 還需要注意的是SQL版本的問題會導致視訊中seckill表建立會出錯。只要將create_time放在start_time和end_time之前是方便的解決方法。


對比下我修改過後的跟視訊裡面的sql片段: 


640


我們可以看到在這三個欄位有一個小差別,那就是給start_time,end_time,create_time三個欄位都新增一個預設值,然後執行資料庫語句就沒問題了


這裡我們需要修改下web.xml中的servlet版本為3.0

開啟WEB-INF下的web.xml,修改為以下程式碼:

<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-->


修改的原因有以下幾點:

  • 高版本的Servlet支援更多的特性,更方便我們的Coding,特別是支援註解這一特性

  • Servlet2.3中新加入了Listener介面的實現,,我們可以使用Listener引入SpringContextLoaderListener

舉個例子:

  • Servlet2.3以前我們這樣配置ContextLoaderListener:


<servlet>
<servlet-name>context</servlet-name>
<servlet-class>org.springframework.context.ContextLoaderServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>


  • Servlet2.3以後可以使用Listener配置,也就是我們專案中使用的方法


<listener>
<listener-class>org.springframework.context.ContextLoaderListener</listener-class>
</listener>


兩種方法的效果都是一樣的,主要不要同時使用,否則會報錯的

建立實體類

  • 首先建立SuccessKilled 秒殺狀態表


package com.suny.entity;

import java.io.Serializable;
import java.time.LocalDateTime;


public class SuccessKilled implements Serializable {
   private static final long serialVersionUID = 1834437127882846202L;

   private long seckillId;
   /* 使用者的手機號碼*/
   private long userPhone;
   /* 秒殺的狀態*/
   private short state;
   /* 建立時間*/
   private LocalDateTime createTime;
   /* 多對一,因為一件商品在庫存中肯定有許多,對應的購買資訊也有很多*/
   private Seckill seckill;

   public SuccessKilled() {
   }

   public SuccessKilled(long seckillId, long userPhone, short state, LocalDateTime createTime, Seckill seckill) {
       this.seckillId = seckillId;
       this.userPhone = userPhone;
       this.state = state;
       this.createTime = createTime;
       this.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 LocalDateTime getCreateTime() {
       return createTime;
   }

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

   public Seckill getSeckill() {
       return seckill;
   }

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

   @Override
   public String toString() {
       return "SuccessKilled{" +
               "主鍵ID=" + seckillId +
               ", 手機號碼=" + userPhone +
               ", 秒殺狀態=" + state +
               ", 建立時間=" + createTime +
               ", 秒殺的商品=" + seckill +
               '}';
   }
}


  • 再建立Seckill 秒殺商品資訊


package com.suny.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;
   /* 秒殺的商品編號 */
   private int number;
   /* 開始秒殺的時間 */
   private LocalDateTime startTime;
   /* 結束秒殺的時間 */
   private LocalDateTime endTime;
   /* 建立的時間 */
   private LocalDateTime createTIme;

   public Seckill() {
   }

   public Seckill(long seckillId, String name, int number, LocalDateTime startTime, LocalDateTime endTime, LocalDateTime createTIme) {
       this.seckillId = seckillId;
       this.name = name;
       this.number = number;
       this.startTime = startTime;
       this.endTime = endTime;
       this.createTIme = 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 LocalDateTime getStartTime() {
       return startTime;
   }

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

   public LocalDateTime getEndTime() {
       return endTime;
   }

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

   public LocalDateTime getCreateTIme() {
       return createTIme;
   }

   public void setCreateTIme(LocalDateTime createTIme) {
       this.createTIme = createTIme;
   }

   @Override
   public String toString() {
       return "com.suny.entity.Seckill{" +
               "主鍵ID=" + seckillId +
               ", 秒殺商品='" + name + '\'' +
               ", 編號=" + number +
               ", 開始秒殺時間=" + startTime +
               ", 結束秒殺時間=" + endTime +
               ", 建立時間=" + createTIme +
               '}';
   }
}


對實體類建立對應的mapper介面,也就是dao介面類

  • 首先建立SeckillMapper,在我這裡位於com.suny.dao包下


package com.suny.dao;

import com.suny.entity.Seckill;
import org.apache.ibatis.annotations.Param;

import java.time.LocalDateTime;
import java.util.List;

public interface SeckillMapper {
  /**
   * 根據傳過來的<code>seckillId</code>去減少商品的庫存.
   *
   * @param seckillId 秒殺商品ID
   * @param killTime  秒殺的精確時間
   * @return 如果秒殺成功就返回1,否則就返回0
   */

  int reduceNumber(@Param("seckillId") long seckillId, @Param("killTime") LocalDateTime killTime);

  /**
   * 根據傳過來的<code>seckillId</code>去查詢秒殺商品的詳情.
   *
   * @param seckillId 秒殺商品ID
   * @return 對應商品ID的的資料
   */

  Seckill queryById(@Param("seckillId") long seckillId);

  /**
   * 根據一個偏移量去查詢秒殺的商品列表.
   *
   * @param offset 偏移量
   * @param limit  限制查詢的資料個數
   * @return 符合偏移量查出來的資料個數
   */

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


  • 再建立SuccessKilledMapper


package com.suny.dao;

import com.suny.entity.SuccessKilled;
import org.apache.ibatis.annotations.Param;


public interface SuccessKilledMapper {
  /**
   * 插入一條詳細的購買資訊.
   *
   * @param seckillId 秒殺商品的ID
   * @param userPhone 購買使用者的手機號碼
   * @return 成功插入就返回1, 否則就返回0
   */

  int insertSuccessKilled(@Param("seckillId") long seckillId, @Param("userPhone") long userPhone);

  /**
   * 根據秒殺商品的ID查詢<code>SuccessKilled</code>的明細資訊.
   *
   * @param seckillId 秒殺商品的ID
   * @param userPhone 購買使用者的手機號碼
   * @return 秒殺商品的明細資訊
   */

  SuccessKilled queryByIdWithSeckill(@Param("seckillId") long seckillId, @Param("userPhone") long userPhone);
}


接下來書寫xml配置檔案

建立對應的mapper.xml

首先在src/main/resources建立com.suny.dao這個包,也就是對應mapper介面檔案包一樣的包名,這樣符合Maven的約定,就是資源放置在Resource包下,Java包下則是放置java類檔案,編譯後最後還是會在同一個目錄下

640


  • 首先建立SeckillMapper.xml


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

<mapper namespace="com.suny.dao.SeckillMapper">
   <!--這裡的<=需要使用進行忽略,所以是要進行忽略,使用CDATA 區段中的文字會被解析器忽略 -->
   <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="com.suny.entity.Seckill">
       SELECT
           *
       FROM seckill AS s
       WHERE s.seckill_id = #{seckillId}
   </select>


   <select id="queryAll" resultType="com.suny.entity.Seckill">
       SELECT
           *
       FROM seckill AS s
       ORDER BY create_time DESC
       LIMIT #{offset}, #{limit}
   </select>
</mapper>


  • 建立SuccessKilledMapper.xml


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

<mapper namespace="com.suny.dao.SuccessKilledMapper">
   <!--新增主鍵衝突時忽略錯誤返回0-->  
   <insert id="insertSuccessKilled">
       INSERT IGNORE INTO success_killed (seckill_id, user_phone, state)
       VALUES (#{seckillId}, #{userPhone}, 0)
   </insert>
   <!--根據seckillId查詢SuccessKilled物件,並攜帶Seckill物件,告訴mybatis把對映結果對映到SuccessKill屬性同時對映到Seckill屬性-->  
   <select id="queryByIdWithSeckill" resultType="com.suny.entity.SuccessKilled">
       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的配置檔案mybatis-config.xml


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

<configuration>
   <!--首先配置全域性屬性-->
   <settings>
       <!--開啟自動填充主鍵功能,原理時通過jdbc的一個方法getGeneratekeys獲取自增主鍵值-->
       <setting name="useGeneratedKeys" value="true"/>
       <!--使用別名替換列名,預設就是開啟的-->
       <setting name="useColumnLabel" value="true"/>
       <!--開啟駝峰命名的轉換-->
       <setting name="mapUnderscoreToCamelCase" value="true"/>
   </settings>
</configuration>


  • 然後建立連線資料庫的配置檔案jdbc.properties,這裡的屬性要根據自己的需要去進行修改,切勿直接複製使用


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


  • 建立Springdao的配置檔案,在resources包下建立applicationContext-dao.xml


<?xml version="1.0" encoding="UTF-8"?>
<!--suppress SpringFacetInspection -->
<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"
      xmlns:tx="http://www.springframework.org/schema/tx"
      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
   http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"
>

   <context:property-placeholder location="classpath:jdbc.properties"/>

   <!--配置資料庫連線池-->
   <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
       <!--配置基本的資料庫連線-->
       <property name="driverClass" value="${jdbc.driver}"/>
       <property name="jdbcUrl" value="${jdbc.url}"/>
       <property name="user" value="${jdbc.user}"/>
       <property name="password" value="${jdbc.password}"/>
       <!--c3p0私有屬性-->
       <property name="maxPoolSize" value="30"/>
       <property name="minPoolSize" value="10"/>
       <!--關閉連線後不自動commit-->
       <property name="autoCommitOnClose" value="false"/>
       <!--獲取連線超時時間-->
       <property name="checkoutTimeout" value="1000"/>
       <!--當獲取連線失敗時的重試次數-->
   </bean>
   <!--配置sqlSessionFactory物件-->
   <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
       <!--注入資料庫連線池-->
       <property name="dataSource" ref="dataSource"/>
       <!--配置mybatis全域性配置檔案-->
       <property name="configLocation" value="mybatis-config.xml"/>
       <!--配置entity包,也就是實體類包,自動掃描,用於別名配置-->
       <property name="typeAliasesPackage" value="com.suny.entity"/>
       <!--配置需要掃描的mapper.xml檔案-->
       <property name="mapperLocations" value="classpath*:com/suny/dao/*.xml"/>
   </bean>

   <!--配置mapper介面包,動態實現mapper介面,注入到Spring容器-->
   <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
       <!--注入sqlSessionFactory,請注意不要使用sqlSessionFactoryBean,否則會出現注入異常-->
       <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
       <!--給出要掃描的mapper介面-->
       <property name="basePackage" value="com.suny.dao"/>
   </bean>

</beans>


  • 基礎的部分我們搭建完成了,然後要開始測試了 在IDEA裡面有一個快速建立測試的快捷鍵Ctrl+Shift+T,在某個要測試的類裡面按下這個快捷鍵就會出現Create new Test,然後選擇你要測試的方法跟測試的工具就可以了,這裡我們使用Junit作為測試


    建立SeckillMapperTest檔案,程式碼如下


package com.suny.dao;

import com.suny.entity.Seckill;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import javax.annotation.Resource;

import java.time.LocalDateTime;
import java.util.List;

import static org.junit.Assert.*;


@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:spring/applicationContext-dao.xml"})
public class SeckillMapperTest {
  @Resource
  private SeckillMapper seckillMapper;

  @Test
  public void reduceNumber() throws Exception {
      long seckillId=1000;
      LocalDateTime localDateTime=LocalDateTime.now();
      int i = seckillMapper.reduceNumber(seckillId, localDateTime);
      System.out.println(i);
  }

  @Test
  public void queryById() throws Exception {
      long seckillId = 1000;
      Seckill seckill = seckillMapper.queryById(seckillId);
      System.out.println(seckill.toString());
  }

  @Test
  public void queryAll() throws Exception {
      List<Seckill> seckills = seckillMapper.queryAll(0, 100);
      for (Seckill seckill : seckills) {
          System.out.println(seckill.toString());
      }
  }

}


測試中可能會出現Mybatis引數繫結失敗的錯誤,在mapper介面中的方法裡面新增@Param的註解,顯示的告訴mybatis引數的名稱是什麼,例如


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




PS:如果覺得我的分享不錯,歡迎大家隨手點贊、轉發。

(未完待續)

640

Java團長

專注於Java乾貨分享

640

掃描上方二維碼獲取更多Java乾貨

相關文章