Java高并发秒杀API之业务分析与DAO层

课程介绍

高并发和秒杀都是当今的热门词汇,如何使用Java框架实现高并发秒杀API是该系列课程要研究的内容。秒杀系列课程分为四门,本门课程是第一门,主要对秒杀业务进行分析设计,以及DAO层的实现。课程中使用了流行的框架组合SpringMVC+spring+MyBatis,还等什么,赶快来加入吧!

第1章 课程介绍 本章介绍秒杀系统的技术内容,以及系统演示。并介绍不同程度的学员可以学到什么内容。 第2章 梳理所有技术和搭建工程 本章首先介绍秒杀系统所用框架和技术点,然后介绍如何基于maven搭建项目,最后对工程目录包进行了划分。 第3章 秒杀业务分析 本章讲解常见秒杀业务以及如何用最常用的技术实现。分析了秒杀业务的难点,以及本课程要实现哪些秒杀API。 第4章 DAO层设计与开发 本章介绍秒杀系统数据库设计与实现,分析DAO数据持久化层所需接口,并编码实现。以及MyBatis如何与spring进行整合,最后介绍如何测试整合框架,完成DAO层的单元测试。

第1章 课程介绍

1-1 课程介绍

SpringMVC+Spring+MyBatis使用与整合 秒杀类系统需求理解和实现 常用技术解决高并发问题(Java web、前端、MySQL

为什么使用这三个框架? 互联网公司常用框架(阿里、京东、搜狐、美团…) 框架易于使用和轻量级 底代码侵入性 成熟的社区和用户群

为什么用秒杀系统来讲本课程? 秒杀业务场景具有典型“事务”特性 秒杀/红包类需求越来越常见 面试常问问题(如何设计一个秒杀系统和优化一个秒杀系统)

从本课程学到什么? 初学者:框架的使用与整合 技巧 有经验者:秒杀分析过程和优化思路

秒杀系列将分为四门课程进行,分别是: Java高并发秒杀API之业务分析与DAO层 Java高并发秒杀API之Service Java高并发秒杀API之web Java高并发秒杀API之高并发优化

1-2 项目效果演示

第2章 梳理所有技术和搭建工程

2-1 相关技术介绍

用到那些技术? MySQL(表设计 、SQL技巧、事务和行级锁) MyBatis(DAO层设计与开发、MyBatis合理使用、MyBatis与Spring整合) Spring(Spring IOC整合Service、声明式事务运用) SpringMVC(Restful接口设计和使用、框架运作流程、controller开发技巧) 前端(交互设计、Bootstrap、jQuery) 高并发(高并发点和高并发分析、优化思路并实现)

基于maven创建项目

2-2 创建项目和依赖

开始创建项目之前的说明 从零开始创建 从官网获取相关配置 使用Maven创建项目

为什么从官网获取资源 文档更全面权威 避免过时货错误 官网地址: logback配置:http://logback.qos.ch/manual/configuration.html spring配置:http://docs.spring.io/spring/docs/ mybatis配置:http://mybatis.github.io/mybatis-3/zh/index.html

开始创建项目 maven命令创建web骨架项目 mvn archetype:generate -DarchetypeCatalog=internal -DgroupId=com.seckill -DartifactId=seckill -DarchetypeArtifactId=maven-archetype-webapp 注:-DgroupId和-DartifactId标注项目的坐标,项目叫com.seckill,-DarchetypeArtifactId=maven-archetype-webapp表示使用maven的原型webapp的原型去创建项目

下面就通过IntelliJ IDEA或Eclipse开发工具Import项目

存在的问题: 1.用maven创建的webapp要修改servlet的版本: maven 创建项目的web.xml 版本比较低(Servlet2.3,jsp默认的el表达式是不工作的),所以需要把他切换到更高的Servlet版本,可以去tomcat webapps包下面去找到示例项目的web.xml的头copy过来。如果使用的IntelliJ IDEA导入的项目需要把一些没有的目录补全,如下示例(仅供参考):

打开maven的pom.xml文件把默认的junit版本改为4.11是应为3.0的junit默认使用编程的方式,4.0是使用注解的方式来运行junit。再接下来就是补全项目的依赖

第3章 秒杀业务分析

3-1 秒杀业务分析

秒杀业务的核心–> 库存的处理

为什么需要事物?

关于数据落地:MySQL VS NoSQL(事务依然是目前最可靠的落地方案,NoSQL对事物的支持做的不是很好。)

3-2 MySQL实现秒杀难点分析

对于MySQL来说竞争反应到背后的技术是怎样的呢?(事物+行级锁)

事物: Start Transaction Update库存数量 Insert购买明细 Commit

秒杀的难点是如何高效的处理竞争? 知道问题后如何解决?

3-3 实现哪些秒杀功能

我们只实现秒杀相关的功能

第4章 DAO层设计与开发

4-1 数据库设计与编码

--数据库初始化

--创建数据库
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='描述库存表';

--创建索引,用于加速查询
--key idx_start_time(start_time),
--key idx_end_time(end_time),   
--key idx_create_time(create_time)

--默认mysql有多种存储引擎,但是可以支持事物的只有InnoDB,所以在这里通过显示的语法去告诉mysql这个表的ENGINE引擎是InnoDB
--由于数据库采用的是自增作为主键,给出初始的自增id:AUTO_INCREMENT=1000
--默认编码 DEFAULT CHARSET=utf-8 
--表的注释 COMMENT='秒杀库存表'

--初始化数据
insert into 
    seckill(name,number,start_time,end_time)
value
    ('1000元秒杀iphone6',100,'2012-12-12 00:00:00','2012-12-13 00:00:00'),
    ('500元秒杀ipad2',200,'2012-12-12 00:00:00','2012-12-13 00:00:00'),
    ('300元秒杀mi4',300,'2012-12-12 00:00:00','2012-12-13 00:00:00'),
    ('200元秒杀红米note',400,'2012-12-12 00:00:00','2012-12-13 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='秒杀成功明细表';

--连接数据库控制台
mysql -uroot -p123456

--查看创建表的结构
show create table seckill\G

--为什么手写DDL
--记录每次上线的DDL修改
--上线V1.1
ALTER TABLE seckill
DROP INDEX idx_create_time,
ADD INDEX idx_c_s(start_time,create_time);


--上线V1.2
--DDL

4-2 DAO实体和接口编码

Table —-> Entity

package org.seckill.entity;

import java.util.Date;
/**
 * 秒杀库存实体
 * @author Administrator
 *
 */

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 + '}';
    }
}
package org.seckill.entity;

