让我们使用 Java 和 Spring 从头开发一个电子商务应用程序

2025-06-07

让我们使用 Java 和 Spring 从头开发一个电子商务应用程序

项目设置,开发类别和产品 API

动机

在我看来,学习编程的最佳方式是创建一个具有实际用途的真实项目,这样整个学习过程就会变得非常精彩。此外,你可以在作品集中展示你的应用,这对你获得自由职业或面试机会大有裨益。

在本系列博客中,你将学习如何从零开始构建电商平台,从而提升你的开发技能。首先,你需要熟悉Java和 Spring Boot(我们将用它们来构建后端)以及Vue.js(我们将用它构建前端)。

读者须知:

虽然我已经构建了整个应用程序并编写了一系列教程,这些教程非常受欢迎并且在谷歌搜索结果中名列前茅,对此我感到非常自豪,(仅在 Medium 上的浏览​​量就超过 13 万次)

谷歌搜索结果中名列前茅

后来我发现这些教程有些内容缺失,有些已经不再适用了。比如,有些教程里我们用原生 JS 开发了一个Android 应用,但后来就放弃了。

因此,我尝试重新制作教程,删除/编辑一些不再相关的部分,并创建一些涵盖缺失部分的教程,以便用户可以非常轻松地遵循教程。

视频教程

播放列表

前端

后端

Vue 前端教程

创建项目

  1. 首先,转到https://start.spring.io/,我们可以在其中创建新的 spring 应用程序并添加依赖项

  2. 选择maven,添加Spring Data JPA和Spring Web依赖

  1. 单击“生成”并下载 .zip 文件,解压缩并使用IntelliJ Idea打开它

项目结构

主类

项目的 src/main/java 文件夹包含一个具有 main 方法的类。它是应用程序的入口

应用程序.属性

在src/main/resources文件夹中,会有一个名为application.properties的文件。该文件主要负责向 Spring 传达我们所做的配置以及如何创建对象。换句话说,它在控制反转 (IoC)中扮演着重要的角色。

pom.xml

在项目文件夹中,会有一个名为的文件pom.xml。我们将在此文件中添加所有必需的依赖项。

现在,项目结构如下-

您可以在下面给出的 GitHub 存储库分支中检查后端的项目结构 -
GitHub — webtutsplus/ecommerce

我们的后端应用程序概述

在这个 Spring 应用程序中,以下是您在开始之前必须了解的重要包。

这就是 Spring 架构。外界调用REST API ,REST API 与控制器交互,控制器与服务交互,服务调用存储库。

存储库与数据库交互。我们遵循这种模式,使代码库易于维护,避免出现意大利面条式的代码,长远来看,这可能是一场噩梦。

模型/实体

模型是与数据库中表的结构直接相关的基本实体。换句话说,这些模型充当容器,保存相似和相关的数据,用于将这些数据从客户端传输到数据库。用户资料、产品和类别是我们后端应用程序中的一些模型。

存储库

存储库 (Repository) 是充当数据库和应用程序之间桥梁的接口。它将模型数据传入和传出数据库。每个模型都有一个唯一的存储库用于数据传输。

服务

服务是架构的一部分,在这里实例化存储库并应用业务逻辑。客户端到达这里的数据经过处理后,通过存储库发送到数据库。

控制器

控制器是架构中首先处理来自客户端请求的部分。它控制着应该在后端运行的进程以及需要发送给客户端的响应。它与服务交互,服务又与存储库交互,存储库又使用模型与数据库交互

数据之旅

数据如何移动

设计类别 API

一旦我们准备好基本结构,就可以为我们的电子商务商店添加一些产品和类别了。

举个例子,我们可以创建一个“鞋子”类别,其中包含不同类型的鞋子作为产品。因此,一个类别可以包含多个产品,但每个产品都属于一个类别。

模型

首先我们将创建一个模型,Category它将有四个字段。

  1. ID

  2. 类别名称

  3. 描述

  4. 图片网址

我们还将为这四个字段创建一个 setter 和 getter。

它在数据库中会有对应的表类别

