您现在的位置是:首页 > 文章详情

Java高并发秒杀Api-业务分析与DAO层构建2

日期:2018-05-04点击:335

章节目录

  • DAO 设计编码
    • 数据库设计与编码
    • DAO层实体和接口编码
    • 基于mybatis实现DAO理论
    • 基于mybatis实现DAO接口-1
    • mybatis整合Spring
    • DAO层编码解析

Dao 设计编码

1.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>org.seckill</groupId> <artifactId>seckill</artifactId> <packaging>war</packaging> <version>1.0</version> <name>seckill Maven Webapp</name> <url>http://maven.apache.org</url> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <!--使用注解方式运行junit--> <version>4.11</version> <scope>test</scope> </dependency> <!--补全项目依赖--> <!--日志--> <!--1.日志 java日志,slf4j,log4j,logback,common-logging--> <!--2. 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.2</version> </dependency> <!--实现slf4j接口并实现,直接通过slf4j来进行日志记录--> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.1.2</version> </dependency> <!--数据库依赖--> <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.2</version> </dependency> <!--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> <!--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> <!--end java web 相关依赖--> <!--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> <!--spring dao层依赖 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>4.1.7.RELEASE</version> </dependency> <!--spring 声明式事务--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>4.1.7.RELEASE</version> </dependency> <!--spring web 相关依赖 servlet容器加载spring ioc spring aop 启动spring 的工厂--> <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> <!--junit相关依赖--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>4.1.7.RELEASE</version> </dependency> </dependencies> <build> <finalName>seckill</finalName> </build> </project> 

2.数据库设计与编码
数据库脚本如下所示:

