这是我第一次将 Hibernate ORM 集成到我的项目中,我在尝试调试
org.hibernate.UnresolvableObjectException: No row with the given identifier exists
异常时遇到了一些麻烦,我不完全确定这是因为我错误地注释了我的类,还是因为我我正在做一些完全错误的事情。
[09:19:38 INFO]: [STDOUT] [org.hibernate.engine.jdbc.spi.SqlStatementLogger] Hibernate: select am1_0.id,a1_0.arena_manager_id,a1_0.name,a1_0.id,a1_0.car_part_locations,a1_0.corners,a1_0.schematic,a1_0.spawn,a1_0.truck from arena_manager am1_0 left join arena a1_0 on am1_0.id=a1_0.arena_manager_id where am1_0.id=?
[09:19:38 ERROR]: [Murder Run] Exception while handling click event in inventory 'Create or modify a lobby', slot=16, item=GREEN_WOOL
org.hibernate.UnresolvableObjectException: No row with the given identifier exists: [io.github.pulsebeat02.murderrun.game.arena.ArenaManager#5]
at org.hibernate.UnresolvableObjectException.throwIfNull(UnresolvableObjectException.java:49) ~[?:?]
at org.hibernate.event.internal.DefaultRefreshEventListener.refresh(DefaultRefreshEventListener.java:201) ~[?:?]
at org.hibernate.event.internal.DefaultRefreshEventListener.refresh(DefaultRefreshEventListener.java:184) ~[?:?]
at org.hibernate.event.internal.DefaultRefreshEventListener.onRefresh(DefaultRefreshEventListener.java:109) ~[?:?]
at org.hibernate.event.internal.DefaultRefreshEventListener.onRefresh(DefaultRefreshEventListener.java:55) ~[?:?]
at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:127) ~[?:?]
at org.hibernate.internal.SessionImpl.fireRefresh(SessionImpl.java:1218) ~[?:?]
at org.hibernate.internal.SessionImpl.refresh(SessionImpl.java:1181) ~[?:?]
at MurderRun-1.21.1-v1.0.0-all-1729700029542.jar/io.github.pulsebeat02.murderrun.data.hibernate.controllers.AbstractController.serialize(AbstractController.java:51) ~[MurderRun-1.21.1-v1.0.0-all-1729700029542.jar:?]
at MurderRun-1.21.1-v1.0.0-all-1729700029542.jar/io.github.pulsebeat02.murderrun.MurderRun.updatePluginData(MurderRun.java:158) ~[MurderRun-1.21.1-v1.0.0-all-1729700029542.jar:?]
at MurderRun-1.21.1-v1.0.0-all-1729700029542.jar/io.github.pulsebeat02.murderrun.gui.lobby.LobbyModificationGui.createNewLobby(LobbyModificationGui.java:191) ~[MurderRun-1.21.1-v1.0.0-all-1729700029542.jar:?]
at com.github.stefvanschie.inventoryframework.gui.GuiItem.callAction(GuiItem.java:178) ~[?:?]
at com.github.stefvanschie.inventoryframework.pane.PatternPane.click(PatternPane.java:216) ~[?:?]
at com.github.stefvanschie.inventoryframework.gui.InventoryComponent.click(InventoryComponent.java:205) ~[?:?]
at com.github.stefvanschie.inventoryframework.gui.type.ChestGui.click(ChestGui.java:161) ~[?:?]
at com.github.stefvanschie.inventoryframework.gui.GuiListener.onInventoryClick(GuiListener.java:84) ~[?:?]
at com.destroystokyo.paper.event.executor.asm.generated.GeneratedEventExecutor85.execute(Unknown Source) ~[?:?]
at org.bukkit.plugin.EventExecutor$2.execute(EventExecutor.java:77) ~[paper-mojangapi-1.21.1-R0.1-SNAPSHOT.jar:?]
at co.aikar.timings.TimedEventExecutor.execute(TimedEventExecutor.java:80) ~[paper-mojangapi-1.21.1-R0.1-SNAPSHOT.jar:1.21.1-128-d348cb8]
at org.bukkit.plugin.RegisteredListener.callEvent(RegisteredListener.java:70) ~[paper-mojangapi-1.21.1-R0.1-SNAPSHOT.jar:?]
at io.papermc.paper.plugin.manager.PaperEventManager.callEvent(PaperEventManager.java:54) ~[paper-1.21.1.jar:1.21.1-128-d348cb8]
at io.papermc.paper.plugin.manager.PaperPluginManagerImpl.callEvent(PaperPluginManagerImpl.java:131) ~[paper-1.21.1.jar:1.21.1-128-d348cb8]
at org.bukkit.plugin.SimplePluginManager.callEvent(SimplePluginManager.java:628) ~[paper-mojangapi-1.21.1-R0.1-SNAPSHOT.jar:?]
at net.minecraft.server.network.ServerGamePacketListenerImpl.handleContainerClick(ServerGamePacketListenerImpl.java:3224) ~[paper-1.21.1.jar:1.21.1-128-d348cb8]
at net.minecraft.network.protocol.game.ServerboundContainerClickPacket.handle(ServerboundContainerClickPacket.java:69) ~[paper-1.21.1.jar:1.21.1-128-d348cb8]
at net.minecraft.network.protocol.game.ServerboundContainerClickPacket.handle(ServerboundContainerClickPacket.java:33) ~[paper-1.21.1.jar:1.21.1-128-d348cb8]
at net.minecraft.network.protocol.PacketUtils.lambda$ensureRunningOnSameThread$0(PacketUtils.java:56) ~[paper-1.21.1.jar:1.21.1-128-d348cb8]
at net.minecraft.server.TickTask.run(TickTask.java:18) ~[paper-1.21.1.jar:1.21.1-128-d348cb8]
at net.minecraft.util.thread.BlockableEventLoop.doRunTask(BlockableEventLoop.java:151) ~[paper-1.21.1.jar:1.21.1-128-d348cb8]
at net.minecraft.util.thread.ReentrantBlockableEventLoop.doRunTask(ReentrantBlockableEventLoop.java:24) ~[paper-1.21.1.jar:1.21.1-128-d348cb8]
at net.minecraft.server.MinecraftServer.doRunTask(MinecraftServer.java:1535) ~[paper-1.21.1.jar:1.21.1-128-d348cb8]
at net.minecraft.server.MinecraftServer.doRunTask(MinecraftServer.java:201) ~[paper-1.21.1.jar:1.21.1-128-d348cb8]
at net.minecraft.util.thread.BlockableEventLoop.pollTask(BlockableEventLoop.java:125) ~[paper-1.21.1.jar:1.21.1-128-d348cb8]
at net.minecraft.server.MinecraftServer.pollTaskInternal(MinecraftServer.java:1512) ~[paper-1.21.1.jar:1.21.1-128-d348cb8]
at net.minecraft.server.MinecraftServer.pollTask(MinecraftServer.java:1505) ~[paper-1.21.1.jar:1.21.1-128-d348cb8]
at net.minecraft.util.thread.BlockableEventLoop.managedBlock(BlockableEventLoop.java:135) ~[paper-1.21.1.jar:1.21.1-128-d348cb8]
at net.minecraft.server.MinecraftServer.managedBlock(MinecraftServer.java:1464) ~[paper-1.21.1.jar:1.21.1-128-d348cb8]
at net.minecraft.server.MinecraftServer.waitUntilNextTick(MinecraftServer.java:1471) ~[paper-1.21.1.jar:1.21.1-128-d348cb8]
at net.minecraft.server.MinecraftServer.runServer(MinecraftServer.java:1316) ~[paper-1.21.1.jar:1.21.1-128-d348cb8]
at net.minecraft.server.MinecraftServer.lambda$spin$0(MinecraftServer.java:329) ~[paper-1.21.1.jar:1.21.1-128-d348cb8]
at java.base/java.lang.Thread.run(Thread.java:1583) ~[?:?]
对于上下文,这是我的 ArenaManager.java
package io.github.pulsebeat02.murderrun.game.arena;
import io.github.pulsebeat02.murderrun.data.hibernate.HibernateIdentifiers;
import jakarta.persistence.*;
import java.io.Serial;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.bukkit.Location;
import org.checkerframework.checker.nullness.qual.KeyFor;
import org.checkerframework.checker.nullness.qual.Nullable;
@Entity
@Table(name = "arena_manager")
public final class ArenaManager implements Serializable {
@Serial
private static final long serialVersionUID = -2378194945450834205L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id = HibernateIdentifiers.ARENA_MANAGER_ID;
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@MapKeyColumn(name = "name")
@JoinColumn(name = "arena_manager_id")
@Column(name = "arena")
private final Map<String, Arena> arenas;
public ArenaManager() {
this.arenas = new HashMap<>();
}
public void addArena(
final String name,
final Location[] corners,
final Location[] itemLocations,
final Location spawn,
final Location truck
) {
final ArenaSchematic schematic = ArenaSchematic.copyAndCreateSchematic(name, corners);
final Arena arena = new Arena(schematic, name, corners, itemLocations, spawn, truck);
this.arenas.put(name, arena);
}
public @Nullable Arena getArena(final String name) {
return this.arenas.get(name);
}
public void removeArena(final String name) {
this.arenas.remove(name);
}
public Map<String, Arena> getArenas() {
return this.arenas;
}
public Set<@KeyFor("this.arenas") String> getArenaNames() {
return this.arenas.keySet();
}
}
Arena.java
package io.github.pulsebeat02.murderrun.game.arena;
import io.github.pulsebeat02.murderrun.data.hibernate.HibernateIdentifiers;
import io.github.pulsebeat02.murderrun.data.hibernate.converters.LocationConverter;
import io.github.pulsebeat02.murderrun.utils.RandomUtils;
import jakarta.persistence.Column;
import jakarta.persistence.Convert;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import java.io.Serial;
import java.io.Serializable;
import org.bukkit.Location;
import org.bukkit.util.BoundingBox;
@Entity
@Table(name = "arena")
public final class Arena implements Serializable {
@Serial
private static final long serialVersionUID = -6251041532325023867L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id = HibernateIdentifiers.ARENA_ID;
@Column(name = "schematic")
private final ArenaSchematic schematic;
@Column(name = "name")
private final String name;
@Convert(converter = LocationConverter.class)
@Column(name = "corners")
private final Location[] corners;
@Convert(converter = LocationConverter.class)
@Column(name = "car_part_locations")
private final Location[] carPartLocations;
@Convert(converter = LocationConverter.class)
@Column(name = "spawn")
private final Location spawn;
@Convert(converter = LocationConverter.class)
@Column(name = "truck")
private final Location truck;
public Arena(
final ArenaSchematic schematic,
final String name,
final Location[] corners,
final Location[] carPartLocations,
final Location spawn,
final Location truck
) {
this.schematic = schematic;
this.name = name;
this.corners = corners;
this.carPartLocations = carPartLocations;
this.spawn = spawn;
this.truck = truck;
}
public String getName() {
return this.name;
}
public Location getFirstCorner() {
return this.corners[0];
}
public Location getSecondCorner() {
return this.corners[1];
}
public Location getSpawn() {
return this.spawn;
}
public Location getTruck() {
return this.truck;
}
public Location[] getCorners() {
return this.corners;
}
public ArenaSchematic getSchematic() {
return this.schematic;
}
public BoundingBox createBox() {
return BoundingBox.of(this.corners[0], this.corners[1]);
}
public Location[] getCarPartLocations() {
return this.carPartLocations;
}
public Location getRandomItemLocation() {
final int length = this.carPartLocations.length;
if (length == 0) {
return this.spawn;
}
final int index = RandomUtils.generateInt(length);
final Location location = this.carPartLocations[index];
final Location drop = location.clone();
drop.add(0, 1.5, 0);
return drop;
}
}
ArenaSchematic.java
package io.github.pulsebeat02.murderrun.game.arena;
import static java.util.Objects.requireNonNull;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.extent.clipboard.io.BuiltInClipboardFormat;
import com.sk89q.worldedit.extent.clipboard.io.ClipboardWriter;
import com.sk89q.worldedit.function.operation.ForwardExtentCopy;
import com.sk89q.worldedit.function.operation.Operations;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.CuboidRegion;
import io.github.pulsebeat02.murderrun.data.hibernate.converters.BlockVectorConverter;
import io.github.pulsebeat02.murderrun.data.hibernate.converters.PathConverter;
import io.github.pulsebeat02.murderrun.utils.IOUtils;
import it.unimi.dsi.fastutil.io.FastBufferedOutputStream;
import jakarta.persistence.Column;
import jakarta.persistence.Convert;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Serial;
import java.io.Serializable;
import java.nio.file.Files;
import java.nio.file.Path;
import org.bukkit.Location;
import org.bukkit.World;
public final class ArenaSchematic implements Serializable {
@Serial
private static final long serialVersionUID = 4953428050756665476L;
@Convert(converter = PathConverter.class)
@Column(name = "schematic_path")
private final Path schematicPath;
@Convert(converter = BlockVectorConverter.class)
@Column(name = "origin")
private final BlockVector3 origin;
public ArenaSchematic(final Path schematicPath, final BlockVector3 origin) {
this.schematicPath = schematicPath;
this.origin = origin;
}
public static ArenaSchematic copyAndCreateSchematic(final String name, final Location[] corners) {
try {
final Clipboard clipboard = performForwardExtentCopy(corners);
final Path path = performSchematicWrite(clipboard, name);
final BlockVector3 origin = clipboard.getOrigin();
return new ArenaSchematic(path, origin);
} catch (final WorldEditException | IOException e) {
throw new AssertionError(e);
}
}
private static Clipboard performForwardExtentCopy(final Location[] corners) throws WorldEditException {
final CuboidRegion region = createRegion(corners);
final BlockArrayClipboard clipboard = new BlockArrayClipboard(region);
final com.sk89q.worldedit.world.World world = region.getWorld();
final WorldEdit instance = WorldEdit.getInstance();
try (final EditSession session = instance.newEditSession(world)) {
final BlockVector3 min = region.getMinimumPoint();
final ForwardExtentCopy forwardExtentCopy = new ForwardExtentCopy(session, region, clipboard, min);
forwardExtentCopy.setCopyingEntities(true);
Operations.complete(forwardExtentCopy);
return clipboard;
}
}
private static Path performSchematicWrite(final Clipboard clipboard, final String name) throws IOException {
final Path data = IOUtils.getPluginDataFolderPath();
final Path parent = data.resolve("schematics");
IOUtils.createFolder(parent);
final Path file = parent.resolve(name);
final BuiltInClipboardFormat format = BuiltInClipboardFormat.SPONGE_V3_SCHEMATIC;
try (
final OutputStream stream = Files.newOutputStream(file);
final OutputStream fast = new FastBufferedOutputStream(stream);
final ClipboardWriter writer = format.getWriter(fast)
) {
writer.write(clipboard);
}
return file;
}
private static CuboidRegion createRegion(final Location[] corners) {
final Location first = corners[0];
final Location second = corners[1];
final World world = requireNonNull(first.getWorld());
final BlockVector3 firstCorner = BukkitAdapter.asBlockVector(first);
final BlockVector3 secondCorner = BukkitAdapter.asBlockVector(second);
final com.sk89q.worldedit.world.World instance = BukkitAdapter.adapt(world);
return new CuboidRegion(instance, firstCorner, secondCorner);
}
public Path getSchematicPath() {
return this.schematicPath;
}
public BlockVector3 getOrigin() {
return this.origin;
}
}
我正在尝试将这些实体导入到我的 H2 嵌入式服务器数据库中。我有一个抽象的
Controller
类来控制我的管理器类,例如 ArenaManager
。
package io.github.pulsebeat02.murderrun.data.hibernate.controllers;
import static java.util.Objects.*;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
public abstract class AbstractController<T> implements Controller<T> {
private final SessionFactory factory;
private final Object id;
public AbstractController(final SessionFactory factory, final Object id) {
this.factory = factory;
this.id = id;
}
@Override
public T deserialize() {
try (final Session session = this.factory.openSession()) {
final Transaction transaction = session.beginTransaction();
final Class<T> clazz = this.getGenericClass();
final T manager = session.get(clazz, this.id);
final T defaultEntity = requireNonNull(this.createDefaultEntity());
final T returnEntity = requireNonNullElse(manager, defaultEntity);
transaction.commit();
return returnEntity;
} catch (final Exception e) {
throw new AssertionError(e);
}
}
public abstract T createDefaultEntity();
@Override
public void shutdown() {
if (this.factory.isOpen()) {
this.factory.close();
}
}
@Override
public void serialize(final T data) {
if (data == null) {
return;
}
try (final Session session = this.factory.openSession()) {
final Transaction transaction = session.beginTransaction();
session.merge(data); // error occurs here
transaction.commit();
}
}
}
我使用预设 ID,因为我想为每个经理设置标识符,例如我的
ArenaManager
。
package io.github.pulsebeat02.murderrun.data.hibernate;
public interface HibernateIdentifiers {
Long LOBBY_ID = 1L;
Long ARENA_ID = 2L;
Long PLAYER_STATISTICS_ID = 3L;
Long LOBBY_MANAGER_ID = 4L;
Long ARENA_MANAGER_ID = 5L;
Long STATISTICS_MANAGER_ID = 6L;
}
我让我的用户以 JSON 或 H2 之类的数据库存储数据,并且 JSON 保存格式有效,但我试图弄清楚如何通过 Hibernate 通过数据库来实现这一点。我不想仅为数据库创建一组新的 Arena 数据抽象,我想使用当前的管理器并正确注释
Map
,使其与我的 H2 数据库的行和列相对应。
当我在用户更改或修改我的
ArenaManager
实例(通常是底层 Map
中的新条目或已删除条目)后尝试将数据更新/保存到数据库中时,我最终会遇到上述异常。
我尝试对更多 StackOverflow 线程、论坛进行进一步研究,并在各种 Discord 中提问,但我没有运气,因为我不确定这个错误的根源是什么。我见过许多其他线程也有同样的问题,但由于我缺乏经验,我似乎无法真正从堆栈跟踪中跟踪源或原因。如果有人能指出我正确的方向,我将不胜感激。谢谢你。
在您的实体中尝试此操作:
private Long id;
而不是
private Long id = HibernateIdentifiers.ARENA_ID;