SpringBoot实现Java高并发秒杀系统之DAO层开发(一)

SpringBoot实现Java高并发秒杀系统之Service层开发 Service接口的设计 之前我们写好了DAO层的接口,这里我们要开始着手编写业务层接口,然后编写业务层接口的实现类并编写业务层的核心逻辑。 设计业务层接口,应该站在使用者角度上设计,如我们应该做到: 1.定义业务方法的颗粒度要细。 2.方法的参数要明确简练,不建议使用类似Map这种类型,让使用者可以封装进Map中一堆参数而传递进来,尽量精确到哪些参数。 3.方法的return返回值... 阅读详情

秒杀系统在如今电商项目中是很常见的,最近在学习电商项目时讲到了秒杀系统的实现,于是打算使用SpringBoot框架学习一下秒杀系统(本项目基于慕课网的一套免费视频教程:Java高并发秒杀API,视频教程中讲解的很详细,非常感谢这位讲师)。也是因为最近学习了SpringBoot框架(GitHub教程:SpringBoot入门之CRUD ),觉得SpringBoot框架确实比传统SSM框架方便了很多,于是更深层次练习使用SpringBoot框架,注意:SpringBoot不是对Spring功能上的增强,而是提供了一种快速使用Spring的方式。 如果你熟悉了SSM框架,学习SpringBoot框架也是很Easy的。