package com.educative.ecommerce.model;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.validation.constraints.NotBlank;
import java.util.Set;
@Entity
@Table(name = "categories")
public class Category {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "category_name")
private @NotBlank String categoryName;
private @NotBlank String description;
private @NotBlank String imageUrl;
public Category() {
}
public Category(@NotBlank String categoryName, @NotBlank String description) {
this.categoryName = categoryName;
this.description = description;
}
public Category(@NotBlank String categoryName, @NotBlank String description, @NotBlank String imageUrl) {
this.categoryName = categoryName;
this.description = description;
this.imageUrl = imageUrl;
}
public String getCategoryName() {
return this.categoryName;
}
public void setCategoryName(String categoryName) {
this.categoryName = categoryName;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
@Override
public String toString() {
return "User {category id=" + id + ", category name='" + categoryName + "', description='" + description + "'}";
}
public String getImageUrl() {
return imageUrl;
}
public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
}
view raw Category.java hosted with ❤ by GitHub
package com.educative.ecommerce.model;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.validation.constraints.NotBlank;
import java.util.Set;
@Entity
@Table(name = "categories")
public class Category {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "category_name")
private @NotBlank String categoryName;
private @NotBlank String description;
private @NotBlank String imageUrl;
public Category() {
}
public Category(@NotBlank String categoryName, @NotBlank String description) {
this.categoryName = categoryName;
this.description = description;
}
public Category(@NotBlank String categoryName, @NotBlank String description, @NotBlank String imageUrl) {
this.categoryName = categoryName;
this.description = description;
this.imageUrl = imageUrl;
}
public String getCategoryName() {
return this.categoryName;
}
public void setCategoryName(String categoryName) {
this.categoryName = categoryName;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
@Override
public String toString() {
return "User {category id=" + id + ", category name='" + categoryName + "', description='" + description + "'}";
}
public String getImageUrl() {
return imageUrl;
}
public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
}
view raw Category.java hosted with ❤ by GitHub

我们对该类别使用了 @NotBlank 注释。为此,我们必须在 pom.xml 文件中包含以下依赖项。



    <dependency>

      <groupId>javax.validation</groupId>

      <artifactId>validation-api</artifactId>

    </dependency>


Enter fullscreen mode Exit fullscreen mode

存储库

现在我们将创建一个扩展 JpaRepository 的存储库 Categoryrepository.java。

它将有一个方法 findByCategoryName。

package com.educative.ecommerce.repository;
import com.educative.ecommerce.model.Category;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface Categoryrepository extends JpaRepository<Category, Integer> {
Category findByCategoryName(String categoryName);
}
package com.educative.ecommerce.repository;
import com.educative.ecommerce.model.Category;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface Categoryrepository extends JpaRepository<Category, Integer> {
Category findByCategoryName(String categoryName);
}

服务

现在我们将创建一个 CategoryService 文件,负责创建、更新或获取存储库。

Categoryrepository 具有内置方法 findAll()、save(),因为它扩展了 JpaRepository

package com.educative.ecommerce.service;
import com.educative.ecommerce.model.Category;
import com.educative.ecommerce.repository.Categoryrepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.transaction.Transactional;
import java.util.List;
import java.util.Optional;
@Service
public class CategoryService {
@Autowired
private Categoryrepository categoryrepository;
public List<Category> listCategories() {
return categoryrepository.findAll();
}
public void createCategory(Category category) {
categoryrepository.save(category);
}
public Category readCategory(String categoryName) {
return categoryrepository.findByCategoryName(categoryName);
}
public Optional<Category> readCategory(Integer categoryId) {
return categoryrepository.findById(categoryId);
}
public void updateCategory(Integer categoryID, Category newCategory) {
Category category = categoryrepository.findById(categoryID).get();
category.setCategoryName(newCategory.getCategoryName());
category.setDescription(newCategory.getDescription());
category.setImageUrl(newCategory.getImageUrl());
categoryrepository.save(category);
}
}
package com.educative.ecommerce.service;
import com.educative.ecommerce.model.Category;
import com.educative.ecommerce.repository.Categoryrepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.transaction.Transactional;
import java.util.List;
import java.util.Optional;
@Service
public class CategoryService {
@Autowired
private Categoryrepository categoryrepository;
public List<Category> listCategories() {
return categoryrepository.findAll();
}
public void createCategory(Category category) {
categoryrepository.save(category);
}
public Category readCategory(String categoryName) {
return categoryrepository.findByCategoryName(categoryName);
}
public Optional<Category> readCategory(Integer categoryId) {
return categoryrepository.findById(categoryId);
}
public void updateCategory(Integer categoryID, Category newCategory) {
Category category = categoryrepository.findById(categoryID).get();
category.setCategoryName(newCategory.getCategoryName());
category.setDescription(newCategory.getDescription());
category.setImageUrl(newCategory.getImageUrl());
categoryrepository.save(category);
}
}

