You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Hibernate H2更新/删除时的参照完整性约束冲突及Queue持久化问题

Fixing Your JPA Database Issues: Referential Integrity, Bidirectional Associations, and Queue Persistence

Hey there! Let's break down your three main problems and walk through how to fix each one step by step.

1. Referential Integrity Error When Deleting a User

The error you're seeing happens because your ExperimentierStation and User have a many-to-many relationship—JPA automatically creates an intermediate join table (EXPERIMENTIERSTATION_USER) that links the two entities. When you try to delete a User, rows in this join table still reference the user's ID, violating the foreign key constraint.

Why CascadeType.MERGE Isn't Working

CascadeType.MERGE only propagates merge operations from the parent to child entities—it has no effect on delete operations, so that's why you still get the error.

The Fix:

First, you need to properly set up the bidirectional many-to-many association (more on this next), then follow this safe workflow to delete a user:

  • Fetch all ExperimentierStation entities associated with the user you want to delete
  • Remove the user from the benutzer list of each of these stations
  • Save the updated stations to the database
  • Now you can delete the user without violating constraints

If your business logic allows deleting all associated stations when a user is removed, you could add CascadeType.REMOVE to the @ManyToMany annotation—but this is rarely the desired behavior for many-to-many relationships, so proceed with caution.

2. Using mappedBy for Bidirectional Associations (No Table Name Needed!)

mappedBy doesn't require a table name—it refers to the field name in the owning side of the association (the entity that manages the join table). Here's how to set it up:

Update the User Class

Add the bidirectional association pointing back to ExperimentierStation:

@Data
@Entity
// ... keep existing named queries and annotations
public class User {
    // ... keep all existing fields

    // Add this bidirectional many-to-many association
    @ManyToMany(mappedBy = "benutzer")
    private List<ExperimentierStation> experimentierStationen;

    // ... keep existing toString method
}
  • mappedBy = "benutzer" tells JPA that the owning side of the relationship is the benutzer field in the ExperimentierStation class.
  • JPA will automatically use the existing join table for this association—no need to specify a table name here.

3. Persisting a Queue in ExperimentierStation

JPA doesn't support persisting Queue directly because it's an interface—JPA needs to know the concrete implementation class to use. Here's how to fix this:

Option 1: Persist a Queue of Entities (e.g., ProzessSchritt)

If your queue holds entity objects, use @OneToMany with a concrete implementation like LinkedList:

@Data
@Entity
// ... keep existing annotations and fields
public class ExperimentierStation {
    // ... keep all existing fields

    // Persisted Queue for entities
    @OneToMany(targetEntity = ProzessSchritt.class)
    @JoinColumn(name = "es_id") // Foreign key linking to ExperimentierStation's esID
    private Queue<ProzessSchritt> queue = new LinkedList<>();

    // ... keep existing constructors
}

Option 2: Persist a Queue of Value Types (e.g., Strings, Enums)

If your queue holds simple value types, use @ElementCollection:

@Data
@Entity
// ... keep existing annotations and fields
public class ExperimentierStation {
    // ... keep all existing fields

    @ElementCollection(targetClass = String.class)
    @CollectionTable(name = "ES_QUEUE_ITEMS", joinColumns = @JoinColumn(name = "es_id"))
    @Column(name = "queue_item")
    private Queue<String> queue = new LinkedList<>();

    // ... keep existing constructors
}
  • We initialize the queue with LinkedList (a concrete Queue implementation) so JPA knows how to instantiate it when loading from the database.

Full Updated Code Snippets

Modified ExperimentierStation Class

import lombok.*;
import javax.persistence.*;
import java.util.List;
import java.util.Queue;
import java.util.LinkedList;

/** Experimenting stations data class */
@Data
@Entity
@NamedQueries({
 @NamedQuery(name = "ExperimentierStation.findAllInLocation", query = "SELECT es FROM ExperimentierStation es WHERE es.standort = :standort"),
 @NamedQuery(name = "ExperimentierStation.getByStatus", query = "SELECT es FROM ExperimentierStation es WHERE es.status = :status"),
 @NamedQuery(name = "ExperimentierStation.getAll", query = "SELECT es FROM ExperimentierStation es")
})
@RequiredArgsConstructor
@NoArgsConstructor
public class ExperimentierStation {
 /** The station's id */
 @NonNull @Id
 private int esID;
 /** The station's location */
 @NonNull @ManyToOne
 private Standort standort;
 @NonNull
 private String name;
 /** The station's status */
 @NonNull
 private Enum<ExperimentierStationZustand> status;
 /** Conditions for using an experimenting station */
 @OneToMany
 private List<Bedingung> bedingungen;
 @NonNull @ManyToMany(cascade=CascadeType.MERGE, fetch = FetchType.LAZY)
 private List<User> benutzer;
 @OneToOne
 private ProzessSchritt currentPS;

 // Persisted Queue example (adjust based on your actual data type)
 @OneToMany(targetEntity = ProzessSchritt.class)
 @JoinColumn(name = "es_id")
 private Queue<ProzessSchritt> queue = new LinkedList<>();
}

Modified User Class

import lombok.*;
import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.List;

/**
 * This class is used to create user objects
 */
@Data
@Entity
@NamedQueries({
 @NamedQuery(name = "User.findById", query = "SELECT u from User u WHERE u.id = :id"),
 @NamedQuery(name = "User.findByUsername", query = "SELECT u from User u WHERE u.username = :username"),
 @NamedQuery(name = "User.findByEmail", query = "SELECT u from User u WHERE u.email = :email"),
 @NamedQuery(name = "User.getAll", query = "SELECT u FROM User u")
})
@RequiredArgsConstructor(access = AccessLevel.PUBLIC)
@NoArgsConstructor(access = AccessLevel.PUBLIC)
public class User {
 /**
 * User's id
 */
 @Id @NonNull
 private int id;
 /**
 * The user's name
 */
 @NonNull
 private String vorname;
 /**
 * The user's surname
 */
 @NonNull
 private String nachname;
 /**
 * User's email address
 */
 @NonNull
 private String email;
 /**
 * The user's phone number
 */
 @NonNull
 private String telefonnummer;
 /**
 * User's username
 */
 @NonNull
 private String username;
 /**
 * User's hashed password
 */
 @NonNull
 private String password;
 /**
 * Is the user verified?
 */
 @NonNull
 private boolean wurdeVerifiziert;
 /**
 * Creation date of the user object
 */
 @NonNull
 private LocalDateTime erstellungsDatum;
 /**
 * The role's of the user
 */
 @NonNull @ElementCollection
 private List<Role> rollen;
 /**
 * The User's language preference
 */
 @NonNull
 private String language;

 // Bidirectional many-to-many association
 @ManyToMany(mappedBy = "benutzer")
 private List<ExperimentierStation> experimentierStationen;

 public String toString() {
 return vorname + nachname;
 }
}

Final Notes

  • Always remember to synchronize both sides of a bidirectional association when adding/removing entities (e.g., when adding a user to a station, also add the station to the user's experimentierStationen list to keep the model consistent).
  • For the queue, choose the annotation based on whether you're storing entities or simple values.

内容的提问来源于stack exchange,提问作者leolion3

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 16:19:08