本项目的源码请参看:springboot-seckill 如果觉得不错可以star一下哦(#.#)

本项目一共分为四个模块来讲解,具体的开发教程请看我的博客文章:

起步

首先我们需要搭建SpringBoot项目开发环境,IDEA搭建SpringBoot项目的具体教程请看我的:博文。

如果你对SpringBoot框架或是SSM框架不熟悉,我想推荐一下我的几个小项目帮助你更好的理解:


项目设计

.
├── README  -- Doc文档
├── db  -- 数据库约束文件
├── mvnw
├── mvnw.cmd
├── pom.xml  -- 项目依赖
└── src
    ├── main
    │   ├── java
    │   │   └── cn
    │   │       └── tycoding
    │   │           ├── SpringbootSeckillApplication.java  -- SpringBoot启动器
    │   │           ├── controller  -- MVC的web层
    │   │           ├── dto  -- 统一封装的一些结果属性,和entity类似
    │   │           ├── entity  -- 实体类
    │   │           ├── enums  -- 手动定义的字典枚举参数
    │   │           ├── exception  -- 统一的异常结果
    │   │           ├── mapper  -- Mybatis-Mapper层映射接口,或称为DAO层
    │   │           ├── redis  -- redis,jedis 相关配置
    │   │           └── service  -- 业务层
    │   └── resources
    │       ├── application.yml  -- SpringBoot核心配置
    │       ├── mapper  -- Mybatis-Mapper层XML映射文件
    │       ├── static  -- 存放页面静态资源,可通过浏览器直接访问
    │       │   ├── css
    │       │   ├── js
    │       │   └── lib
    │       └── templates  -- 存放Thymeleaf模板引擎所需的HTML,不能在浏览器直接访问
    │           ├── page
    │           └── public  -- HTML页面公共组件(头部、尾部)
    └── test  -- 测试文件

SpringBoot

之前我们在SpringBoot-Mybatis入门之CRUD中已经详细讲解了SpringBoot框架的开发流程,还是觉得一句话说的特别好:SpringBoot不是对对Spring功能上的增强,而是提供了一种快速使用Spring的方式。所以用SSM阶段的知识足够了SpringBoot阶段的开发,下面我们强调一下小技巧:

  • SpringBoot不需要配置注解扫描,之前我们配置<context:component-scan>扫描可能使用注解(@Service,@Component,@Controller等)的包路径。默认创建SpringBoot项目自动生成的Application.java启动器类会自动扫描其下的所有注解。

  • SpringBoot项目中静态资源都放在resources目录下,其中static目录中的数据可以直接通过浏览器访问,多用来放CSS、JS、img,但是不用来放html页面;其中templates用来存放HTML页面,但是需要在SpringBoot的配置文件(application.yml)中配置spring.thymeleaf.prefix标识Thymeleaf模板引擎渲染的页面位置。

  • HTML页面通过Thymeleaf的加持,为HTML页面赋予了很多功能,此时的HTML页面类似于JSP页面。访问后端存入域对象(session,request…)中的数据,可以通过th:text="${key}"获得,在JS中也可以通过[[${key}]]获得。

  • Thymeleaf提供了类似JSP页面<include>的功能:public-component:<div th:fragment="header">,main-component:<div th:replace="path/header :: header">(其中path表示public-component相对于templates的路径,/header表示component文件名,最后的header表示th:fragment中定义的名称)。

pom依赖

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.5.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.2</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- alibaba的druid数据库连接池 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.9</version>
        </dependency>

        <!-- redis客户端 -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

JavaBean实体类配置

此处源码请看:GitHub

Seckill.java

public class Seckill implements Serializable {

    private long seckillId; //商品ID
    private String title; //商品标题
    private String image; //商品图片
    private BigDecimal price; //商品原价格
    private BigDecimal costPrice; //商品秒杀价格

    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date createTime; //创建时间

    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date startTime; //秒杀开始时间

    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date endTime; //秒杀结束时间

    private long stockCount; //剩余库存数量
}

SeckillOrder.java

public class SeckillOrder implements Serializable {

    private long seckillId; //秒杀到的商品ID
    private BigDecimal money; //支付金额

    private long userPhone; //秒杀用户的手机号

    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date createTime; //创建时间

    private boolean status; //订单状态, -1:无效 0:成功 1:已付款

    private Seckill seckill; //秒杀商品,和订单是一对多的关系
}

注意实体类中Date类型数据都用了@DateTimeFormat()(来自springframework)和@JsonFormat()(来自jackson)标识可以实现Controller在返回JSON数据(用@ResponseBody标识的方法或@RestController标识的类)的时候能将Date类型的参数值(经Mybatis查询得到的数据是英文格式的日期,因为实体类中是Date类型)转换为注解中指定的格式返回给页面(相当于经过了一层SimpleDateFormate)。

其次要注意在编写实体类的时候尽量养成习惯继承Serializable接口。在SeckillOrder中我们注入了Seckill类作为一个属性,目的是为了可以使用多表查询的方式从seckill_order表中查询出来对应的seckill表数据。

表设计

创建完成了SpringBoot项目,首先我们需要初始化数据库,秒杀系统的建表SQL如下:

/*
 *  mysql-v: 5.7.22
 */

-- 创建数据库
-- CREATE DATABASE seckill DEFAULT CHARACTER SET utf8;

DROP TABLE IF EXISTS `seckill`;
DROP TABLE IF EXISTS `seckill_order`;

-- 创建秒杀商品表
CREATE TABLE `seckill`(
  `seckill_id` bigint NOT NULL AUTO_INCREMENT COMMENT '商品ID',
  `title` varchar (1000) DEFAULT NULL COMMENT '商品标题',
  `image` varchar (1000) DEFAULT NULL COMMENT '商品图片',
  `price` decimal (10,2) DEFAULT NULL COMMENT '商品原价格',
  `cost_price` decimal (10,2) DEFAULT NULL COMMENT '商品秒杀价格',
  `stock_count` bigint DEFAULT NULL COMMENT '剩余库存数量',
  `start_time` timestamp NOT NULL DEFAULT '1970-02-01 00:00:01' COMMENT '秒杀开始时间',
  `end_time` timestamp NOT NULL DEFAULT '1970-02-01 00:00:01' 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` (`end_time`)
) CHARSET=utf8 ENGINE=InnoDB COMMENT '秒杀商品表';

-- 创建秒杀订单表
CREATE TABLE `seckill_order`(
  `seckill_id` bigint NOT NULL COMMENT '秒杀商品ID',
  `money` decimal (10, 2) DEFAULT NULL COMMENT '支付金额',
  `user_phone` bigint NOT NULL COMMENT '用户手机号',
  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间',
  `state` tinyint NOT NULL DEFAULT -1 COMMENT '状态:-1无效 0成功 1已付款',
  PRIMARY KEY (`seckill_id`, `user_phone`) /*联合主键,保证一个用户只能秒杀一件商品*/
) CHARSET=utf8 ENGINE=InnoDB COMMENT '秒杀订单表';

解释

秒杀系统的表设计还是相对简单清晰的,这里我们只考虑秒杀系统的业务表,不涉及其他的表,所以整个系统主要涉及两张表:秒杀商品表、订单表。当然实际情况肯定不止这两张表(比如付款相关表,但是我们并未实现这个功能),也不止表中的这些字段。这里我们需要特别注意以下几点:

注意

  • 1.我这里使用的Mysql版本是5.7.22,在Mysql5.7之后timestamp默认值不能再是0000 00-00 00:00:00,具体的介绍请看:mysql官方文档。即 TIMESTAMP has a range of ‘1970-01-01 00:00:01’ UTC to ‘2038-01-19 03:14:07’ UTC.

  • 2.timestamp类型用来实现自动为新增行字段设置当前系统时间;且使用timestamp的字段必须给timestamp设置默认值,而在Mysql中date, datetime等类型都是无法实现默认设置当前系统时间值的功能(DEFAULT CURRENT_TIMESTAMP)的,所以我们必须使用timestamp类型,否则你要给字段传进来系统时间。

  • 3.decimal类型用于在数据库中设置精确的数值,比如decimal(10,2)表示可以存储10位且有2位小数的数值。

  • 4.tinyint类型用于存放int类型的数值,但是若用Mybatis作为DAO层框架,Mybatis会自动为tinyint类型的数据转换成true或false(0:false; 1 or 1+:true)。

  • 5.在订单表seckill_order中我们设计了联合主键:PRIMARY KEY (seckill_id, user_phone),目的是为了避免单个用户重复购买同一件商品(一个用户只能秒杀到一次同一件商品)。

  • 6.无论是创建数据库还是创建表我们都应该养成一个习惯就是指定character=utf-8,避免中文数据乱码;其次还应该指定表的储存引擎是InnoDB,MySQL提供了两种储存引擎:InnoDB, MyISAM。但是只有InnoDB是支持事务的,且InnoDB相比MyISAM在并发上更具有高性能的优点。

DAO层开发

DAO层是我们常说的三层架构(Web层-业务层-持久层)中与数据库交互的持久层,但是实际而言,架构是这样设计的,但是并不代表着实际项目中就一定存在一个dao文件夹,特别是现阶段我们使用的Spring-Mybatis框架。Mybatis提供了一种接口代理开发模式,也就是我们需要提供一个interface接口,其他和数据库交互的SQL编写放到对应的XML文件中(但是需要进行相关的数据库参数配置,并且Mybatis规定了使用这种开发模式必须保持接口和XML文件名称对应)。于是在本项目中就没有出现dao整个文件夹,取而代之的是mapper这个文件夹,我感觉更易识别出为Mybatis的映射接口文件。其实在实际项目中考虑到项目的大小和复杂程度,dao和mapper可能是同时存在的,因为service可能并不满足项目的设计,即为dao接口创建实现类,在实现类中再调用mapper接口来实现功能模块的扩展。


DAO层开发,即DAO层接口开发,主要设计需要和数据库交互的数据有哪些?应该用什么返回值类型接收查询到的数据?所以包含的方法有哪些?带着这些问题,我们先看一下秒杀系统的业务流程:

由上图可以看出,相对与本项目而言和数据库打交道的主要涉及两个操作:1.减库存(秒杀商品表);2.记录购买明细(订单表)。

  • 减库存,顾名思义就是减少当前被秒杀到的商品的库存数量,这也是秒杀系统中一个处理难点的地方。实现减库存即count-1,但是我们需要考虑Mysql的事务特性引发的种种问题、需要考虑如何避免同一用户重复秒杀的行为。

  • 如果减库存的业务解决了那么记录购买明细的业务就相对简单很多了,我们需要记录购买用户的姓名、手机号、购买的商品ID等。因为本项目中不涉及支付功能,所以记录用户的购买订单的业务并不复杂。

分析了上面的功能,下面我们开始DAO层接口的编写(源码请看:GitHub):

    /**
     * 减库存。
     * 对于Mapper映射接口方法中存在多个参数的要加@Param()注解标识字段名称,不然Mybatis不能识别出来哪个字段相互对应
     *
     * @param seckillId 秒杀商品ID
     * @param killTime  秒杀时间
     * @return 返回此SQL更新的记录数,如果>=1表示更新成功
     */
    int reduceStock(@Param("seckillId") long seckillId, @Param("killTime") Date killTime);

    /**
     * 插入购买订单明细
     *
     * @param seckillId 秒杀到的商品ID
     * @param money     秒杀的金额
     * @param userPhone 秒杀的用户
     * @return 返回该SQL更新的记录数,如果>=1则更新成功
     */
    int insertOrder(@Param("seckillId") long seckillId, @Param("money") BigDecimal money, @Param("userPhone") long userPhone);

但从接口设计上我们无非关注的就是这两个方法:1.减库存;2.插入购买明细。此处需要注意的是:

  • 对于SpringBoot系统,DAO(Mapper)层的接口需要使用@Mapper注解标识。因为SpringBoot系统中接口的XML文件不在/java目录下而是在/resources目录下。

  • 对于Mapper接口方法中存在传递多个参数的情况需要使用@Param()标识这个参数的名称,目的是为了帮助Mybatis识别传递的参数,不然Mybatis的XML中用的#{}不能识别出来你传递的参数名称是谁和谁对应的,类似于Controller层中常用的@RequestParam()注解。

  • 小技巧: 之前我们做insert和update操作时直接用void作为方法返回值,实际上虽然Mybatis的<update>和<select>语句并没有resultType属性,但是并不代表其没有返回值,默认返回0或1,表示执行该SQL影响的行数。为此我们可以这样写SQL,如:insert ignore into xxx用来避免Mybatis报错,而是直接返回0表示当前SQL执行失败。

  • 小技巧:因为我们必须要避免同一个用户多次抢购同一件商品,在SQL中必须限制这一点(因为即使前端怎么控制都无法避免用户多次请求同一个接口,所谓接口防刷)。所以在设计订单表的时候用了联合主键且不自增的方式,以用户ID和用户电话组成联合主键,这样当同一个用户(电话相同)多次抢购同一件商品时插入的SQL就会产生主键冲突的问题,这样就会报错。

XML映射

    <update id="reduceStock">
        UPDATE seckill
        SET stock_count = stock_count - 1
        WHERE seckill_id = #{seckillId}
        AND start_time &lt;= #{killTime}
        AND end_time &gt;= #{killTime}
        AND stock_count &gt; 0
    </update>

    <insert id="insertOrder">
        INSERT ignore INTO seckill_order(seckill_id, money, user_phone)
        VALUES (#{seckillId}, #{money}, #{userPhone})
    </insert>

SQL语句相对不是很复杂。减库存:执行update语句,令stock_count字段依次减一,并且当前要在一系列where条件的限制下;新增订单信息:保存订单数据,这里为接口防刷用联合主键seckillId, userPhone,如果同一个用户多次抢购同一件商品导致主键冲突会直接报错,为了避免系统不直接报错设计了ignore实现主键冲突就直接返回0表示该条SQL执行失败。

拓展

上面我使用了&lt;、&gt;的语法其实代表的是>= <=这种符号,因为在Mybatis中编写的SQL语句如果直接使用>=或<=这种判断条件可能会报错,我这里提供一种简单的解决方案就是用这种英文符号代替:

原符号替换符号
<<
<=<=
>>
>=>=
&&
’'
""

order表中findById方法

之前在SeckillOrder.java实体类中我们注入了Seckill属性,用于可以根据查询seckill_order表的同时查询到其对应的seckill表数据,对应的接口定义如下:

    /**
     * 根据秒杀商品ID查询订单明细数据并得到对应秒杀商品的数据,因为我们再SeckillOrder中已经定义了一个Seckill的属性
     *
     * @param seckillId
     * @return
     */
    SeckillOrder findById(long seckillId);

对应的SQL如下:

    <select id="findById" resultType="SeckillOrder">
        SELECT
          so.seckill_id,
          so.user_phone,
          so.money,
          so.create_time,
          so.state,
          s.seckill_id "seckill.seckill_id",
          s.title "seckill.title",
          s.cost_price "seckill.cost_price",
          s.create_time "seckill.create_time",
          s.start_time "seckill.start_time",
          s.end_time "seckill.end_time",
          s.stock_count "seckill.stock_count"
        FROM seckill_order so
        INNER JOIN seckill s ON so.seckill_id = s.seckill_id
        WHERE so.seckill_id = #{seckillId}
    </select>

这个SQL看似复杂些,但是就是仅仅的多表(两张表)查询语句:根据seckill_order表中的seckill_id字段查询seckill表中seckill_id字段值对应的数据(也就是说:对于多表查询,其实两张表之间必然存在一定的字段关联关系,不一定是外键关联,当然我们也不建议用外键关联两张表)。

其中findById的SQL中类似s.seckill_id "seckill.seckill_id"语句其实是s.seckill_id as "seckill.seckill_id",这里省略了as(别名);而INNER JOIN语句正是查询若两张表中中又相同字段的匹配值就根据两张表关联字段查询两张表的数据。这也可以使用<resultMap>中的<association>标签来实现,用于查询两张关联表的数据,如:

  <resultMap id="findById" type="SeckillOrder">
      <id column="seckill_id" property="seckillId"/>
      <result column="user_phone" property="userPhone"/>
      ...
      <association property="seckill" javaType="Seckill">
          <id column="seckill_id" property="seckillId"/>
          <result column="title" property="title"/>
          ...
      </association>
  </resultMap>

如以上也是一种映射另外一张表数据的方式(当然使用这种方式在写SQL的时候需要指定限制条件where s.seckill_id = so.seckill_id强调两张表中的seckill_id字段值相同)。

测试

在编写了Mybatis的映射接口和XML映射文件,我们可以编写一个测试类来测试一下接口和XML配置是否正确。由于我们使用IDEA开发工具,打开接口文件用快捷键Alt + Enter(我这里用的Mac系统)显示一个面板,选择Create Test快速创建本文件的测试类。

由于使用的SpringBoot框架,新创建的测试类位于/src/test/java/目录下,我们举例说明,比如创建SeckillMapper接口的测试文件:SeckillMapperTest.java

public class SeckillMapperTest {

    @Autowired
    private SeckillMapper seckillMapper;

    @Test
    public void findAll() {
    }

    @Test
    public void findById() {
    }

    @Test
    public void reduceStock() {
    }
}

以上就是使用IDEA快捷键创建的测试类,我们仅以findAll()方法举例说明一下如何使用SpringBoot的测试类。如下:

此处的源码请参看:Github

@RunWith(SpringJUnit4ClassRunner.class)
//@ContextConfiguration("classpath:application.yml")
@SpringBootTest
public class SeckillMapperTest {

    @Autowired
    private SeckillMapper seckillMapper;

    @Test
    public void findAll() {
        List<Seckill> all = seckillMapper.findAll();
        for (Seckill seckill : all) {
            System.out.println(seckill.getTitle());
        }
    }

    @Test
    public void findById() {
    }

    @Test
    public void reduceStock() {
    }
}

SpringBoot的测试类和传统Spring框架测试类的最大区别就是不再使用@ContextConfiguration()注解去加载配置文件,取而代之的是使用@SpringBootTest注解。因为SpringBoot已经严格规定了配置文件放在resources目录下,且一般是.properties或.yml结尾。如果你再使用@ContextConfiguration()注解加载配置文件反而会报错。



交流

如果大家有兴趣,欢迎大家加入我的Java交流群:671017003 ,一起交流学习Java技术。博主目前一直在自学JAVA中,技术有限,如果可以,会尽力给大家提供一些帮助,或是一些学习方法,当然群里的大佬都会积极给新手答疑的。所以,别犹豫,快来加入我们吧!


联系

If you have some questions after you see this article, you can contact me or you can find some info by clicking these links.

FinRL实战:5步搞定股票量化交易策略(附Python代码) 本文详细介绍了如何使用FinRL框架,通过5个步骤构建基于深度强化学习的股票量化交易策略。内容涵盖从环境搭建、数据获取与预处理,到交易环境构建、模型训练(如PPO算法),以及策略回测评估与优化的完整流程,并提供了可运行的Python代码示例,帮助读者快速入门AI驱动的量化交易。 阅读详情

相关推荐

匈牙利算法解决指派问题清晰流程

匈牙利算法解决指派问题清晰流程 百度词条上,指派问题(Assignment problem)是这么定义的:在满足特定指派要求条件下,使指派方案总体效果最佳。如:有若干项工作需要分配给若干人(或部门)来完成;有若干项合同需要选择若干个投标者来承包:有若干班级需要安排在若干教室里上课等。   一、做减法(归约): 行归约:每行元素减去该行最小元素。 列归约:每行元素减去该行最小元素。

siss0siss的博客 6万+

Spring-Boot-4-Dao层

Spring-Boot-4-Dao层

SpriCoder的博客 3309

【架构师面试-缓存与搜索-1】-缓存与缓存置换策略源码实现

1:什么是缓存 缓存:加速数据访问的存储,降低延迟(latency),提升吞吐量(Throughput)的利器。 1:缓存演进历史 1. 查库 2. ConcurrentHashMap 3. LRU 4. Guava Cache 5. 分布式缓存(redis,MemCache) 6. 多级缓存 2:在什么地方加缓存 缓存对于每个开发者来说是相当熟悉了,为了提高程序的性能我们会去加缓存,但是在什么地方加缓存,如何加缓存呢? 举个栗子:假设一个网站,需要提高性能,缓存可以放在浏览器,可以放

chongfa2008的博客 6437

SpringBoot+MyBatis项目Dao层最简单写法

前言 DAO(Data Access Object) 是数据访问层,说白了就是跟数据库打交道的,而数据库都有哪几种操作呢?没错,就是增删改查。这就意味着Dao层要提供增删改查操作。 不知道大家是怎么写Dao层的接口的。如果你没有一个好的思路,那就看看我的思路吧。如果你有更好的思路,欢迎指正。 正文 1.每一个实体类对应一个Dao接口文件和一个mybatis文件 结构如下: 2.UserDao采用......

Hit the keyboard hard 1万+

三端口DC-DC变换器设计与光伏储能协同控制

三端口DC-DC变换器是面向分布式能源系统的关键功率变换拓扑,通过在光伏输入、储能电池与直流负载三个端口间实现动态能量分配,解决多源异步供电下的电压稳定与功率平衡问题。其核心原理基于双Boost半桥架构与双闭环协同控制,融合MPPT最大功率点跟踪与电池智能充放电管理,在保证30V高精度稳压(±0.5V)的同时,支持光伏主导供电与电池支撑供电的自动模式切换。该技术显著提升光储系统能量利用率与供电连续性,广泛应用于微电网、便携式交直流电源及离网型光储一体机等场景,是构建柔性直流配电与智能能源管理的基础硬件单元。

weixin_36431814的博客 140

ISO26262 功能安全各个阶段测试要求

1. 开发过程说明 系统层面:包括软硬件集成测试、系统集成测试以及整车测试。 硬件层面:包括硬件集成与测试。 软件层面:单元测试、集成测试。 2. 功能安全总体要求 2.1 各项指标 2.2 诊断覆盖度举例 针对一个信号举例说明: 3. 功能安全测试要求 3.1 测试计划 核心内容应包括, 测试对象:需要测试的对象描述,以及测试的范围描...

AgingMoon的博客 1万+

【Spring Boot】开发基于三层架构设计:Dao层、Service层、Controller层及案例一(91)

1.Spring Boot 开发基于三层架构设计:Dao层、Service层、Controller层及案例一

qq_42139049的博客 4887

图像分割 - 使用Mask R-CNN模型在COCO数据集上进行图像分割任务

图像分割是计算机视觉领域的一个重要任务,它旨在将图像中的每个像素分类为对象或背景。Mask R-CNN是一种基于区域的卷积神经网络,可用于图像分割任务。它是Faster R-CNN的扩展,将目标检测与图像分割结合起来,能够同时检测和分割对象。在本博客中,我们将使用Mask R-CNN模型在COCO数据集上进行图像分割任务。我们将介绍如何下载和准备COCO数据集,以及如何使用Mask R-CNN模型进行训练和推理。

斌擎科技 1188

SpringBoot秒杀系统(一)Dao 层

SpringBoot秒杀体统(一)Dao 层 学习自慕课网(https://www.imooc.com/u/2145618/courses?sort=publish),原项目为SSM,现改造为springboot项目。 秒杀系统业务流程 1、问题在于如何不超卖、使用户如何不超买。 可能出现的问题: 难点分析: 使用事务+ 行级锁来约束购买 事务: 数据库管理系统执行过程中的一个逻辑单位,由一...

尔等同学的博客 769

MATLAB 车牌识别系统

车牌识别系统利用计算机视觉和图像处理技术,自动识别车辆牌照上的字符信息。本系统基于 MATLAB 实现车牌识别,包括图像预处理、车牌定位、字符分割、字符识别等模块。车牌识别系统是实现智能交通和公共安全的重要手段,具有广泛的应用前景。MATLAB 提供了丰富的工具和函数,可以方便地进行算法仿真和系统开发。

走向CTO的路上... 714

GEE:本地影像上传到GEE的Assets中,并输入机器学习算法中作为特征变量

当我们在 Google Earth Engine(GEE)中应用机器学习算法时,会输入一些影像作为特征变量数据,进一步根据这些特征影像去推理未知区域的数据。但是 GEE 平台上计算特征变量的 API 函数并不是非常全面,我们希望获得更多的特征用于分类。这个时候,我们可以将本地计算的特征影像上传到 GEE 中进行后续处理。本文记录了从本地上传特征数据到 GEE 的 Assets 中的教程,用于后续在 GEE 中分类或者回归使用。本文也可作为《》一文的补充。

养乐多的博客 1730

springboot(二)web页面、dao层代码准备

1、准备html模板 404页面、dashboard主页、index登录页面、list表单页面 asserts文件夹里面是css、js、img这些静态资源 2、静态资源放进项目 404页面、dashboard主页、index登录页面、list表单页面放到resources/templates文件夹下,asserts文件夹放到resources/static文件夹下。 3、pojo创建 在com.cjc.myspringboot包下创建pojo包,再创建Department.java、Employee.j

linpulao的博客 696

ZYNQ 网络通信的四种实现方案

一,ZYNQ MIO以RGMII 接口连接外部PHY芯片实现LWIP网络通信。 二,ZYNQ PS 的 ENET0/ENET1 通过 EMIO 的方式扩展至 PL,在 PL 中通过 RGMII 接口连接扩展子卡实现 LWIP 网络通信。 在PL中搭建了1个AXI 1G/2.5G Ethernet Subsystem以及1个AXI Direct Memory Access IP 核。这两个 IP核均通过AXI 总线经S_AXI_HP0 口与PS 连接,PS 通过AXI 总线对其进行配置和控..

wangjie36的博客 1万+

SpringBoot实现Java高并发秒杀系统

SpringBoot实现Java高并发秒杀系统pom依赖JavaBean实体类pojo配置表设计DAO层开发XML映射Service接口的设计前端交互流程设计 首先创建springboot项目; pom依赖 <parent> <groupId>org.springframework.boot</groupId> <a...

Ronin_He的博客 874

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

(一)基于SpringBoot实战Java秒杀高并发系统DAO开发(超详细)

项目介绍 秒杀系统在如今电商项目中是很常见的,最近在学习电商项目时讲到了秒杀系统的实现,于是打算使用SpringBoot框架学习一下秒杀系统(本项目基于慕课网的一套免费视频教程:Java高并发秒杀API,视频教程中讲解的很详细,非常感谢这位讲师)。也是因为最近学习了SpringBoot框架觉得SpringBoot框架确实比传统SSM框架方便了很多,于是更深层次练习使用SpringBoot框架,注意:SpringBoot不是对Spring功能上的增强,而是提供了一种快速使用Spring的方式。 如果你熟悉了S

weixin_45593950的博客 1220

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

SpringBoot实现Java高并发秒杀系统之Service层开发(二)

继上一篇文章:SpringBoot实现Java高并发秒杀系统之DAO层开发 我们创建了SpringBoot项目并熟悉了秒杀系统的表设计,下面我们将讲解一下秒杀系统的核心部分:Service业务层的开发。 Service层又称为业务层,在Spring阶段主要是由@Service注解标记的一层,包含Service业务接口的开发和业务接口实现类的开发,这里我们将讲解如何优雅的设计业务层接口以及针对秒杀系...

TyCoding的博客 1万+

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

(二)基于SpringBoot实战Java秒杀高并发系统Service开发(超详细)

根据上篇博客接下来我们继续讲,SpringBoot实现Java高并发秒杀系统之DAO层开发 我们创建了SpringBoot项目并熟悉了秒杀系统的表设计,下面我们将讲解一下秒杀系统的核心部分:Service业务层的开发。 Service层又称为业务层,在Spring阶段主要是由@Service注解标记的一层,包含Service业务接口的开发和业务接口实现类的开发,这里我们将讲解如何优雅的设计业务层接口以及针对秒杀系统业务层的优化技术等和针对高并发的解决方案。 Service接口的设计 之前我们写好了DAO层的

weixin_45593950的博客 849

SpringBoot实现Java高并发秒杀系统之Web层开发(三)

接着上一篇文章:SpringBoot实现Java高并发之Service层开发,今天我们开始讲SpringBoot实现Java高并发秒杀系统之Web层开发。 Web层即Controller层,当然我们所说的都是在基于Spring框架的系统上而言的,传统的SSH项目中,与页面进行交互的是struts框架,但struts框架很繁琐,后来就被SpringMVC给顶替了,SpringMVC框架在与页面的交互...

TyCoding的博客 1972

探索高并发解决方案:SpringBoot实现的Java秒杀系统

探索高并发解决方案:SpringBoot实现的Java秒杀系统 在如今的互联网环境中,高并发处理能力是每个大型网站必备的技术之一。特别是在促销活动中,秒杀系统的稳定性和高效性显得尤为重要。这里向您推荐一个基于SpringBoot的Java高并发秒杀系统开源项目,它将带给你全新的开发体验和深度理解高并发场景的解决策略。 项目介绍 这个项目是一个精心构建的秒杀系统,采用SpringBoot作为后端框架...

gitblog_00094的博客 720

SpringBoot实现Java高并发秒杀系统之并发优化(四)

之前我们已经讲了: SpringBoot实现Java高并发秒杀系统之Web层开发(三) SpringBoot实现Java高并发秒杀系统之Service层开发(二) SpringBoot实现Java高并发秒杀系统之DAO层开发(一) 今天我们来分析一下秒杀系统的难点和怎么进行并发优化。 本项目的源码请参看:springboot-seckill 如果觉得不错可以star一下哦(#.#...

TyCoding的博客 1万+

SpringBoot从入门到精通系列八:高并发秒杀系统实践

SpringBoot从入门到精通系列八:高并发秒杀系统实践 以电商网站常见的秒杀场景为例,SpringBoot整合Spring MVC、Mybatis、Redis、RabbitMQ等技术来实现高并发秒杀系统。 该系统包含常规用到的SSM技术,还包含了基于Redis的分布式Session、基于Redis的数据缓存、基于Redis的页面缓存、基于RabbitMQ的秒杀限流等。 ...

zhengzaifeidelushang的博客 1029

Springboot+mybatis+redis实现java秒杀系统

Springboot+mybatis+redis实现java秒杀系统1.效果图标题 1.效果图 标题

sbw17608463386的博客 890

Java高并发秒杀系统

每个类都写大量的健壮性判断过于麻烦,我们可以使用 validation 简化我们的代码。比如可以自定义一个@IsMobile注解来判断登录功能中手机号码的合法性。

Teachmepatiently的博客 1265
上一篇: SpringBoot实现Java高并发秒杀系统之Service层开发(二)
下一篇: 基于SpringBoot炒鸡漂亮的Tumo Blog开源了
TyCoding
博客等级 码龄9年 316粉丝 49原创
评论 4
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值