控制器

我们将创建一个辅助类 ApiResponse.java,用于返回 API 的响应。

package com.educative.ecommerce.common;
import java.time.LocalDateTime;
public class ApiResponse {
private final boolean success;
private final String message;
public ApiResponse(boolean success, String message) {
this.success = success;
this.message = message;
}
public boolean isSuccess() {
return success;
}
public String getMessage() {
return message;
}
public String getTimestamp() {
return LocalDateTime.now().toString();
}
}
package com.educative.ecommerce.common;
import java.time.LocalDateTime;
public class ApiResponse {
private final boolean success;
private final String message;
public ApiResponse(boolean success, String message) {
this.success = success;
this.message = message;
}
public boolean isSuccess() {
return success;
}
public String getMessage() {
return message;
}
public String getTimestamp() {
return LocalDateTime.now().toString();
}
}

现在我们将创建包含所有 API 的控制器

我们将为类别创建 3 个 API

  1. 创造

  2. 更新

  3. 列出所有类别

    package com.educative.ecommerce.controllers;
    import com.educative.ecommerce.common.ApiResponse;
    import com.educative.ecommerce.model.Category;
    import com.educative.ecommerce.service.CategoryService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.ResponseEntity;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import javax.validation.Valid;
    import java.util.List;
    import java.util.Objects;
    @RestController
    @RequestMapping("/category")
    public class CategoryController {
    @Autowired
    private CategoryService categoryService;
    @GetMapping("/")
    public ResponseEntity<List<Category>> getCategories() {
    List<Category> body = categoryService.listCategories();
    return new ResponseEntity<>(body, HttpStatus.OK);
    }
    @PostMapping("/create")
    public ResponseEntity<ApiResponse> createCategory(@Valid @RequestBody Category category) {
    if (Objects.nonNull(categoryService.readCategory(category.getCategoryName()))) {
    return new ResponseEntity<ApiResponse>(new ApiResponse(false, "category already exists"), HttpStatus.CONFLICT);
    }
    categoryService.createCategory(category);
    return new ResponseEntity<>(new ApiResponse(true, "created the category"), HttpStatus.CREATED);
    }
    @PostMapping("/update/{categoryID}")
    public ResponseEntity<ApiResponse> updateCategory(@PathVariable("categoryID") Integer categoryID, @Valid @RequestBody Category category) {
    // Check to see if the category exists.
    if (Objects.nonNull(categoryService.readCategory(categoryID))) {
    // If the category exists then update it.
    categoryService.updateCategory(categoryID, category);
    return new ResponseEntity<ApiResponse>(new ApiResponse(true, "updated the category"), HttpStatus.OK);
    }
    // If the category doesn't exist then return a response of unsuccessful.
    return new ResponseEntity<>(new ApiResponse(false, "category does not exist"), HttpStatus.NOT_FOUND);
    }
    }
    package com.educative.ecommerce.controllers;
    import com.educative.ecommerce.common.ApiResponse;
    import com.educative.ecommerce.model.Category;
    import com.educative.ecommerce.service.CategoryService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.ResponseEntity;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import javax.validation.Valid;
    import java.util.List;
    import java.util.Objects;
    @RestController
    @RequestMapping("/category")
    public class CategoryController {
    @Autowired
    private CategoryService categoryService;
    @GetMapping("/")
    public ResponseEntity<List<Category>> getCategories() {
    List<Category> body = categoryService.listCategories();
    return new ResponseEntity<>(body, HttpStatus.OK);
    }
    @PostMapping("/create")
    public ResponseEntity<ApiResponse> createCategory(@Valid @RequestBody Category category) {
    if (Objects.nonNull(categoryService.readCategory(category.getCategoryName()))) {
    return new ResponseEntity<ApiResponse>(new ApiResponse(false, "category already exists"), HttpStatus.CONFLICT);
    }
    categoryService.createCategory(category);
    return new ResponseEntity<>(new ApiResponse(true, "created the category"), HttpStatus.CREATED);
    }
    @PostMapping("/update/{categoryID}")
    public ResponseEntity<ApiResponse> updateCategory(@PathVariable("categoryID") Integer categoryID, @Valid @RequestBody Category category) {
    // Check to see if the category exists.
    if (Objects.nonNull(categoryService.readCategory(categoryID))) {
    // If the category exists then update it.
    categoryService.updateCategory(categoryID, category);
    return new ResponseEntity<ApiResponse>(new ApiResponse(true, "updated the category"), HttpStatus.OK);
    }
    // If the category doesn't exist then return a response of unsuccessful.
    return new ResponseEntity<>(new ApiResponse(false, "category does not exist"), HttpStatus.NOT_FOUND);
    }
    }
    我们还将添加 swagger 以便于测试代码。
    package com.educative.ecommerce.config;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import springfox.documentation.builders.ApiInfoBuilder;
    import springfox.documentation.builders.PathSelectors;
    import springfox.documentation.builders.RequestHandlerSelectors;
    import springfox.documentation.service.ApiInfo;
    import springfox.documentation.service.Contact;
    import springfox.documentation.spi.DocumentationType;
    import springfox.documentation.spring.web.plugins.Docket;
    import springfox.documentation.swagger2.annotations.EnableSwagger2;
    @Configuration
    @EnableSwagger2
    public class SwaggerConfig {
    @Bean
    public Docket productApi() {
    return new Docket(DocumentationType.SWAGGER_2)
    .apiInfo(getApiInfo())
    .select()
    .apis(RequestHandlerSelectors.basePackage("com.educative.ecommerce"))
    .paths(PathSelectors.any())
    .build();
    }
    private ApiInfo getApiInfo() {
    Contact contact = new Contact("webtutsplus", "http://webtutsplus.com", "contact.webtutsplus@gmail.com");
    return new ApiInfoBuilder()
    .title("Ecommerce API")
    .description("Documentation Ecommerce api")
    .version("1.0.0")
    .license("Apache 2.0")
    .licenseUrl("http://www.apache.org/licenses/LICENSE-2.0")
    .contact(contact)
    .build();
    }
    }
    package com.educative.ecommerce.config;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import springfox.documentation.builders.ApiInfoBuilder;
    import springfox.documentation.builders.PathSelectors;
    import springfox.documentation.builders.RequestHandlerSelectors;
    import springfox.documentation.service.ApiInfo;
    import springfox.documentation.service.Contact;
    import springfox.documentation.spi.DocumentationType;
    import springfox.documentation.spring.web.plugins.Docket;
    import springfox.documentation.swagger2.annotations.EnableSwagger2;
    @Configuration
    @EnableSwagger2
    public class SwaggerConfig {
    @Bean
    public Docket productApi() {
    return new Docket(DocumentationType.SWAGGER_2)
    .apiInfo(getApiInfo())
    .select()
    .apis(RequestHandlerSelectors.basePackage("com.educative.ecommerce"))
    .paths(PathSelectors.any())
    .build();
    }
    private ApiInfo getApiInfo() {
    Contact contact = new Contact("webtutsplus", "http://webtutsplus.com", "contact.webtutsplus@gmail.com");
    return new ApiInfoBuilder()
    .title("Ecommerce API")
    .description("Documentation Ecommerce api")
    .version("1.0.0")
    .license("Apache 2.0")
    .licenseUrl("http://www.apache.org/licenses/LICENSE-2.0")
    .contact(contact)
    .build();
    }
    }
    我们还需要在 pom.xml 文件中添加 Swagger 和 H2 内存数据库的依赖项。当然,你也可以自由选择其他数据库。
    
    
    <dependency>
    
      <groupId>io.springfox</groupId>
    
      <artifactId>springfox-bean-validators</artifactId>
    
      <version>2.9.2</version>
    
    </dependency>
    
    <dependency>
    
      <groupId>io.springfox</groupId>
    
      <artifactId>springfox-swagger2</artifactId>
    
      <version>2.9.2</version>
    
    </dependency>
    
    <dependency>
    
      <groupId>io.springfox</groupId>
    
      <artifactId>springfox-swagger-ui</artifactId>
    
      <version>2.9.2</version>
    
    </dependency>
    
    <dependency>
    
      <groupId>com.h2database</groupId>
    
      <artifactId>h2</artifactId>
    
      <scope>runtime</scope>
    
    </dependency>
    

