Spring Boot - JPA 或 SQL 查询在连接表中查找最常见的项目?
Posted
技术标签:
【中文标题】Spring Boot - JPA 或 SQL 查询在连接表中查找最常见的项目?【英文标题】:Spring Boot - JPA or SQL Query to find Most Common Item in Joined Tables? 【发布时间】:2020-06-21 18:08:07 【问题描述】:我正在使用 Spring Boot、JPA 和 mysql 创建一个 Spring Boot 微服务,它由以下实体关系组成:
Owner can have multiple Cars.
Cars can only have one Owner.
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.myapi</groupId>
<artifactId>car-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>car-api</name>
<description>Car REST API</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</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>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
所有者实体:
@Entity
@Table(name = "owner")
public class Owner extends AuditModel
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotNull
private String name;
private String address,
private String city;
private String state;
private int zipCode;
@OneToMany(cascade = CascadeType.ALL,
fetch = FetchType.EAGER,
mappedBy = "owner")
private List<Car> cars = new ArrayList<>();
public Owner()
// Getter & Setters omitted for brevity.
汽车实体:
@Entity
@Table(name="car")
public class Car extends AuditModel
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
String make;
String model;
String year;
@JsonIgnore
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "owner_id", nullable = false)
private Owner owner;
// Getter & Setters omitted for brevity.
所有者存储库:
@Repository
public interface OwnerRepository extends JpaRepository<Owner, Long>
@Query(value = "SELECT * FROM owner WHERE name = ?", nativeQuery = true)
Owner findOwnerByName(String name);
汽车存储库:
@Repository
public interface CarRepository extends JpaRepository<Car, Long>
所有者服务:
public interface OwnerService
List<Owner> getAllOwners();
OwnerServiceImpl:
@Service
public class OwnerServiceImpl implements OwnerService
@Autowired
OwnerRepository ownerRepository;
@Override
public List<Owner> getAllOwners()
return ownerRepository.findAll();
所有者控制器:
@RestController
public class OwnerController
private HttpHeaders headers = null;
@Autowired
OwnerService ownerService;
public OwnerController()
headers = new HttpHeaders();
headers.add("Content-Type", "application/json");
@RequestMapping(value = "/owners" , method = RequestMethod.GET, produces = "APPLICATION/JSON")
public ResponseEntity<Object> getAllOwners()
List<Owner> owners = ownerService.getAllOwners();
if (owners.isEmpty())
return new ResponseEntity<Object>(HttpStatus.NOT_FOUND);
return new ResponseEntity<Object>(owners, headers, HttpStatus.OK);
所以,当我使用以下方式调用此端点时:
GET http://localhost:8080/car-api/owners
产生以下 JSON 负载:
[
"id": 1,
"name": "Tom Brady",
"cars": [
"id": 1,
"make": "Honda",
"model": "Accord",
"year": "2020"
,
"id": 11,
"make": "Nissan",
"model": "Maxima",
"year": "2019"
,
"id": 12,
"make": "Porsche",
"model": "911",
"year": "2017"
]
,
"id": 2,
"name": "Kobe Bryant",
"cars": [
"id": 2,
"make": "Porsche",
"model": "911",
"year": "2017"
]
,
"id": 3,
"name": "Mike Tyson",
"cars": [
"id": 3,
"make": "Volkswagen",
"model": "Beatle",
"year": "1973"
]
,
"id": 4,
"name": "Scottie Pippen",
"cars": [
"id": 4,
"make": "Ford",
"model": "F-150",
"year": "2010"
]
,
"id": 5,
"name": "John Madden",
"cars": [
"id": 5,
"make": "Chevrolet",
"model": "Silverado",
"year": "2020"
]
,
"id": 6,
"name": "Arnold Palmer",
"cars": [
"id": 6,
"make": "Toyota",
"model": "Camary",
"year": "2018"
]
,
"id": 7,
"name": "Tiger Woods",
"cars": [
"id": 7,
"make": "Alfa",
"model": "Romeo",
"year": "2017"
]
,
"id": 8,
"name": "Magic Johnson",
"cars": [
"id": 8,
"make": "Porsche",
"model": "911",
"year": "2017"
]
,
"id": 9,
"name": "George Foreman",
"cars": [
"id": 9,
"make": "Toyota",
"model": "Camary",
"year": "2018"
]
,
"id": 10,
"name": "Charles Barkley",
"cars": [
"id": 10,
"make": "Alfa",
"model": "Romeo",
"year": "2017"
]
]
如您所见,汤姆·布雷迪、科比·布莱恩特、魔术师约翰逊拥有所有保时捷 911……这是此列表中最常见的汽车。
在幕后,JPA 创建如下表:
汽车桌:
CREATE TABLE `car` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`battery_capacity` int(11) NOT NULL,
`make` varchar(255) DEFAULT NULL,
`model` varchar(255) DEFAULT NULL,
`year` varchar(255) DEFAULT NULL,
`owner_id` bigint(20) NOT NULL,
PRIMARY KEY (`id`),
KEY `FK6wxv6hdekqn26n47pb7f1dt02` (`user_id`),
CONSTRAINT `FK6wxv6hdekqn26n47pb7f1dt02` FOREIGN KEY (`owner_id`) REFERENCES `owner` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=latin1;
所有者表:
CREATE TABLE `owner` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`address` varchar(255) DEFAULT NULL,
`city` varchar(255) DEFAULT NULL,
`name` varchar(255) NOT NULL,
`state` varchar(255) DEFAULT NULL,
`zip_code` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=latin1;
SELECT * FROM OWNER
:
+----+----------------+
| id | name |
+----+----------------+
| 1 | Tom Brady |
| 2 | Kobe Bryant |
| 3 | Mike Tyson |
| 4 | Scottie Pippen |
| 5 | John Madden |
| 6 | Arnold Palmer |
| 7 | Tiger Woods |
| 8 | Magic Johnson |
| 9 | George Foreman |
| 10 | Charles Barkley|
+----+----------------+
SELECT id, user_id, c.make, c.model, c.year FROM cars c;
+----------------+--------------+------+
| id | make | model | year |
+----+-----------+-----------+---------+
| 1 | Honda | Accord | 2020 |
| 2 | Nissan | Leaf | 2029 |
| 3 | Volkswagen| Beatle | 1973 |
| 4 | Porsche | Taycan | 2020 |
| 5 | Ford | F-150 | 2010 |
| 6 | Chevrolet | Silverado | 2020 |
| 7 | Toyota | Camry | 2018 |
| 8 | Chevrolet | Bolt | 2020 |
| 9 | Honda | Clarity | 2018 |
| 10 | Hyundai | Ioniq | 2017 |
| 11 | Nissan | Maxima | 2019 |
| 12 | Porsche | 911 | 2020 |
+----+-----------+--------------+------+
什么是 JPA 或 SQL 查询(甚至可以是命名查询),我可以放置在我的存储库中,它会返回带有最常见汽车的数据(例如 Select count(*) 和可能的 Group By 语句),其中会表明拥有最多的汽车是保时捷 911,因为 3 个人拥有它们?
【问题讨论】:
【参考方案1】:声明一个用于保存car
及其计数的类。
public class CarStatistics
private final Car car;
private final Long count;
public CarStatistics(Car car, Long count)
this.car = car;
this.count = count;
public Car getCar()
return car;
public Long getCount()
return count;
从存储库方法中返回一个 bean 实例,按计数分组和排序:
public interface CarRepository extends JpaRepository<Car, Long>
@Query("SELECT new com.example.CarStatistics(c, COUNT(c)) "
+ " FROM Car c "
+ " GROUP BY c.make, c.model "
+ " ORDER BY COUNT(c) DESC")
List<CarStatistics> findCarCount();
调用存储库方法并获取结果集中的第一个对象:
@Service
public class CarService
private final CarRepository carRepository;
public CarService(CarRepository carRepository)
this.carRepository = carRepository;
public Car findMostCommonCar()
List<CarStatistics> carStatistics = carRepository.findCarCount();
return carStatistics.get(0).getCar();
【讨论】:
谢谢@Evgeniy Khyst...很快就会尝试。在阅读您的解决方案之前,我实际上找到了一个不同的解决方案。以上是关于Spring Boot - JPA 或 SQL 查询在连接表中查找最常见的项目?的主要内容,如果未能解决你的问题,请参考以下文章
Spring Boot - JPA 或 SQL 查询在连接表中查找最常见的项目?
spring boot(十五)spring boot+thymeleaf+jpa增删改查示例
Spring Boot (十五): Spring Boot + Jpa + Thymeleaf 增删改查示例
spring boot2+jpa+thymeleaf增删改查例子