import java.util.Date;

/**
 * 秒杀成功明细实体
 */
public class SuccessKilled {

    /**
     * 一个秒杀seckill对应多个成功记录
     */
    private Seckill seckill;

    private long seckillId;

    private long userPhone;

    private short state;

    private Date createTime;

    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{" +
                "seckill=" + seckill +
                ", seckillId=" + seckillId +
                ", userPhone=" + userPhone +
                ", state=" + state +
                ", createTime=" + createTime +
                '}';
    }
}
  • 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
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 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
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74

DAO相关接口编码(DAO针对的是具体实体来操作的“实体的增删改查”)

package org.seckill.dao;

import java.util.Date;
import java.util.List;

import org.seckill.entity.Seckill;

public interface SeckillDao {

    /**
     * 减库存
     * @param seckillId
     * @param killTime
     * @return 如果更新行数大于1,表示更新的行数
     */
    int reduceNumber(long seckillId,Date killTime);

    /**
     * 根据id查询秒杀对象
     * @param seckillId
     * @return
     */
    Seckill queryById(long seckillId);


    /**
     * 根据偏移量查询描述列表
     * @param offet
     * @param limit
     * @return
     */
    List<Seckill> queyAll(int offet,int limit);

}
  • 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
  • 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
package org.seckill.dao;

import org.seckill.entity.SuccessKilled;

public interface SuccessKilledDao {

    /**
     * 插入购买明细(可过滤重复)
     * @param seckillId
     * @param userPhone
     * @return
     */
    int insertSuccessKilled(long seckillId,long userPhone);