We also have to modify our application.properties file by adding the lines
Enter fullscreen mode Exit fullscreen mode
spring.datasource.url=jdbc:h2:mem:testdb

spring.datasource.driverClassName=org.h2.Driver

spring.datasource.username=sa

spring.datasource.password=password

spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
Enter fullscreen mode Exit fullscreen mode

Now, run the code and open [http://localhost:8080/swagger-ui.html](http://localhost:8080/swagger-ui.html) page. We will see this screen

![](https://cdn-images-1.medium.com/max/2000/1*DmVIKGkSSW14pNJOcLe9-g.png)

## Demo

Let’s create a category watch, with this request body. (Note: we do not need to pass id here, it will be auto created.)

    {
      "categoryName": "watches",
      "description": "best watches",
      "imageUrl": "https://images.unsplash.com/photo-1524805444758-089113d48a6d?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=634&q=80"
    }

![](https://cdn-images-1.medium.com/max/2000/1*RRebmprvGLgUnrr53Fqr8g.png)

You will get the response as below-

![](https://cdn-images-1.medium.com/max/2000/1*w91nIxx7X0dIU_g5QQPibA.png)

Now, let us hit the get API

![](https://cdn-images-1.medium.com/max/2018/1*7rtYwUNeqt37NzAr-CyRYQ.png)

We will get the following response-

    [
      {
        "id": 1,
        "categoryName": "watches",
        "description": "best watches",
        "imageUrl": "https://images.unsplash.com/photo-1524805444758-089113d48a6d?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=634&q=80"
      }
    ]

## Enable CORS We will add the webconfig.java file, so that our [front end](https://medium.com/javarevisited/6-best-frontend-development-courses-for-beginners-to-learn-in-2021-f2772157864) can hit the API.
@Configuration
class Webconfig {
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "PUT", "POST", "PATCH", "DELETE", "OPTIONS");
}
};
}
}
view raw WebConfig.java hosted with ❤ by GitHub
@Configuration
class Webconfig {
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "PUT", "POST", "PATCH", "DELETE", "OPTIONS");
}
};
}
}
view raw WebConfig.java hosted with ❤ by GitHub

