第二章:水果商品API接口-增删改查的实现:
这里数据存储在MySQL中,符合大多数的应用场景,首先需要先将必需的依赖进行导入:
<!-- Spring Boot Web依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Boot JPA依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- MySQL驱动依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
</dependency>
PS:若出现mysql报错,则大概率是连接问题,请检查是否允许所有主机进行连接、Mysql驱动依赖是否相对应、application配置文件是否正确等。这里我使用的是Mysql8.0.26的版本,如有需要,请做适当修改。
在application.properties中配置Mysql数据源:
spring.datasource.url=jdbc:mysql://localhost:3306/数据库
spring.datasource.username=用户名
spring.datasource.password=密码
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
生成对应表格的sql如下:
CREATE TABLE fruit (
id INT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
name VARCHAR(50) NOT NULL COMMENT '水果名称',
description TEXT COMMENT '水果描述',
price DECIMAL(10, 2) NOT NULL COMMENT '价格',
category VARCHAR(50) NOT NULL COMMENT '水果类别',
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='水果信息表';
注释说明:
id:主键ID
name:水果名称
description:水果描述
price:价格
category:水果类别
PRIMARY KEY (id):将 id 字段设为主键
ENGINE=InnoDB:使用 InnoDB 存储引擎
DEFAULT CHARSET=utf8mb4:设置默认字符集为 utf8mb4
COMMENT='水果信息表':对表进行注释,说明该表的作用。
(PS:使用Navicat可以实现数据的自动生成。)
接着,我们创建一个名为Fruit的实体类,并使用@Entity注解将其标记为JPA实体类。该类包含了水果商品的id、名称、描述、价格和库存属性,并通过getter和setter方法对其进行了封装。
@Entity
public class Fruit {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String description;
private BigDecimal price;
private Integer category;
// 自行生成getter和setter方法
}
@Entity注解的作用是用来标识Fruit类是一个JPA实体类,也就是将Fruit类映射到数据库中的表,在JPA中,每一个实体类对应着数据库中的一张表,也就是说:这里我创建了一个Fruit类,给F类标识为JPA实体类,name在数据库中就对应着一张表,其中id、name、description等变量都对应成数据库中表的字段。@Entity这个注解就是用来表示这个类对应的是哪张表。
@Id注解则是用来标识一个属性为实体的唯一标识符,对应着数据库中的主键。在JPA中,每一个实体类都必须有一个@Id注解标识的属性来表示它在数据库中的唯一性。
@GeneratedValue注解则是用来指定主键生成策略的。在JPA中,主键可以使用多种方式生成,如自增长、UUID等,而这个注解就是用来指定生成方式的。它通常和@Id注解一起使用,用来为实体类的主键生成一个唯一的值。
JPA Repository接口工作原理、优点及实现举例
我们通过定义Fruit实体类对数据库中的表格实现映射,接下来我们进行JPA Repository接口的实现。我们在utils包下,创建一个名为FruitRepository的接口,该接口继承了JPARepository,JPARepository提供了一种通用的数据访问接口,能够实现CRUD操作和一些基于属性的查询方法,以下是JpaRepository的一些优势:
简化数据访问层的代码:提供了一些数据访问的方法,可以大大的减少开发者编写数据访问层代码层的工作量.
提供了一些基于属性的查询方法,能够根据实体类(FruitRepository)的属性自动生成查询语句,大大简化了查询语句的编写。
自定义查询:JPARespotitory接口提供了自定义查询的方法,如@Query注解和方法命名规则等方式,这些方式可以让开发者根据需求进行自定义查询。
JPA的优势巨大,在这里便不再一一赘述。总而言之,JpaRepository 接口的优势在于它可以帮助开发者快速地完成数据访问层的开发,同时提供了一些基于属性查询和自定义查询的方式,使得开发者可以更加方便地进行数据的操作。
JPA Repository详解及举例
为了方便理解,在这里我们举一个JPA接口的例子。假设我们有一个实体类User,属性包括id、name、age、email,我们需要进行常见的增删改查操作,在这里我们定义了一个UserRepository接口来继承JPARepositiry,然后,在UserRepository中进行自定义查询方法的定义,如下:
public interface UserRepository extends JpaRepository<User, Long> {
List<User> findByName(String name);
List<User> findByAgeGreaterThan(int age);
List<User> findByEmailLike(String emailPattern);
}
//定义findByxxx方法,实这些方法会根据传入参数自动生成查询语句。
接下来,我们可以自使用自动注入UserRepository对象来进行数据操作:
这里我们定义了一个Service类,Service类是业务逻辑层,通过调用UserRepository接口来实现对UserRepository实体类的操作。
@@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public User findById(Long id) {
return userRepository.findById(id).orElse(null);
}
public List<User> findByName(String name) {
return userRepository.findByName(name);
}
public List<User> findByAgeGreaterThan(int age) {
return userRepository.findByAgeGreaterThan(age);
}
public List<User> findByEmailLike(String emailPattern) {
return userRepository.findByEmailLike("%" + emailPattern + "%");
}
public User save(User user) {
return userRepository.save(user);
}
public void deleteById(Long id) {
userRepository.deleteById(id);
}
}
上面的代码中,我们在 UserService 中注入了 UserRepository 对象,然后可以调用其中定义的方法来进行数据操作。例如,findById 方法调用了 JpaRepository 中提供的 findById 方法,findByName 方法调用了自定义的 findByName 方法等。这些方法可以方便地完成常见的数据操作。
简单来说就是:User类提供了数据库映射,UR接口继承了JPA提供的接口,简化了数据操作。Service类则实现了通过User实体类,进而实现对数据库的增删改查操作。
+----------------+ +---------------------+
| User | | UserRepository |
+----------------+ +---------------------+
| id | | findAll() |
| name | | findById() |
| email | 关联关系 | save() |
| password | ------------> | delete() |
+----------------+ +---------------------+
| |
| |
| |
| |
| |
| |
+----------------+ +---------------------+
| UserService | | 数据库 |
+----------------+ +---------------------+
| getUserById() | | user_table |
| saveUser() | 关联关系 | id |
| deleteUser() | ------------> | name |
+----------------+ | email |
| password |
+---------------------+
在这个示意图中,User 类和 UserRepository 接口之间的关系是通过 JPA 实现的。User 类通过注解来与数据库中的表进行映射,并且定义了一些实体类的属性和方法。UserRepository 接口继承了 JpaRepository 接口,并且提供了一些基本的数据访问方法,例如 findAll()、findById()、save() 和 delete() 等。
而 UserService 类作为应用程序的服务类,通过调用 UserRepository 中的方法,来对 User 实体类进行操作。例如,getUserById() 方法可能会调用 UserRepository 的 findById() 方法,来查询一个 User 对象,并且对查询结果进行一些逻辑处理后返回给调用方。这样,User 类、UserRepository 接口和 UserService 类就可以协同工作,来完成应用程序中的数据操作和业务逻辑处理。
FruitRepository接口实现
至此,我们大概也就理解了为何要创建FruitRepository接口来继承JPARepository。
下面我们开始编写FruitRepository接口:
package com.example.springrestudy.utils;
import com.example.springrestudy.bean.Fruit;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface FruitRepository extends JpaRepository <Fruit,Long>{
List<Fruit> findAll();
}
JpaRepository <Fruit,Long>是一个带有泛型的接口,泛型是可以用来创建具有不同数据类型的类、接口和方法的机制,使用泛型使得代码更为灵活,daima的复用性和可读性更高。
这里传入的第一泛型是实体类,第二泛型是参实体类主键的数据类型,第一泛型较好理解,但第二泛型若多个数据类型同为long,如何解决该问题?
事实上,在Fruit类中已经解决了该问题了,如下:
@Entity
public class Fruit {
@Id //这里的Id注解便将id作为Fruit类中的实体类主键,因此,即使有第二个类型为long的字段也不影响。
}
接着,我们编写FruitController类,在controller包下创建名为FruitController类,用来处理Http请求,
在一个典型的 Spring MVC 应用程序中,控制器类(Controller)负责处理 HTTP 请求,并根据请求的内容调用适当的服务或处理逻辑,然后返回 HTTP 响应。
这里我们先以Get请求返回所有数据来进行演示:
package com.example.springrestudy.controller;
import com.example.springrestudy.bean.Fruit;
import com.example.springrestudy.utils.FruitRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/api/fruit")
public class FruitController {
@Autowired
private FruitRepository fruitRepository;
@GetMapping("/findAll")
public List<Fruit> getAllFruits(){
return fruitRepository.findAll();
}
}
我们将数据浏览器中进行请求操作,如下
在ApiPost中进行请求:
请求成功!下面开始进行增删改查的数据操作的实现:
数据添加操作:
在FruitController中创建一个POST请求处理方法,在其中使用FruitRepository中的save()方法将数据保存至数据库中。
@PostMapping("/add")
public Fruit addFruit(@RequestBody Fruit fruit){
return fruitRepository.save(fruit);
}
将json数据通过POST请求方式发送至/api/fruit/add下,即可返回响应数据。
数据查询操作:
通过定义Get查询,传入为name的参数,按照名称来进行查询,同理我们也可以通过id、category(类别)来进行查询。这就看你的具体需求了。
@GetMapping("/search")
public List<Fruit> findFruitsByName(@RequestParam("name") String name) {
return fruitRepository.findByName(name);
}
数据删除操作:
同理,删除操作也是非常简单的,此处需要注意,删除操作的请求方式为Delete,不要用Get一直尝试请求。
//删除操作,请求路径为 /fruits/{id},请求方式为 DELETE
@DeleteMapping("/{id}")
public ResponseEntity<?> deleteFruit(@PathVariable Long id) {
Fruit fruit = fruitRepository.findById(id).orElse(null);
if (fruit == null) {
return ResponseEntity.notFound().build();
}
fruitRepository.delete(fruit);
return ResponseEntity.ok().build();
}
在上述示例中,我们定义了一个名为deleteFruit的方法,并在该方法上使用了@DeleteMapping注解。该注解表示请求方式为DELETE,并且路径为/fruits/{id},其中{id}表示要删除的水果商品的id。
在方法中,我们通过FruitRepository的findById方法查询指定id的水果商品,如果查询到了,则使用FruitRepository的delete方法删除该商品。最后,我们使用ResponseEntity.ok()方法返回删除成功的响应。如果查询不到指定id的水果商品,我们则返回一个404响应。
这里我们尝试删除刚添加的id为1005的商品,测试成功,该id商品已被删除。
数据修改操作:
在SpringBoot中,数据修改的工作流程如下:
接收请求:通过Controller层接收POST请求,请求参数包含要修改的数据以及修改后的值。
数据验证:对请求参数进行校验,确保传入的数据格式正确。
调用JPA接口:通过调用对应的JPA接口,执行数据修改操作。JPA接口中的方法名必须符合JPA规范,以便框架自动转换为对应的SQL语句。
数据库操作:JPA会将方法名转换为对应的SQL语句,并通过JDBC执行操作。
返回响应:操作完成后,将操作结果封装成响应对象,通过Controller层返回给客户端。
我们以id为1006的peach为例,将价格修改为8.99
首先需要编写JPA接口方法,在实际开发中,会遇到各种复杂的查询或者数据操作,因此JPA所提供的方法并无法满足数据操作的需求,因此,我们需要自行编写方法:
在FruitRepository接口中进行如下daima编写:
@Transactional
@Modifying //指定该方法为执行修改操作
@Query("UPDATE Fruit f SET f.price = :newPrice WHERE f.id = :id")
//@Query注解的作用是指定该方法执行SQL语句,在此为更新水果价格。
int updatePriceById(@Param("id") Long id, @Param("newPrice") BigDecimal newPrice);
控制器类中调用该updatePriceById方法:
@PostMapping("/change/{id}") //使用{id}作为路径参数。
//声明changeFruitPrice方法,使用@PathVariable获取路径参数{id},使用@RequestBody获取请求体中的JSON数据。
public ResponseEntity<String> changeFruitPrice(@PathVariable("id") Long id, @RequestBody Map<String, Object> body) {
//获取JSON数据中的newPrice,并将其转换为BigDecimal类型。
BigDecimal newPrice = new BigDecimal(body.get("newPrice").toString());
//调用FruitRepository的updatePriceById方法,更新水果的价格。
int result = fruitRepository.updatePriceById(id, newPrice);
//如果更新失败,则返回状态码为404,响应消息为"Fruit not found."。
if (result == 0) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body("Fruit not found.");
}
//更新成功后,返回状态码为200,响应消息为"Fruit price updated successfully."。
return ResponseEntity.ok("Fruit price updated successfully.");
}
通过APIPost进行调用测试:请求成功!数据已被修改。
数据修改部分需要注意:
因为是通过json进行传输数据,而json是字符串类型的,因此需要将数据类型转化为BigDecimal,也就是符合数据库存储类型的数据类型。
在进行POST请求时,注意@PathVariable注解绑定了id,因此在传参是必须将id传入到URL中,而price需要通过json格式进行传输。
至此,我们的商品的增删改查接口也就已经完成了!是不是不难呀~其实只要掌握了注解的使用方法、JPA接口的使用方法、实体类对数据库表格的映射,增删改查就变得非常简单了。
第三章我们开始讲解如何通过VUE,结合element-ui,以最简单的方式,将我们的数据渲染至前端页面上!