Set<Enum> von Hibernate Entity als ENUM(...)ARRAY in der H2-Datenbank speichern

ExceptionOfExpectation

Bekanntes Mitglied
Hallo,
ich habe folgendes Problem in meinem Projekt:
H2 Datenbank kann mit den Set-Kollektion nicht umgehen, ich habe in meiner Tabelle eine Zeile mit ENUM ARRAY
tree_empty.gif
CHARACTERISTIC : ENUM('Aggressive', 'Calm', 'Curious', 'Fearful', 'Friendly', 'Independent', 'Loyal', 'None', 'Playful', 'Protective', 'Reasonable', 'Social', 'Vigorous') ARRAY
Dazu habe ich die Entity Klasse & Enum:
Java:
@Entity
@Data
@Access(AccessType.FIELD)
public class ProductObject {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @SequenceGenerator(
            name = "product_seq_gen",
            sequenceName = "PRODUCT_SEQUENCE",
            allocationSize = 1
        )
    private Long id;
    
    @Column(name="foodId")
    private Long foodId;
    
    @Column(name="puppyId")
    private Long puppyId;
    
    @Column(name="name")
    private String name;
    
    @Column(name="weight", length=30)
    private BigDecimal weight;
    
    @Column(name="price")
    private BigDecimal price;
    
    @Column(name="image")
    private String image;
    

    @Column(name="object_name")
    private String objectName;
    
    @Enumerated(EnumType.STRING)
    private FoodType foodType;
    
    
    
    @Column(name="color")
    private String color;
    
    private int count;
    
    @OneToOne(mappedBy="productObject", cascade=CascadeType.ALL, fetch= FetchType.EAGER)
    private Bread race;
    
    
    
    @Enumerated(EnumType.STRING)
    @Column(name = "characteristic")
    private Set<AnimalCharacter> characteristic;

    @Column(name="height", length=50)
    private BigDecimal height;

    
    @ManyToOne
    @JoinColumn(name = "category_id", referencedColumnName = "id", nullable = false)
    private Category category;
    

    @Override
    public int hashCode() {
         return Objects.hash(id, puppyId, name, weight, price, image, objectName, foodType, characteristic, color, count, race, height, category);  // Hier nur id und andere stabile Eigenschaften verwenden
    }
    
    public enum FoodType{    MEAT, LEFTOVER_FOOD, CANNED, PRODUCTION, VITAMINS, BONE, DRINKS, LIQUID_FOOD, NONE    }
    
    
    

}
Die AnimalCharacter Klasse:

Code:
package de.irahi.welp.props;



public enum AnimalCharacter{
    Calm("Calm"),
    Vigorous("Vigorous"),
    Fearful("Fearful"),
    Friendly("Friendly"),
    Independent("Independent"),
    Reasonable("Reasonable"),
    Loyal("Loyal"),
    None("None"),
    Playful("Playful"),
    Protective("Protective"),
    Curious("Curious"),
    Social("Social"),
    Aggressive("Aggressive");

    
    private final String characterName;
    
    AnimalCharacter(String characterName){
        this.characterName = characterName;
    }
    
    public String getCharacterName() {    return this.characterName;    }
    
}
Bei dem Versuch das Entity Objekt zuspeichern bekomme ich folgende Fehlermeldung:

XML:
2024-11-21T17:29:09.271+01:00 DEBUG 7608 --- [Puppies] [nio-8080-exec-9] org.hibernate.SQL                        :
    insert
    into
        product_object
        (category_id, characteristic, color, count, food_id, food_type, height, image, name, object_name, price, puppy_id, weight, id)
    values
        (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, default)
Hibernate:
    insert
    into
        product_object
        (category_id, characteristic, color, count, food_id, food_type, height, image, name, object_name, price, puppy_id, weight, id)
    values
        (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, default)