    /**
     * 根据id查询SuccessKilled并携带秒杀产品对象
     * @param seckillId
     * @return
     */
    SuccessKilled queryByIdWithSeckill(long seckillId);



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

4-3 基于myBatis实现DAO理论

MyBatis用来做什么?

MyBatis怎么用 SQL写在哪?(XML提供SQL、注解提供SQL,注:注解是java5.0之后提供的一个新特性) 对于实际的使用中建议使用XML文件的方式提供SQL,通过注解的方式呢,注解本身还是java源码,修改和调整SQL其实是非常不方便的,一样需要重新编译类,当我们写复杂的SQL尤其拼接逻辑时,注解处理起来就会非常繁琐,那么XML提供了很多的SQL拼接和处理逻辑的标签可以非常方便的帮我们去做封装。 如何DAO接口接口 Mapper自动实现DAO(也就是DAO只需要设计接口,不需要去写实现类,MyBatis知道我们的参数、返回类型是什么同时也有SQL文件,他可以制动帮我们生成接口的实现类来帮我们执行参数的封装,执行SQL,把我们的返回结果集封装成我们想要的类型) 第二种是通过API编程方式实现DAO接口(MyBatis通过给我们提供了非常多的API,跟其他的ORM和JDBC很像)

在实际开发中建议使用Mapper自动实现DAO,这样可以直接只关注SQL如何编写,如何去设计DAO接口,帮我们节省了很多的维护程序,所有的实现都是MyBatis自动完成。

4-4 基于myBatis实现DAO编程(上)

打开MyBatis的官方文档 http://www.mybatis.org/mybatis-3/zh/index.html 入门——>找到MyBatis全局配置,里面有XML的规范(XML的标签约束dtd文件)拷入到项目的MyBatis全局配置文件中,开始配置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获取自增主键值,当inert一条记录时我们是不插入id的,id是通过自增去赋值的,当插入完后想得到该插入记录的id时可以调用jdbc的getGeneratekeys -->
        <setting name="useGeneratedKeys" value="true" />

        <!--使用列别名替换别名 默认true select name as title form table; -->
        <setting name="useColumnLabel" value="true" />
        <!--开启驼峰命名转换Table:create_time到 Entity(createTime) -->
        <setting name="mapUnderscoreToCamelCase" value="true" />
    </settings>
</configuration>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

使用Mapper自动实现DAO 在mapper目录中创建SeckillDao.xml和SuccessKilledDao.xml

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

<!-- namespace:指定为那个接口提供配置 --> 
<mapper namespace="org.seckill.dao.SeckillDao">
    <!-- 目的:为DAO接口方法提供sql语句配置  -->  

    <!-- int reduceNumber(long seckillId, Date killTime);-->
    <update id="reduceNumber">
        <!-- 具体sql -->
       UPDATE seckill set number = number -1
       where seckill_id = #{seckillId}
       AND  start_time <![CDATA[ <= ]]> #{killTime}
       AND  end_time >= #{killTime}
       AND number >0;
   </update>

    <!-- parameterType:参数类型,正常情况java表示一个类型的包名+类名,这直接写类名,因为后面有一个配置可以简化写包名的过程 -->
    <!--   Seckill queryById(long seckillId);-->
    <select id="queryById" resultType="Seckill" parameterType="long">
        <!-- 可以通过别名的方式列明到java名的转换,如果开启了驼峰命名法就可以不用这么写了 
        select seckill_id as seckillId
        -->

        SELECT seckill_id,name,number,start_time,end_time,create_time
        FROM seckill
        WHERE seckill_id = #{seckillId}
    </select>

    <!-- List<Seckill> queryAll(int offset,int limit);-->
    <select id="queryAll" resultType="Seckill">
        SELECT seckill_id,name,number,start_time,end_time,create_time
        FROM seckill
        ORDER BY create_time DESC
        limit #{offset},#{limit}
    </select>





</mapper>  
  • 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
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 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
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

4-5 基于myBatis实现DAO编程(下)

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.seckill.dao.SuccessKilledDao">