Hurray! We can now play with the APIs and can create some new category, update and fetch all the categories.

Designing the Product API

Now we have some categories, it is time to make the products APIs. First, we will create the model, then we will create the repository, then we will make the service and at the end, we will create the controller and test it.

Model

Product will have id, name, imageURL, price, description as well as a foreign key to category, as every product belong to a category.

@Entity
@Table(name = "products")
public class Product {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private @NotNull String name;
    private @NotNull String imageURL;
    private @NotNull double price;
    private @NotNull String description;

    @JsonIgnore
    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "category_id", nullable = false)
    Category category;


    public Product(String name, String imageURL, double price, String description, Category category) {
        super();
        this.name = name;
        this.imageURL = imageURL;
        this.price = price;
        this.description = description;
        this.category = category;
    }
// setters and getters
}
Enter fullscreen mode Exit fullscreen mode

Repository

Next, we will create a file, ProductRepository.java in repository package, which will just extend JpaRepository. If we need some methods, we will add it later

package com.educative.ecommerce.repository;

import com.educative.ecommerce.model.Product;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface ProductRepository extends JpaRepository&lt;Product, Integer&gt; {

}
Enter fullscreen mode Exit fullscreen mode

Service

Now we are ready to create the service class. Create a file ProductService.java in service directory. It will have an autowired ProductRepository.

