保存 Arena 数据时出现 Hibernate 异常(不存在具有给定标识符的行)

问题描述 投票:0回答:1

这是我第一次将 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 中提问,但我没有运气,因为我不确定这个错误的根源是什么。我见过许多其他线程也有同样的问题,但由于我缺乏经验,我似乎无法真正从堆栈跟踪中跟踪源或原因。如果有人能指出我正确的方向,我将不胜感激。谢谢你。

java hibernate jpa h2
1个回答
0
投票

在您的实体中尝试此操作:

private Long id;

而不是

private Long id = HibernateIdentifiers.ARENA_ID;
© www.soinside.com 2019 - 2024. All rights reserved.