根据慕课网上关于java高并发秒杀API的课程讲解用maven+ssm+redis实现的一个秒杀系统

参考了codingXiaxw's blog,很详细:http://codingxiaxw.cn/2016/11/27/53-maven-ssm-seckill-dao/

这是第一个部分:业务分析与DAO层

1.相关技术介绍

MySQL:1.这里我们采用手写代码创建相关表,掌握这种能力对我们以后的项目二次上线会有很大的帮助;2.SQL技巧;3.事务和行级锁的理解和一些应用。

MyBatis:1.DAO层的设计与开发。2.MyBatis的合理使用,使用Mapper动态代理的方式进行数据库的访问。3.MyBatis和Spring框架的整合:如何高效的去整合MyBatis和Spring框架。

Spring:1.Spring IOC帮我们整合Service以及Service所有的依赖。2.声明式事务。对Spring声明式事务做一些分析以及它的行为分析。

Spring MVC:1.Restful接口设计和使用。Restful现在更多的被应用在一些互联网公司Web层接口的应用上。2.框架运作流程。3.Spring Controller的使用技巧。

前端:1.交互设计。2.bootstrap。3.JQuery。设计到前端的页面代码我们直接拷贝。

高并发:1.高并发点和高并发分析。2.优化思路并实现。

2.用maven创建项目

工程结构如下:

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层的开发工作

最新文章

  1. C 调用redis缓冲
  2. Eclipse中Ctrl+方法名发现无法进入到该方法中……
  3. javascript继承(三)—继承的实现原理
  4. Linux下原子性操作,类似Windows下的InterLockedXXX
  5. Linux命令行修改IP、网关、DNS的方法
  6. CentOS 7 下使用 Firewall
  7. SQL学习中(一)序列
  8. Here are some of my ideas .
  9. 通过crash了解linux页表
  10. ie启动不了的解决办法,win7,win8都可以
  11. java中“==”号的运用
  12. python 面向对象的程序设计
  13. python_汉塔诺
  14. bash: pip: command not found... 解决方法
  15. yum安装提示Another app is currently holding the yum lock; waiting for it to exit...
  16. linux操作命令 开发人员需要掌握的一些命令
  17. js 二维数组 for 循环重新赋值
  18. PPPoE图解
  19. Common tasks that you can perform with the Groovy Script test step
  20. 微软BI 之SSAS 系列 - 多维数据集中度量值设计时的聚合函数 (累加性_半累加性和非累加性)

热门文章

  1. SPHINX 文档写作工具安装简要指南 - windows 版 - 基于python
  2. require.js基本用法
  3. power designer 一般常用快捷键(转)
  4. 【BZOJ5291】[BJOI2018]链上二次求和(线段树)
  5. 添加 [DataContract] 到 Entity Framework 6.0 POCO Template
  6. [NOI2010]海拔(最小割)
  7. dom4j解析xml时取消DTD验证
  8. 使用 Spring Cloud Stream 构建消息驱动微服务
  9. ArcGIS for qml - 地址地标转换为经纬度(地理编码)
  10. 【CSS 技能提升】 :before和:after的使用