--数据库初始化脚本 --创建数据库 CREATE DATABASE seckill; --使用数据库 use seckill; --创建秒杀库存表 CREATE TABLE seckill( `seckill_id` bigint not null AUTO_INCREMENT COMMENT '商品库存id', `name` VARCHAR (120) not null COMMENT '商品名称', `stock` 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,stock,start_time,end_time) VALUES ('1000元秒杀iphone x','100','2018-05-04 00:00:00','2018-05-05 00:00:00'), ('500元秒杀ipad x','200','2018-05-04 00:00:00','2018-05-05 00:00:00'), ('300元秒杀小米4','300','2018-05-04 00:00:00','2018-05-05 00:00:00'), ('200元秒杀小米note','400','2018-05-04 00:00:00','2018-05-05 00:00:00'); --秒杀成功明细表 CREATE TABLE success_killed( `seckill_id` bigint NOT NULL COMMENT '秒杀商品id', `user_phone` VARCHAR(11) 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=InnoDB AUTO_INCREMENT=1000 DEFAULT CHARSET=utf8 COMMENT='秒杀成功明细表'; --连接数据库的控制台 mysql -u root -p 

3.DAO层实体与接口编码

img_4d4b795c7bd0b0d2c71f8a1d9f783726.png
数据库与实体类映射

3.1MyBatis 实现DAO接口的方式

  • Mapper自动实现DAO接口
    sql直接编写,注解sql(sql更改,原代码需要重新编译),xml方式,单独更新sql,源文件不需要重新编译。
  • API 编程方式自动实现DAO 接口

3.2 基于mybatis实现DAO接口-1

  • 全局mybatis-conf.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"> <!--配置全局属性--> <configuration> <settings> <!-- changes from the defaults for testing --> <setting name="cacheEnabled" value="false" /> <!--使用jdbc 的getGeneratedKeys 获取数据库自增主键值--> <setting name="useGeneratedKeys" value="true" /> <!--使用列别名 替换列名 默认true 复制到对应的entity属性中 select name as tile from table --> <setting name="useColumnLable" value="true"/> <!--开启驼峰命名转化--> <setting name="mapUnderscoreCameCase" value="true"/> <!--REUSE 执行器会重用预处理语句--> <setting name="defaultExecutorType" value="REUSE" /> </settings> </configuration> 
  • SecKillDao.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"> <mapper namespace="org.seckill.dao.SecKillDao"> <!--目的:为DAO方法提供SQL语句配置 parameter-type不用给 #与$的区别 预编译 1.#{} 解析为一个 JDBC 预编译语句(prepared statement)的参数标记符。 会被解析为 ?占位符 动态SQL-会对SQL进行动态解析,解析为一个BoundSql对象 变量替换 2.${} 仅仅为一个纯粹的 string 替换,在动态 SQL 解析阶段将会进行变量替换 3.#{}的变量的替换是在 DBMS 中。 4.${} 在预编译之前已经被变量替换了,这会存在 sql 注入问题 5.表名是不能带''号的,所以使用${} 防止出现变量替换后表名带'' --> <update id="reduceStock"> <!--具体sql--> UPDATE seckill SET stock = stock - 1 WHERE seckill_id = #{secKillId} AND start_time <![CDATA[<=]]> #{killTime} AND end_time >= #{killTime} and stock > 0 </update> <select id="queryById" resultType="SecKill" parameterType="long"> SELECT seckill_id as seckillId,name,stock,start_time,end_time,create_time from seckill where seckill_id = #{secKillId} </select> <select id="queryAll" resultType="SecKill" parameterType="int"> SELECT seckill_id as seckillId,name,stock,start_time,end_time,create_time from seckill order by create_time desc limit #{offset},${limit} </select> </mapper> 
  • 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"> <mapper namespace="org.seckill.dao.SuccessKilledDao"> <insert id="insertSuccessKilled" > <!--如果出现重复,主键冲突,直接产生一个错误--> INSERT ignore INTO success_killed(seckill_id,user_phone) VALUES (#{secKillId},#{userPhone}) </insert> <select id="queryByIdWithSecKill" resultType="SuccessKilled" > <!--根据id查询SuccessKilled并携带SecKill实体--> <!--如何告诉mybatis 把结果映射到SuccessKilled的同时映射secKill 属性--> <!--可以自由控制SQL 优化等--> SELECT sk.seckill_id, sk.user_phone, sk.create_time, sk.state, sc.seckill_id as "secKill.seckill_id", sc.name as "secKill.name", sc.stock as "secKill.stock", sc.start_time as "secKill.start_time", sc.end_time as "sceKill.end_time", sc.create_time as "secKill.create_time" FROM success_killed sk INNER JOIN seckill sc on sk.seckill_id = sc.seckill_id WHERE sk.seckill_id = #{secKillId} </select> </mapper> 
  • entity实体层实现-SecKill、SuccessKilled
package org.seckill.domain; import java.util.Date; /** * 秒杀-库存表 entity */ public class SecKill { private long seckillId; //秒杀-商品id private String name; //秒杀-商品名字 private int stock; //秒杀-商品库存 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 getStock() { return stock; } public void setStock(int stock) { this.stock = stock; } 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 + '\'' + ", stock=" + stock + ", startTime=" + startTime + ", endTime=" + endTime + ", createTime=" + createTime + '}'; } } 
package org.seckill.domain; import java.util.Date; /** * 成功秒杀明细表-SuccessKilled */ public class SuccessKilled { private long secKillId; //秒杀-成功的商品id private String userPhone; //秒杀-成功的用户手机号 private short state; //秒杀-明细状态 private Date createTime; //秒杀-秒杀成功的时间 //one(被秒杀商品)-to-many(秒杀记录) private SecKill secKill; public long getSecKillId() { return secKillId; } public void setSecKillId(long secKillId) { this.secKillId = secKillId; } public String getUserPhone() { return userPhone; } public void setUserPhone(String 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层接口功能声明-SecKillDao、SuccessKilledDao
package org.seckill.dao; import org.seckill.domain.SecKill; import java.util.Date; import java.util.List; /** * 常用的操作 */ public interface SecKillDao { /** * 功能:减库存 * @param secKillId * @param killTime create_time? * @return 如果影响行数>1,表示更新的记录行数 */ int reduceStock(long secKillId,Date killTime); /** * 根据秒杀商品id查询秒杀商品详情 * @param secKillId * @return */ SecKill queryById(long secKillId); /** * 根据偏移量查询秒杀商品列表, * @param offset * @param limit * @return */ List<SecKill> queryAll(int offset,int limit); } 
package org.seckill.dao; import org.seckill.domain.SuccessKilled; public interface SuccessKilledDao { /** * 功能:新增用户秒杀明细 * @param secKillId * @param userPhone * @return 插入的行数,禁止插入表示插入失败 则返回0 */ int insertSuccessKilled(long secKillId,long userPhone); /** * 功能:根据明细id查询具体的秒杀明细并携带秒杀商品对象 * @param secKillId * @return */ SuccessKilled queryByIdWithSecKill(long secKillId); } 

MyBatis整合Spring

整合的目标:

  • 更少的编码
  • 更少的配置
  • 足够的灵活性
    1.mybatis优点
更少的编码、只写接口、不写实现 

2.DAO层接口声明能显示说明很多事情

img_e30135972b761f4e01a3de640426198e.png
轻量级显式接口声明

更少的配置-别名

img_e871e8d92ca50763fbc90333b9ba5d4d.png
image.png

使用spring 提供的package-scan 扫描实体包下的所有实体类。可以简写resultType

更少的配置-配置扫描

  • 单独使用mybatis的场景下,配置文件扫描方式

<mapper resource="mapper/SecKillDao.xml">....

  • 使用spring 整合 mybatis 自动扫描配置文件,采用通配符的方式。

  • 自动实现DAO接口,DAO接口的实现类是自动注入至Spring 容器

足够的灵活性

img_94b027caef0949d158bc678731ed62bc.png
image.png

5.DAO层接口设计与SQL编写的反思

DAO - data access object 数据访问层的简称
这一层我们主要做的是核心数据操作的接口声明,以及SQL编写,并没有涉及到Service 业务逻辑层代码的编写。这样做的好处是什么呢?
主要有以下三点

  • 代码分层,DAO层只关注于核心数据操作的接口声明&SQL编写
  • 代码和SQL的分离,方便代码review
  • 业务逻辑层主要做事务控制、事务中完成DAO层的代码组合与拼接。每一层都可以做单元测试。

接下来详解mybatis 与 spring 整合编码的过程

原文链接:https://yq.aliyun.com/articles/649971
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章