Java高并发秒杀Api-业务分析与DAO层构建2
章节目录
- 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层实体与接口编码
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层接口声明能显示说明很多事情
更少的配置-别名
使用spring 提供的package-scan 扫描实体包下的所有实体类。可以简写resultType
更少的配置-配置扫描
- 单独使用mybatis的场景下,配置文件扫描方式
<mapper resource="mapper/SecKillDao.xml">....
使用spring 整合 mybatis 自动扫描配置文件,采用通配符的方式。
自动实现DAO接口,DAO接口的实现类是自动注入至Spring 容器
足够的灵活性
5.DAO层接口设计与SQL编写的反思
DAO - data access object 数据访问层的简称
这一层我们主要做的是核心数据操作的接口声明,以及SQL编写,并没有涉及到Service 业务逻辑层代码的编写。这样做的好处是什么呢?
主要有以下三点
- 代码分层,DAO层只关注于核心数据操作的接口声明&SQL编写
- 代码和SQL的分离,方便代码review
- 业务逻辑层主要做事务控制、事务中完成DAO层的代码组合与拼接。每一层都可以做单元测试。
接下来详解mybatis 与 spring 整合编码的过程
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
ADM325 ECC6 的PA教材
SAP这个概念对于搞java开发的人来说是比较模糊的,它像是一个抽象概念,也像一个产品,也像一门开发语言。而且在网络上百度一些基本概念,也都是盲人摸象,给人的感觉既没有api也没有框架或者社区。作为一个很low的sap维护人员,我来向有意向进行SAP开发、维护、咨询、实施这方面工作的网友分享,SAP是什么,这些基础概念如何梳理。 首先,官方资料和官方文档和官方安装介质的问题。SAP的官方网站很容易访问,但是怎么使用它,我也不懂。官方文档,我理解就是PA教材,它是一大堆PDF文件,全英文的,建议从头读到尾。这些文档的名字都是字母+数字的组合,实际含义可以参考:上面就是我觉得最权威也是最可信的SAP教材。 然后,我想聊聊SAP世界和JAVA世界的对比和区别。做java开发的人去做SAP最开始往往会深受打击,因为SAP的世界中,你失去了java世界中那种无所不能的感觉。SAP是完全商业的产品、技术、平台、环境。也就是说虽然能百度出来一些知识,但是你想要去构建一个SAP的世界,然后用它完成你想要达成的商业或者工业目的,是不可能的。为什么这样说呢?第一、SAP这个产品不免费。第二、SAP的应用服...
- 下一篇
SQL Server CLR 使用 C# 自定义存储过程和触发器
原文: SQL Server CLR 使用 C# 自定义存储过程和触发器 这一篇博客接着上一篇博客继续介绍 SQL CLR Stored Procedure 和 CLR Trigger, 上一篇博客介绍了 SQL CLR Function 的使用,以及 CLR 程序集的注册和 CLR Function 的注册。 我的上一篇博客:SQL Server CLR 使用 C# 自定义函数 四、CLR Stored Procedure 接下来在之前的项目选择添加新项,选择 SQL CLR C# 存储过程。 public partial class StoredProcedures { /// <summary> /// 无输入参数,无输出参数,无输出结果,有输出消息,无返回值的存储过程 /// </summary> [Microsoft.SqlServer.Server.SqlProcedure(Name = "HelloWorld")] public static void HelloWorld() { SqlContext.Pipe.Send("Hello Worl...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS8安装Docker,最新的服务器搭配容器使用
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- CentOS关闭SELinux安全模块
- MySQL8.0.19开启GTID主从同步CentOS8
- SpringBoot2整合Redis,开启缓存,提高访问速度
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- SpringBoot2全家桶,快速入门学习开发网站教程
- SpringBoot2初体验,简单认识spring boot2并且搭建基础工程
- CentOS8编译安装MySQL8.0.19