2024-11-21T17:29:09.359+01:00  WARN 7608 --- [Puppies] [nio-8080-exec-9] o.h.engine.jdbc.spi.SqlExceptionHelper   : SQL Error: 22018, SQLState: 22018
2024-11-21T17:29:09.360+01:00 ERROR 7608 --- [Puppies] [nio-8080-exec-9] o.h.engine.jdbc.spi.SqlExceptionHelper   : Datenumwandlungsfehler beim Umwandeln von "ARRAY [CAST(X'aced00057e72002364652e69726168692e77656c702e70726f70732e416e696d616c43686172616374657200000000000000001200007872000e6a6176612e6c616e672e456e756d00000000000000001200007870740007437572696f7573' AS JAVA_OBJECT), CAST(X'aced00057e72002364652e69726168692e77656c702e70726f70732e416e696d616c43686172616374657200000000000000001200007872000e6a6176612e6c616e672e456e756d00000000000000001200007870740008467269656e646c79' AS JAVA_OBJECT)] (PRODUCT_OBJECT: ""CHARACTERISTIC"" ENUM('Aggressive', 'Calm', 'Curious', 'Fearful', 'Friendly', 'Independent', 'Loyal', 'None', 'Playful', 'Protective', 'Reasonable', 'Social', 'Vigorous') ARRAY)"
Data conversion error converting "ARRAY [CAST(X'aced00057e72002364652e69726168692e77656c702e70726f70732e416e696d616c43686172616374657200000000000000001200007872000e6a6176612e6c616e672e456e756d00000000000000001200007870740007437572696f7573' AS JAVA_OBJECT), CAST(X'aced00057e72002364652e69726168692e77656c702e70726f70732e416e696d616c43686172616374657200000000000000001200007872000e6a6176612e6c616e672e456e756d00000000000000001200007870740008467269656e646c79' AS JAVA_OBJECT)] (PRODUCT_OBJECT: ""CHARACTERISTIC"" ENUM('Aggressive', 'Calm', 'Curious', 'Fearful', 'Friendly', 'Independent', 'Loyal', 'None', 'Playful', 'Protective', 'Reasonable', 'Social', 'Vigorous') ARRAY)"; SQL statement:
insert into product_object (category_id,characteristic,color,count,food_id,food_type,height,image,name,object_name,price,puppy_id,weight,id) values (?,?,?,?,?,?,?,?,?,?,?,?,?,default) [22018-224]
2024-11-21T17:29:09.389+01:00 ERROR 7608 --- [Puppies] [nio-8080-exec-9] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: org.springframework.dao.DataIntegrityViolationException: could not execute statement [Datenumwandlungsfehler beim Umwandeln von "ARRAY [CAST(X'aced00057e72002364652e69726168692e77656c702e70726f70732e416e696d616c43686172616374657200000000000000001200007872000e6a6176612e6c616e672e456e756d00000000000000001200007870740007437572696f7573' AS JAVA_OBJECT), CAST(X'aced00057e72002364652e69726168692e77656c702e70726f70732e416e696d616c43686172616374657200000000000000001200007872000e6a6176612e6c616e672e456e756d00000000000000001200007870740008467269656e646c79' AS JAVA_OBJECT)] (PRODUCT_OBJECT: ""CHARACTERISTIC"" ENUM('Aggressive', 'Calm', 'Curious', 'Fearful', 'Friendly', 'Independent', 'Loyal', 'None', 'Playful', 'Protective', 'Reasonable', 'Social', 'Vigorous') ARRAY)"
Data conversion error converting "ARRAY [CAST(X'aced00057e72002364652e69726168692e77656c702e70726f70732e416e696d616c43686172616374657200000000000000001200007872000e6a6176612e6c616e672e456e756d00000000000000001200007870740007437572696f7573' AS JAVA_OBJECT), CAST(X'aced00057e72002364652e69726168692e77656c702e70726f70732e416e696d616c43686172616374657200000000000000001200007872000e6a6176612e6c616e672e456e756d00000000000000001200007870740008467269656e646c79' AS JAVA_OBJECT)] (PRODUCT_OBJECT: ""CHARACTERISTIC"" ENUM('Aggressive', 'Calm', 'Curious', 'Fearful', 'Friendly', 'Independent', 'Loyal', 'None', 'Playful', 'Protective', 'Reasonable', 'Social', 'Vigorous') ARRAY)"; SQL statement:
insert into product_object (category_id,characteristic,color,count,food_id,food_type,height,image,name,object_name,price,puppy_id,weight,id) values (?,?,?,?,?,?,?,?,?,?,?,?,?,default) [22018-224]] [insert into product_object (category_id,characteristic,color,count,food_id,food_type,height,image,name,object_name,price,puppy_id,weight,id) values (?,?,?,?,?,?,?,?,?,?,?,?,?,default)]; SQL [insert into product_object (category_id,characteristic,color,count,food_id,food_type,height,image,name,object_name,price,puppy_id,weight,id) values (?,?,?,?,?,?,?,?,?,?,?,?,?,default)]] with root cause

