根据慕课网上关于java高并发秒杀API的课程讲解用maven+ssm+redis实现的一个秒杀系统html
参考了codingXiaxw's blog,很详细:http://codingxiaxw.cn/2016/11/27/53-maven-ssm-seckill-dao/前端
这是第一个部分:业务分析与DAO层java
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层的开发工做