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

根据慕课网上关于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创建项目

工程结构如下:

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

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

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

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

上一篇:仅当使用了列的列表,并且 IDENTITY_INSERT 为 ON 时,才能在表中为标识列指定显式值问题


下一篇:SQL SERVER 批量生成编号