org.h2.jdbc.JdbcSQLDataException: Datenumwandlungsfehler beim Umwandeln von "JAVA_OBJECT to ENUM"
Data conversion error converting "JAVA_OBJECT to ENUM" [22018-224]
    at org.h2.message.DbException.getJdbcSQLException(DbException.java:518) ~[h2-2.2.224.jar:2.2.224]
    at org.h2.message.DbException.getJdbcSQLException(DbException.java:489) ~[h2-2.2.224.jar:2.2.224]
    at org.h2.message.DbException.get(DbException.java:223) ~[h2-2.2.224.jar:2.2.224]
    at org.h2.message.DbException.get(DbException.java:199) ~[h2-2.2.224.jar:2.2.224]
____________________________________________________________________________________________________________________________________________
ich habe bereits versucht mit einer Konverter Klasse zum Laufen zu bringen:

Java:
@Converter
public class AnimalCharacterSetConverter implements AttributeConverter<Set<AnimalCharacter>, String> {

    @Override
    public String convertToDatabaseColumn(Set<AnimalCharacter> attribute) {
        
        return attribute.stream().map(Enum::name).collect(Collectors.joining(","));
    }

    @Override
    public Set<AnimalCharacter> convertToEntityAttribute(String dbData) {
        // TODO Auto-generated method stub
        return Arrays.stream(dbData.split(",")).map(AnimalCharacter::valueOf)
                .collect(Collectors.toSet());
    }

}
hat nichts gebracht, weiterhin gab es einen Versuch mit @Type Annotation von ChatGPT, aber da hat sich einiges nicht zusammengepasst, das Attribut type wurde von Eclipse nicht erkannt und im Internet habe ich keine vergleichbare Anwendung gefunden (stattdessen gibt es ein value Attribut):

Code:
@Type(type = "org.hibernate.type.EnumType")
@Enumerated(EnumType.STRING)
@Column(columnDefinition = "ENUM('Aggressive', 'Calm', 'Curious', 'Fearful', 'Friendly', 'Independent', 'Loyal', 'None', 'Playful', 'Protective', 'Reasonable', 'Social', 'Vigorous') ARRAY")
private List<AnimalCharacterEnum> characteristic;
 
Wenn du ein ENUM in deiner Entity definierst, dann musst du diese vermutlich als 'transient' deklarieren. Wie soll JPA damit umgehen? Das ENUM ist keine Spalte oder ähnliches, es ist nur Typ/Wert einer Spalte in der DB. Besser du verwendest für FoodType eine eigene Datei, wie bei AnimalCharacter.
Meiner Meinung nach ist es sauberer eine Fremdschlüsseltabelle zu verwenden. Die Anwendung lässt sich besser erweitern/ändern, einfacher mehrsprachig umsetzen und Text ist von Wert getrennt. Den Text eines Enum kann man nicht mehr ändern, ohne alle Datensätze anzupassen. Das macht alles unnötig kompliziert.
 
Zuletzt bearbeitet:
Für das FoodType gibt es gar keine Probleme. Das Problem tritt an, wenn ich die characteristic Property speichere und zwar wegen den "Set".
Die H2 Database kann mit Set-Type nicht umgehen, allerdings, wenn ich die Daten von der Datenbank abfrage, dann funktioniert alles Einwandfrei.
Ich verstehe was du meinst, aber hier wollte ich unbedingt Enums anwenden, außerdem werden keine zusätzliche Charaktereigenschaften entstehen, was FoodType angeht gilt dasselbe, den es gibt keinen weiteren relevanten Zustand.
Das Problem entsteht nur dann, wenn ich Daten hinzufügen möchte, aber nicht wenn ich die abfrage.
 
Der Typ der Spalte ist kein ENUM, sondern ein ARRAY von ENUMs. Wenn ich es richtig sehe, wird ARRAY auf ein Object gemappt und eine ENUM-Konstante wird als String dargestellt. Der Converter müsste dementsprechend etwa so aussehen:
Java:
@Converter
public class EnumArrayConverter implements AttributeConverter<Set<AnimalCharacter>, Object>{