    <!--通过ignore关键字将主键冲突时的报错改为返回0-->
    <!--int insertSuccessKilled(long seckilledId,long userPhone);-->
    <update id="insertSuccessKilled">
       INSERT ignore INTO success_killed(seckill_id,user_phone,state)VALUES (#{seckilledId},#{userPhone},1)
    </update>

    <!-- SuccessKilled queryByIdWithSeckill(@Param("seckilledId") long seckilledId, @Param("userPhone") long userPhone);-->
    <select id="queryByIdWithSeckill" resultType="SuccessKilled">
      SELECT
      sk.seckill_id,sk.user_phone,sk.create_time,sk.state,
      s.seckill_id "seckill.seckill_id",s.name "seckill.name", 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=#{seckilledId} and sk.user_phone=#{userPhone};
    </select>

</mapper>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

注:上面的s.seckill_id “seckill.seckill_id”表示将s.seckill_id这一列的数据是seckill属性里的seckill_id属性,是一个级联的过程,使用的就是别名只是忽略了as关键字,别名要加上双引号。

4-6 myBatis整合Spring理论

整合目标 更少的编码(只写接口,不写实现,MyBatis实现DAO) 更少的配置 足够的灵活性

XML提供SQL,DAO接口Mapper来作为接口实现类时完全可以依赖于Spring去帮我们做好这些事情,当用Spring整合MyBatis时建议使用这两种方式ML提供SQL,DAO接口提供Mapper。

4-7 mybatis整合Spring编码

在resource目录下创建一个新的目录Spring(存放所有spring相关的配置)

<?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.数据库连接池-->
    <!--todo java.sql.SQLException: Access denied for user ''@'localhost' (using password: NO)-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
                    <!-- 配置连接池属性 -->
                    <property name="driverClass" value="${driverClassName}"></property>
                    <property name="jdbcUrl" value="${jdbc.url}"></property>
                    <property name="user" value="${jdbc.username}"></property>
                    <property name="password" value="${jdbc.password}"></property>
<!-- 
            <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
            <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test_mysql"></property>
            <property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/test_mysql"></property>
            <property name="user" value="root"></property>
            <property name="password" value="111111"></property>
 -->
            <!-- c3p0连接池的私有属性 -->
            <property name="maxPoolSize" value="30"></property>
            <property name="minPoolSize" value="10"></property>
            <!-- 关闭连接后不自动commit -->
            <property name="autoCommitOnClose" value="false"></property>
            <!--  获取连接超时时间 -->
            <property name="checkoutTimeout" value="10000"></property>
            <!--  获取连接重试次数 -->
            <property name="acquireRetryAttempts" value="3"></property>
        </bean>
<!-- 
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">

        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/test_mysql"/>
        <property name="username" value="root"/>
        <property name="password" value="111111"/>
        todo
           <property name="driverClassName" value="${driverClassName}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
    </bean>
 -->
    <!--3.配置SqlSessionFactory对象-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--注入数据库连接池-->
        <property name="dataSource" ref="dataSource"/>
        <!--配置mybatis全局配置文件:mybatis-config.xml-->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <!--扫描entity包,使用别名,多个用;隔开-->
        <property name="typeAliasesPackage" value="org.seckill.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="org.seckill.dao"/>
    </bean>

    <!--RedisDao-->
    <!-- <bean id="redisDao" class="org.seckill.dao.cache.RedisDao">
        <constructor-arg index="0" value="localhost"/>
        <constructor-arg index="1" value="6379"/>
    </bean> -->

</beans>
  • 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
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 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
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80

4-8 DAO层单元测试编码和问题排查(上)

package org.seckill.dao;

import java.util.Date;
import java.util.List;

import javax.annotation.Resource;

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

/**
 * Created by wchb7 on 16-5-8.
 */

/**
 * 配置Spring和Junit整合,junit启动时加载springIOC容器
 * spring-test,junit
 */

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring/spring-dao.xml")
public class SeckillDaoTest {


    //注入Dao实现类依赖
    @Resource
    private SeckillDao seckillDao;

    @Test
    public void testQueryById() throws Exception {
        long id = 1000;
        Seckill seckill = seckillDao.queryById(id);
        System.out.println(seckill);
    }


    @Test
    public void testQueryAll() throws Exception {
//      Java没有保存形参的记录:QueryAll(int offset,int limit)->QueryAll(arg0,arg1);
//      因为java形参的问题,多个基本类型参数的时候需要用@Param("seckillId")注解区分开来
        List<Seckill> seckills = seckillDao.queryAll(0, 4);
        for (Seckill seckill : seckills) {
            System.out.println(seckill);
        }
    }


    @Test
    public void testReduceNumber() throws Exception {
        Date killTime = new Date();
        int updateCount = seckillDao.reduceNumber(1000L, killTime);
        System.out.println("updateCount:  " + updateCount);
    }
}
  • 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
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 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
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59

4-9 DAO层单元测试编码和问题排查(下)

package org.seckill.dao;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.seckill.entity.SuccessKilled;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import javax.annotation.Resource;

import static org.junit.Assert.*;

/**
 * Created by wchb7 on 16-5-9.
 */


@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring/spring-dao.xml")
public class SuccessKilledDaoTest {

    @Resource
    private SuccessKilledDao successKilledDao;

    @Test
    public void testInsertSuccessKilled() throws Exception {
        /*
         第一次:iinsertCount=1
         第一次:iinsertCount=0
         */
        long id = 1000L;
        long phone = 15811112222L;
        int insertCount = successKilledDao.insertSuccessKilled(id, phone);
        System.out.println("insertCount: " + insertCount);
    }

    @Test
    public void testQueryByIdWithSeckill() throws Exception {

        long id = 1000L;
        long phone = 15811112222L;
        SuccessKilled successKilled = successKilledDao.queryByIdWithSeckill(id, phone);
        System.out.println(successKilled);
        if (successKilled != null) {
            System.out.println(successKilled.getSeckill());
        }
    }
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏张善友的专栏

SQL Server 2012 中的 Service Broker功能的一些改进或增强

1. 可以将消息发送到多个目标服务(多播) 通过支持多个会话句柄,扩展了 SEND (Transact-SQL) 语句的语法以启用多播。 DECLARE @di...

19870
来自专栏乐沙弥的世界

基于sqlcmd命令行工具管理SQL server

31050
来自专栏杨建荣的学习笔记

一个慢查询报警的简单处理 (r8笔记第12天)

今天在做节后的一个基本检查的时候,发现一个不太起眼的报警,报警内容为大体为: MySQL 每秒慢查询次数超过 <1>个on xxxx 查看zabbix的监控数...

37480
来自专栏DannyHoo的专栏

开发后期在各个页面中添加友盟统计

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010105969/article/details/...

31720
来自专栏杨建荣的学习笔记

分分钟搭建MySQL一主多从环境(r12笔记第31天)

之前写过一篇分分钟搭建MySQL Group Replication的测试环境,如果我们在一台服务器上想搭建一主多从的测试环境,怎么能够分分钟搞定呢,其实...

38060
来自专栏数据和云

【循序渐进Oracle】Oracle的物理备份(上)

编辑手记:备份重于一切,我们必需知道,系统总是要崩溃的,没有有效的备份只是等哪一天死!今天你备份了吗?我们一起来回顾Oracle的物理备份,本文摘自《循序渐进O...

35680
来自专栏杨建荣的学习笔记

由一条报警信息发现的一系列问题(r7笔记第67天)

今天看到一条报警短信,提示是某个表空间的问题。 ZABBIX-监控系统: ------------------------------------ 报警内容:...

37290
来自专栏Keegan小钢

App项目实战之路(五):服务端篇

近一个月因为忙于其他事情,一直没能抽出时间来更新项目进度。现在,只能趁着国庆期间,赶紧抽空更新下进度。这次,我想简单谈谈服务端的一些东西。

23060
来自专栏杨建荣的学习笔记

巧用flashback database实现灵活的数据回滚(r5笔记第16天)

之前写了一篇博文分享了使用flashback database的特性来在测试环境中避免重复导入大批量的数据,造成时间和存储空间的浪费。http://blog.i...

34530
来自专栏乐沙弥的世界

Oracle 实例恢复

Oracle实例失败多为实例非一致性关闭所致,通常称为崩溃(crash)。实例失败的结果等同于shutdown abort。

14050

扫码关注云+社区

领取腾讯云代金券