"detached entity passed to persist" Fehler beim Speichern eine Entity

8u3631984

Bekanntes Mitglied
Guten Tag liebe Forumsteilnehmer.

Kurz zu meiner Project Idee :
Ich möchte gerne eine Bilderverwaltung programmieren. Dazu möchte ich Meta-Daten aus Fotos auslesen und in Property Objekten speichern. Später möchte ich nach diesen Properties filter über eine Webanwendung.

Für die Bilder habe ich folgende Entity Klasse :
Java:
@Builder(setterPrefix = "with", toBuilder = true)
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Entity
@Table(name = "IMAGE")
@ToString
public class Image {

    @Id
    @GeneratedValue
    @Column(updatable = false, unique = true)
    private long ID;

    @NonNull
    @Column(updatable = false, unique = true)
    private String hashValue;

    @NonNull
    private String filePath;

    @Singular
    @OneToMany(fetch = FetchType.EAGER,cascade = CascadeType.ALL)
    private Collection<Property> properties;
}

DIe Properties is eine abstracte Klasse :
Code:
@SuperBuilder(setterPrefix = "with")
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
@DiscriminatorColumn(name = "type", discriminatorType = DiscriminatorType.STRING)
@ToString
public abstract class Property {

    @Id
    @GeneratedValue
    @Column(updatable = false, unique = true)
    @Getter
    private long ID;

    public abstract PropertyCategory getCategory();
}

Als Beispiel zeige ich hier mal das Album Property
Java:
@SuperBuilder(setterPrefix = "with")
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Entity
@DiscriminatorValue("ALBUM_PROPERTY")
@ToString(callSuper = true)
@EqualsAndHashCode
public class AlbumProperty extends Property {

    private final PropertyCategory category = PropertyCategory.ALBUM;

    @NonNull
    private String albumName;
}

Was ist nun das Problem. Bisher wurden die Bilder aus einem Verzeichnis ausgelesen und in einem Image Service gebaut und gespeichert. Allerdings wurden die Properties immer neu angelegt. Also so gab es das Album Property (für das Test Album) in jedem Image. Ich möchte aber, dass Properties unique sind. Also es soll nur einmal das Album Property für "Test Album1" und 1x für "Test Album2" geben.
Dazu habe ich in dem Image Repository folgende Funktion hinzugefügt :
Code:
    default Optional<Property> findAlbumProperty(String albumName) {
        return findAll().stream()
                .map(Image::getProperties)
                .flatMap(Collection::stream)
                .filter(property -> property.getCategory().equals(PropertyCategory.ALBUM))
                .filter(property -> ((AlbumProperty) property).getAlbumName().equals(albumName))
                .findFirst();
    }

Das Speichern und Bauen des ersten Bildes funktioniert super.
Allerdings bekomme ich die Exception :
Code:
org.springframework.dao.InvalidDataAccessApiUsageException: detached entity passed to persist: my.mediaservice.image.property.Property

Wenn ich das Bild (Image) an dieser Stelle baue:
Code:
 protected Image buildImageObject(@Nonnull File imageFile, @Nonnull String albumName, @Nonnull ImageStatus
            imageStatus, @Nonnull String hashValue) {
        if (!FilenameUtils.getExtension(imageFile.getAbsolutePath()).equalsIgnoreCase("jpg"))
            throw new IllegalArgumentException(imageFile.getAbsolutePath() + " is not an image");

        HashSet<Property> properties = Sets.newHashSet();

        val albumProperty = repository.findAlbumProperty(albumName).orElseGet(() -> propertyService.buildAlbumProperty(albumName));
        /*if (albumProperty != null) properties.add(albumProperty);

        val statusProperty = (StatusProperty) repository.findStatusProperty(imageStatus)
                .orElseGet(() -> propertyService.buildStatusProperty(imageStatus));
        if (statusProperty != null) properties.add(statusProperty);

        val dimensionProperty = propertyService.buildDimensionProperty(imageFile);
        if (dimensionProperty != null) properties.add(dimensionProperty);

        try {
            val locationProperty = propertyService.buildLocationProperty(imageFile);
            if (locationProperty != null) properties.add(locationProperty);
        } catch (PropertyException e) {
            log.debug("ignore location property of {} because of {}", kv("image file", imageFile.getAbsolutePath()), kv("error", e.getMessage()));
        }

        try {
            val timeStampProperty = propertyService.buildTimeStampProperty(imageFile);
            if (timeStampProperty != null) properties.add(timeStampProperty);
        } catch (PropertyException e) {
            log.debug("ignore timestamp property of {} because of {}", kv("image file", imageFile.getAbsolutePath()), kv("error", e.getMessage()));
        }
*/
        return Image.builder()
                .withHashValue(hashValue)
                .withFilePath(imageFile.getAbsolutePath())
                .withProperties(properties)
                .build();
    }

Ich bin mir bewusst, dass es ziemlich viel Code ist, hoffe aber, dass ich das Problem einigermaßen erklärt hat und mir jemand einen Tipp geben kann.
Vielen Dank für euere Zeit und die Hilfe !
 

KonradN

Super-Moderator
Mitarbeiter
Also was mir erst einmal auffällt ist das @Entity auf der abstracten Superklasse. Mit @Entity markierst du reale Entities (die dann auch als Tabelle existieren würden, direkt oder indirekt falls mehrere Entities in einer Tabelle zugeordnet wären) und das ist hier nicht der Fall. Das ist eine Superklasse für andere Entities, da wäre also meine Erwartung @MappedSuperclass, was dann bedeutet: keine eigene Tabelle, die Elemente werden dann nur Bestandteil der Tabellen der Entities, die davon erben.

Das müsste dann auch die genannte Fehlermeldung zur Folge haben, aber ich habe das jetzt nicht ausprobiert.
 
Zuletzt bearbeitet:

mihe7

Top Contributor
Da muss ich @KonradN widersprechen: die Entity-Annotation bei der abstrakten Superklasse ist völlig in Ordnung und notwendig, wenn man Polymorphie in JPA haben möchte.

Ich sehe allerdings zwei Stellen im Code, die zu Problemen führen können: a) equals und hashCode bei Entities von Lombok implementiert und b) long statt Long für die ID verwendet.

a) equals und hashCode sollten bei einer Entity nur die ID berücksichtigen
b) long führt dazu, dass eine neue Entity eine 0 hat. Ich meine mich erinnern zu können, dass zumindest EclipseLink ein Problem damit hat(te). Sprich: die Unterscheidung zwischen neuer und detached Entity richtet(e) sich in EclipseLink danach, ob die ID null war oder nicht.
 

Ähnliche Java Themen

Neue Themen


Oben