    @Override
    public Object convertToDatabaseColumn(Set<AnimalCharacter> attribute) {
        Object[] names = attribute.stream().map(AnimalCharacter::name).toArray();
        return names;
    }

    @Override
    public Set<AnimalCharacter> convertToEntityAttribute(Object dbData) {
        Object[] chars = (Object[]) dbData;
        return Stream.of(chars).map(Object::toString).map(AnimalCharacter::valueOf).collect(Collectors.toSet());
    }    
}

Das Set müsste dann so deklariert werden:
Java:
    @Convert(converter = EnumArrayConverter.class)
    private Set<AnimalCharacter> characteristic;
 
Der Typ der Spalte ist kein ENUM, sondern ein ARRAY von ENUMs. Wenn ich es richtig sehe, wird ARRAY auf ein Object gemappt und eine ENUM-Konstante wird als String dargestellt. Der Converter müsste dementsprechend etwa so aussehen:
Java:
@Converter
public class EnumArrayConverter implements AttributeConverter<Set<AnimalCharacter>, Object>{

    @Override
    public Object convertToDatabaseColumn(Set<AnimalCharacter> attribute) {
        Object[] names = attribute.stream().map(AnimalCharacter::name).toArray();
        return names;
    }

    @Override
    public Set<AnimalCharacter> convertToEntityAttribute(Object dbData) {
        Object[] chars = (Object[]) dbData;
        return Stream.of(chars).map(Object::toString).map(AnimalCharacter::valueOf).collect(Collectors.toSet());
    }  
}

Das Set müsste dann so deklariert werden:
Java:
    @Convert(converter = EnumArrayConverter.class)
    private Set<AnimalCharacter> characteristic;
Guter Punkt, macht Sinn, nur statt Object sollte Object[] kommen, leider bekomme ich die selbe Fehlermeldung.
Ich habe meinen characteristic Type in der Datenbank Tabelle geprüft und das bekommen:
SHOW COLUMNS FROM Product_object;
FIELD TYPE NULL KEY DEFAULT
IDBIGINTNOPRINULL
CHARACTERISTICENUM('Aggressive', 'Calm', 'Curious', 'Fearful', 'Friendly', 'Independent', 'Loyal', 'None', 'Playful', 'Protective', 'Reasonable', 'Social', 'Vigorous') ARRAYYESNULL
COLORCHARACTER VARYING(255)YESNULL
COUNTINTEGERNONULL
FOOD_IDBIGINTYESNULL
FOOD_TYPEENUM('BONE', 'CANNED', 'DRINKS', 'LEFTOVER_FOOD', 'LIQUID_FOOD', 'MEAT', 'NONE', 'PRODUCTION', 'VITAMINS')YESNULL
HEIGHTNUMERIC(38, 2)YESNULL
IMAGECHARACTER VARYING(255)YESNULL
NAMECHARACTER VARYING(255)YESNULL
OBJECT_NAMECHARACTER VARYING(255)YESNULL
PRICENUMERIC(38, 2)YESNULL
PUPPY_IDBIGINTYESNULL
WEIGHTNUMERIC(38, 2)YESNULL
CATEGORY_IDBIGINTNONULL
(14 rows, 10 ms)
Das heißt für meinen Enum-array gibt es keinen geeigneten Type oder wie?
 
...

Das Set müsste dann so deklariert werden:
Java:
    @Convert(converter = EnumArrayConverter.class)
    private Set<AnimalCharacter> characteristic;
ich musste von Anfang an die @Enumerated-Annotion einfügen:
Java:
....
@Enumerated(EnumType.STRING)
@Convert(converter = EnumArrayConverter.class)
private Set<AnimalCharacter> characteristic;

denn sonst wird es nicht kompiliert.
 
Ich habe das mit genau dem oben angegebenen Converter und einer H2-DB überprüft: funktioniert einwandfrei.

Nachtrag:

Ich habe es mit Object getestet (ein Array ist auch ein Object).
Es lag an meiner @Enumated(...) Annotation. Erstes mal habe ich sie auskommentiert, daraufhin ist der Code auseinander geflogen. Als du gesagt hast, dass es bei dir klappt, habe ich es nochmal versucht und es ging auf. Anscheinend habe ich zu dem Zeitpunkt noch etwas anderes umgebaut und das muss der Grund dafür sein.
Danke für den Code Ausschnitt.
 

Zurück
Oben