我有一个使用 PostgreSQL 和 SwaggerUI 的 Quarkus 项目,在该项目中我可以手动从 IntelliJ DB UI 中的表中删除行,但是当我尝试使用 DELETE 请求执行此操作时,它会失败,并且不会引发任何异常或错误代码。奇怪的是,如果我尝试删除的行没有通过任何 FK 连接到另一个表,则请求实际上会执行并删除该行。然而,即使有一些 FK,这也不应该成为问题,因为我可以手动完成。我还尝试使用 Postman 删除请求,并得到相同的结果 - 默默地失败。我将提供我的实体、服务、存储库和资源类。需要明确的是,GameEntity 中有 4 个 FK 字段,但我只会提供其中之一(流派),因为它们的行为都是相同的。我正在尝试从 Games 表中删除。
编辑:我查看了我的休眠日志,似乎它做了很多选择,但最后它没有执行删除。此外,如上所述,当实体与另一个实体没有连接时,它会被删除,并且可以在日志中看到删除子句。
删除没有连接的实体:
[Hibernate]
select
de1_0.id,
de1_0.developer,
de1_0.email
from
Developers de1_0
[Hibernate]
select
g1_0.developerID,
g1_0.id,
g1_0.description,
g1_0.genreID,
g2_0.id,
g2_0.genre,
g1_0.platformID,
p1_0.id,
p1_0.platform,
g1_0.publisherID,
p2_0.id,
p2_0.email,
p2_0.publisher,
g1_0.release_date,
g1_0.title
from
Games g1_0
left join
Genres g2_0
on g2_0.id=g1_0.genreID
left join
Platforms p1_0
on p1_0.id=g1_0.platformID
left join
Publishers p2_0
on p2_0.id=g1_0.publisherID
where
g1_0.developerID = any (?)
[Hibernate]
select
de1_0.id,
de1_0.developer,
de1_0.email,
g1_0.developerID,
g1_0.id,
g1_0.description,
g1_0.genreID,
g2_0.id,
g2_0.genre,
g1_0.platformID,
p1_0.id,
p1_0.platform,
g1_0.publisherID,
p2_0.id,
p2_0.email,
p2_0.publisher,
g1_0.release_date,
g1_0.title
from
Developers de1_0
left join
Games g1_0
on de1_0.id=g1_0.developerID
left join
Genres g2_0
on g2_0.id=g1_0.genreID
left join
Platforms p1_0
on p1_0.id=g1_0.platformID
left join
Publishers p2_0
on p2_0.id=g1_0.publisherID
where
de1_0.id=?
[Hibernate]
delete
from
Developers
where
id=?
删除具有连接的实体:
[Hibernate]
select
ge1_0.id,
ge1_0.description,
ge1_0.developerID,
d1_0.id,
d1_0.developer,
d1_0.email,
ge1_0.genreID,
g1_0.id,
g1_0.genre,
ge1_0.platformID,
p1_0.id,
p1_0.platform,
ge1_0.publisherID,
p2_0.id,
p2_0.email,
p2_0.publisher,
ge1_0.release_date,
ge1_0.title,
r1_0.gameID,
r1_0.id,
r1_0.comment,
r1_0.rating,
r1_0.userID,
u1_0.id,
u1_0.email,
u1_0.username
from
Games ge1_0
join
Developers d1_0
on d1_0.id=ge1_0.developerID
join
Genres g1_0
on g1_0.id=ge1_0.genreID
join
Platforms p1_0
on p1_0.id=ge1_0.platformID
join
Publishers p2_0
on p2_0.id=ge1_0.publisherID
left join
Reviews r1_0
on ge1_0.id=r1_0.gameID
left join
Users u1_0
on u1_0.id=r1_0.userID
where
ge1_0.id=?
[Hibernate]
select
r1_0.userID,
r1_0.id,
r1_0.comment,
r1_0.gameID,
g1_0.id,
g1_0.description,
g1_0.developerID,
d1_0.id,
d1_0.developer,
d1_0.email,
g1_0.genreID,
g2_0.id,
g2_0.genre,
g1_0.platformID,
p1_0.id,
p1_0.platform,
g1_0.publisherID,
p2_0.id,
p2_0.email,
p2_0.publisher,
g1_0.release_date,
g1_0.title,
r1_0.rating
from
Reviews r1_0
left join
Games g1_0
on g1_0.id=r1_0.gameID
left join
Developers d1_0
on d1_0.id=g1_0.developerID
left join
Genres g2_0
on g2_0.id=g1_0.genreID
left join
Platforms p1_0
on p1_0.id=g1_0.platformID
left join
Publishers p2_0
on p2_0.id=g1_0.publisherID
where
r1_0.userID=?
[Hibernate]
select
g1_0.publisherID,
g1_0.id,
g1_0.description,
g1_0.developerID,
d1_0.id,
d1_0.developer,
d1_0.email,
g1_0.genreID,
g2_0.id,
g2_0.genre,
g1_0.platformID,
p1_0.id,
p1_0.platform,
g1_0.release_date,
g1_0.title
from
Games g1_0
left join
Developers d1_0
on d1_0.id=g1_0.developerID
left join
Genres g2_0
on g2_0.id=g1_0.genreID
left join
Platforms p1_0
on p1_0.id=g1_0.platformID
where
g1_0.publisherID=?
[Hibernate]
select
g1_0.platformID,
g1_0.id,
g1_0.description,
g1_0.developerID,
d1_0.id,
d1_0.developer,
d1_0.email,
g1_0.genreID,
g2_0.id,
g2_0.genre,
g1_0.publisherID,
p2_0.id,
p2_0.email,
p2_0.publisher,
g1_0.release_date,
g1_0.title
from
Games g1_0
left join
Developers d1_0
on d1_0.id=g1_0.developerID
left join
Genres g2_0
on g2_0.id=g1_0.genreID
left join
Publishers p2_0
on p2_0.id=g1_0.publisherID
where
g1_0.platformID=?
[Hibernate]
select
g1_0.genreID,
g1_0.id,
g1_0.description,
g1_0.developerID,
d1_0.id,
d1_0.developer,
d1_0.email,
g1_0.platformID,
p1_0.id,
p1_0.platform,
g1_0.publisherID,
p2_0.id,
p2_0.email,
p2_0.publisher,
g1_0.release_date,
g1_0.title
from
Games g1_0
left join
Developers d1_0
on d1_0.id=g1_0.developerID
left join
Platforms p1_0
on p1_0.id=g1_0.platformID
left join
Publishers p2_0
on p2_0.id=g1_0.publisherID
where
g1_0.genreID=?
[Hibernate]
select
g1_0.developerID,
g1_0.id,
g1_0.description,
g1_0.genreID,
g2_0.id,
g2_0.genre,
g1_0.platformID,
p1_0.id,
p1_0.platform,
g1_0.publisherID,
p2_0.id,
p2_0.email,
p2_0.publisher,
g1_0.release_date,
g1_0.title
from
Games g1_0
left join
Genres g2_0
on g2_0.id=g1_0.genreID
left join
Platforms p1_0
on p1_0.id=g1_0.platformID
left join
Publishers p2_0
on p2_0.id=g1_0.publisherID
where
g1_0.developerID=?
@Entity(name = "Games")
public class GameEntity extends PanacheEntity {
@Column(length = 100, nullable = false, unique = true)
@NotBlank(message = "Title is required")
private String title;
@Column(length = 1000)
private String description;
@Column(name = "release_date")
@Past(message = "Release date must be in the past or present")
private LocalDate releaseDate;
@ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@OnDelete(action = OnDeleteAction.CASCADE)
@JoinColumn(name = "genreID", nullable = false)
@NotNull(message = "Genre is required")
private GenreEntity genre;
@ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@OnDelete(action = OnDeleteAction.CASCADE)
@JoinColumn(name = "publisherID", nullable = false)
@NotNull(message = "Publisher is required")
private PublisherEntity publisher;
@ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@OnDelete(action = OnDeleteAction.CASCADE)
@JoinColumn(name = "developerID", nullable = false)
@NotNull(message = "Developer is required")
private DeveloperEntity developer;
@ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@OnDelete(action = OnDeleteAction.CASCADE)
@JoinColumn(name = "platformID", nullable = false)
@NotNull(message = "Platform is required")
private PlatformEntity platform;
@OneToMany(mappedBy = "game", fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
private List<ReviewEntity> reviews;
/// getters, setters, toString
}
@Entity(name = "Genres")
public class GenreEntity extends PanacheEntity {
@Column(length = 50, nullable = false, unique = true)
@NotBlank(message = "Genre name is required")
private String genre;
@OneToMany(mappedBy = "genre", fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
private List<GameEntity> games;
/// getters, setters, toString
}
@ApplicationScoped
public class GameService {
@Inject
GameRepository repository;
@Inject
GenreRepository genreRepository;
@Inject
PublisherRepository publisherRepository;
@Inject
DeveloperRepository developerRepository;
@Inject
PlatformRepository platformRepository;
public List<GameDTO> getAll() {
return repository.listAll() // Optional sorting
.stream()
.map(this::mapToDto)
.toList();
}
@Transactional
public void create(GameDTO dto) {
GameEntity entity = mapToEntity(dto);
repository.persist(entity);
}
@Transactional
public void update(Long id, GameDTO dto) {
GameEntity updatedEntity = mapToEntity(dto);
GameEntity entity = repository.findById(id);
if (entity == null)
throw new NotFoundException("Game not found with id: " + id);
entity.setTitle(updatedEntity.getTitle());
entity.setDescription(updatedEntity.getDescription());
entity.setReleaseDate(updatedEntity.getReleaseDate());
entity.setGenre(updatedEntity.getGenre());
entity.setPublisher(updatedEntity.getPublisher());
entity.setDeveloper(updatedEntity.getDeveloper());
entity.setPlatform(updatedEntity.getPlatform());
repository.persist(entity);
}
@Transactional
public void delete(Long id) {
GameEntity entity = repository.findById(id);
if (entity == null)
throw new NotFoundException("Game not found with id: " + id);
repository.delete(entity);
}
private GameEntity mapToEntity(GameDTO dto) {
GenreEntity genre = genreRepository.findByGenre(dto.getGenre());
if (genre == null)
throw new NotFoundException("Genre not found.");
PublisherEntity publisher = publisherRepository.findByPublisher(dto.getPublisher());
if (publisher == null)
throw new NotFoundException("Publisher not found.");
DeveloperEntity developer = developerRepository.findByDeveloper(dto.getDeveloper());
if (developer == null)
throw new NotFoundException("Developer not found.");
PlatformEntity platform = platformRepository.findByPlatform(dto.getPlatform());
if (platform == null)
throw new NotFoundException("Platform not found.");
GameEntity entity = new GameEntity();
entity.setTitle(dto.getTitle());
entity.setDescription(dto.getDescription());
entity.setReleaseDate(dto.getReleaseDate());
entity.setGenre(genre);
entity.setPublisher(publisher);
entity.setDeveloper(developer);
entity.setPlatform(platform);
return entity;
}
private GameDTO mapToDto(GameEntity e) {
return new GameDTO(
e.getID(),
e.getTitle(),
e.getDescription(),
e.getReleaseDate(),
e.getGenre().getGenre(),
e.getPublisher().getPublisher(),
e.getDeveloper().getDeveloper(),
e.getPlatform().getPlatform()
);
}
}
@ApplicationScoped
@Path("api/v1/games")
public class GameResource {
@Inject
GameService service;
@GET
public Response getAll() {
List<GameDTO> list = service.getAll();
return Response
.ok(list)
.build();
}
@POST
public Response create(GameDTO dto) {
service.create(dto);
return Response
.status(Response.Status.CREATED)
.entity(dto)
.build();
}
@PUT
@Path("/{id}")
public Response update(@PathParam("id") Long id, GameDTO dto) {
service.update(id, dto);
return Response
.ok(dto)
.build();
}
@DELETE
@Path("/{id}")
public Response delete(@PathParam("id") Long id) {
service.delete(id);
return Response
.noContent()
.build();
}
}
解决方案是使用本机查询,它绕过 hibernate 所做的所有加载并直接执行删除命令:
@Transactional
public void delete(Long id) {
long deleted = repository.delete("id", id);
if (deleted == 0) {
throw new NotFoundException("Game not found with id: " + id);
}
}