@Service
public class ProductService {

@Autowired
    private ProductRepository productRepository;
}
Enter fullscreen mode Exit fullscreen mode

DTO concept#

Before creating a product, we need to understand, what is a DTO (data transfer object)

Martin Fowler introduced the concept of a Data Transfer Object (DTO) as an object that carries data between processes.

In category controller, we directly used the model as request body, but that is not practical in many cases. We need to create a different object because

  1. sometimes we might have to change the model, and we do not want to change the API for backward compatibility

  2. We can’t use the model as request body if it has relationship with another model.

So quickly, we will create a package dto and inside the package we will create another package product, and there we will create our ProductDto.java class, which will have the following attributes

private Integer id;
private @NotNull String name;
private @NotNull String imageURL;
private @NotNull double price;
private @NotNull String description;
private @NotNull Integer categoryId;
Enter fullscreen mode Exit fullscreen mode

We are also passing categoryId, because we need this to link a product with a category.

Controller

Now as we have the productDto ready, now time to create ProductController.java class

@RestController
@RequestMapping("/product")
public class ProductController {

@Autowired
    ProductService productService;
    @Autowired
    CategoryService categoryService;
}
Enter fullscreen mode Exit fullscreen mode

It will autowire ProductService and CategoryService

Create a new product API

@PostMapping("/add")
    public ResponseEntity&lt;ApiResponse&gt; addProduct(@RequestBody ProductDto productDto) {
        Optional&lt;Category&gt; optionalCategory = categoryService.readCategory(productDto.getCategoryId());
        if (!optionalCategory.isPresent()) {
            return new ResponseEntity&lt;ApiResponse&gt;(new ApiResponse(false, "category is invalid"), HttpStatus.CONFLICT);
        }
        Category category = optionalCategory.get();
        productService.addProduct(productDto, category);
        return new ResponseEntity&lt;&gt;(new ApiResponse(true, "Product has been added"), HttpStatus.CREATED);
    }
Enter fullscreen mode Exit fullscreen mode

We will receive categoryId and product details from the request body.

First, we will check if the categoryId is valid or return “category is invalid” error.

Then we will create a product by calling method, productService.addProduct which takes productDto and category as arguments.

public void addProduct(ProductDto productDto, Category category) {
        Product product = getProductFromDto(productDto, category);
        productRepository.save(product);
    }

public static Product getProductFromDto(ProductDto productDto, Category category) {
        Product product = new Product();
        product.setCategory(category);
        product.setDescription(productDto.getDescription());
        product.setImageURL(productDto.getImageURL());
        product.setPrice(productDto.getPrice());
        product.setName(productDto.getName());
        return product;
    }
Enter fullscreen mode Exit fullscreen mode

The complete code can be found in the GitHub repository given below-
GitHub — webtutsplus/ecommerce at product-apis

And this marks the end of this tutorial. But wait! The tutorial series will continue for building the UI using Vue.js for the above-developed backend application. Till that, stay tuned!

Happy Learning

Continue to the next tutorial, where we will use the API to make a frontend using vue.js

https://javascript.plainenglish.io/lets-develop-an-e-commerce-application-from-scratch-using-spring-boot-and-vue-js-aca33bd76517

Enter fullscreen mode Exit fullscreen mode
文章来源:https://dev.to/nilmadhabmondal/lets-develop-an-e-commerce-application-from-scratch-using-java-and-spring-28go
PREV
六个月内找到一份软件工作!
NEXT
使用 Vite 代替 CRA 开发 React 应用