Äquivalent zu Django Models in Java

MJannek

Bekanntes Mitglied
Java:
package org.mjannek.sport.buli.tables;

import java.sql.*;
import java.time.*;
import javax.persistence.*;
import org.mjannek.database.entity.*;

@Entity
@Table(name = "matches")
public class Match extends BaseEntity {

    @ManyToOne
    @JoinColumn(name = "seasonid", foreignKey = @ForeignKey(name = "fk_match_season"))
    private Season season;

    @Column(name = "matchday", nullable = false)
    private Integer matchday;

    @ManyToOne
    @JoinColumn(name = "hometeamid", foreignKey = @ForeignKey(name = "fk_match_home_team"))
    private Club homeTeam;

    @ManyToOne
    @JoinColumn(name = "awayteamid", foreignKey = @ForeignKey(name = "fk_match_away_team"))
    private Club awayTeam;

    @Column(name = "homegoals")
    private Integer homeGoals;

    @Column(name = "awaygoals")
    private Integer awayGoals;

    @Column(name = "timestamp")
    private Timestamp timestamp;

    @ManyToOne
    @JoinColumn(name = "stadiumid", foreignKey = @ForeignKey(name = "fk_match_stadium"))
    private Stadium stadium;

    @Column(name = "refid")
    private String refId;

    @Column(name = "attendance")
    private Integer attendance;

    @Column(name = "link")
    private String link;

    @ManyToOne
    @JoinColumn(name = "refassid1", foreignKey = @ForeignKey(name = "fk_match_ref_ass_1"))
    private Person refAss1;

    @ManyToOne
    @JoinColumn(name = "refassid2", foreignKey = @ForeignKey(name = "fk_match_ref_ass_2"))
    private Person refAss2;

    @ManyToOne
    @JoinColumn(name = "refassid3", foreignKey = @ForeignKey(name = "fk_match_ref_ass_3"))
    private Person refAss3;

    @ManyToOne
    @JoinColumn(name = "refassid4", foreignKey = @ForeignKey(name = "fk_match_ref_ass_4"))
    private Person refAss4;

    @ManyToOne
    @JoinColumn(name = "statusid", foreignKey = @ForeignKey(name = "fk_match_status"))
    private MatchStatus status;

    // No-arg constructor for JPA
    public Match() {
        super();
    }

    // Constructor for setting id
    public Match(int id) {
        super(id);
    }

    // Getters and Setters
    public Season getSeason() {
        return season;
    }

    public void setSeason(Season season) {
        this.season = season;
    }

    public Integer getMatchday() {
        return matchday;
    }

    public void setMatchday(Integer matchday) {
        this.matchday = matchday;
    }

    public Club getHomeTeam() {
        return homeTeam;
    }

    public void setHomeTeam(Club homeTeam) {
        this.homeTeam = homeTeam;
    }

    public Club getAwayTeam() {
        return awayTeam;
    }

    public void setAwayTeam(Club awayTeam) {
        this.awayTeam = awayTeam;
    }

    public Integer getHomeGoals() {
        return homeGoals;
    }

    public void setHomeGoals(Integer homeGoals) {
        this.homeGoals = homeGoals;
    }

    public Integer getAwayGoals() {
        return awayGoals;
    }

    public void setAwayGoals(Integer awayGoals) {
        this.awayGoals = awayGoals;
    }

    public Timestamp getTimestamp() {
        return timestamp;
    }

    public void setTimestamp(Timestamp timestamp) {
        this.timestamp = timestamp;
    }

    public Stadium getStadium() {
        return stadium;
    }

    public void setStadium(Stadium stadium) {
        this.stadium = stadium;
    }

    public String getRefId() {
        return refId;
    }

    public void setRefId(String refId) {
        this.refId = refId;
    }

    public Integer getAttendance() {
        return attendance;
    }

    public void setAttendance(Integer attendance) {
        this.attendance = attendance;
    }

    public String getLink() {
        return link;
    }

    public void setLink(String link) {
        this.link = link;
    }

    public Person getRefAss1() {
        return refAss1;
    }

    public void setRefAss1(Person refAss1) {
        this.refAss1 = refAss1;
    }

    public Person getRefAss2() {
        return refAss2;
    }

    public void setRefAss2(Person refAss2) {
        this.refAss2 = refAss2;
    }

    public Person getRefAss3() {
        return refAss3;
    }

    public void setRefAss3(Person refAss3) {
        this.refAss3 = refAss3;
    }

    public Person getRefAss4() {
        return refAss4;
    }

    public void setRefAss4(Person refAss4) {
        this.refAss4 = refAss4;
    }

    public MatchStatus getStatus() {
        return status;
    }

    public void setStatus(MatchStatus status) {
        this.status = status;
    }
}

Wie kann ich einen Unique Constraint so setzen, das ein Club pro Saison, Spieltag und MatchStatus, egal ob heim oder auswärts nur einmal vorkommen darf?
Der MatchStatus spielt eine Rolle, da ein Spiel den Status "abgebrochen" hat und es ein Wiederholungsspiel geben muss.
 

MJannek

Bekanntes Mitglied
Außerdem habe ich noch eine Frage, wie ich Internationalisierung "vernunftig" umsetze.
Das ist mein bisheriger Ansatz:
Java:
package org.mjannek.sport.buli.tables;

import javax.persistence.*;
import org.mjannek.database.entity.*;

@Entity
@Table(name = "languages")
public class Language extends BaseEntity {

    @Column(name = "name", nullable = false, columnDefinition = "varchar(255) CHARACTER SET utf8mb4")
    private String name;

    @Column(name = "lowerLocale", nullable = false, columnDefinition = "varchar(255) CHARACTER SET utf8mb4")
    private String lowerLocale;

    @Column(name = "upperLocale", nullable = false, columnDefinition = "varchar(255) CHARACTER SET utf8mb4")
    private String upperLocale;

    // No-arg constructor for JPA
    public Language() {
        super();
    }

    // Constructor for setting id
    public Language(int id) {
        super(id);
    }

    // Getters and Setters
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getLowerLocale() {
        return lowerLocale;
    }

    public void setLowerLocale(String lowerLocale) {
        this.lowerLocale = lowerLocale;
    }

    public String getUpperLocale() {
        return upperLocale;
    }

    public void setUpperLocale(String upperLocale) {
        this.upperLocale = upperLocale;
    }
}

Java:
package org.mjannek.sport.buli.tables;

import javax.persistence.*;
import org.mjannek.database.entity.*;

@Entity
@Table(name = "goaltypes")
public class GoalType extends BaseEntity {

    @Column(name = "name", nullable = false, length = 50)
    private String name;

    @Column(name = "description", columnDefinition = "varchar(255) CHARACTER SET utf8mb4")
    private String description;

    // Default Constructor
    public GoalType() {
    }

    // Constructor for setting fields
    public GoalType(String name, String description) {
        this.name = name;
        this.description = description;
    }

    // Getters and Setters
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }
}

Java:
package org.mjannek.sport.buli.tables;

import javax.persistence.*;
import org.mjannek.database.entity.*;

@Entity
@Table(name = "goaltypesi18n",
        uniqueConstraints = {
                @UniqueConstraint(name = "uk_goal_type_language", columnNames = {"goaltypeid", "languageid"})
        }
)
public class GoalTypeI18n extends BaseEntity {

    @ManyToOne
    @JoinColumn(name = "goaltypeid", foreignKey = @ForeignKey(name = "fk_goal_type_i18n_goaltype"))
    private GoalType goalType;

    @ManyToOne
    @JoinColumn(name = "languageid", foreignKey = @ForeignKey(name = "fk_goal_type_i18n_language"))
    private Language language;

    @Column(name = "goalTypeName", nullable = false, columnDefinition = "varchar(255) CHARACTER SET utf8mb4")
    private String goalTypeName;

    // Default Constructor
    public GoalTypeI18n() {
    }

    // Getters and Setters
    public GoalType getGoalType() {
        return goalType;
    }

    public void setGoalType(GoalType goalType) {
        this.goalType = goalType;
    }

    public Language getLanguage() {
        return language;
    }

    public void setLanguage(Language language) {
        this.language = language;
    }

    public String getGoalTypeName() {
        return goalTypeName;
    }

    public void setGoalTypeName(String goalTypeName) {
        this.goalTypeName = goalTypeName;
    }
}

Ich verweise dann in Goal jedoch nur auf GoalType und nicht auf GoalTypeI18n

Java:
package org.mjannek.sport.buli.tables;

import javax.persistence.*;
import org.mjannek.database.entity.*;

@Entity
@Table(name = "goals")
public class Goal extends BaseEntity {

    @ManyToOne
    @JoinColumn(name = "matchid", foreignKey = @ForeignKey(name = "fk_goal_match"))
    private Match match;

    @ManyToOne
    @JoinColumn(name = "clubid", foreignKey = @ForeignKey(name = "fk_goal_club"))
    private Club club;

    @ManyToOne
    @JoinColumn(name = "goalscorerid", foreignKey = @ForeignKey(name = "fk_goal_goal_scorer"))
    private Person goalscorer;

    @ManyToOne
    @JoinColumn(name = "assistid", foreignKey = @ForeignKey(name = "fk_goal_assist"))
    private Person assist;

    @Column(name = "minute", nullable = false, length = 10)
    private String minute;

    @ManyToOne
    @JoinColumn(name = "typeid", foreignKey = @ForeignKey(name = "fk_goal_type"))
    private GoalType type;

    @ManyToOne
    @JoinColumn(name = "statusid", foreignKey = @ForeignKey(name = "fk_goal_status"))
    private GoalStatus status;

    // Default Constructor
    public Goal() {
        super();
    }

    // Getters and Setters
    public Match getMatch() {
        return match;
    }

    public void setMatch(Match match) {
        this.match = match;
    }

    public Club getClub() {
        return club;
    }

    public void setClub(Club club) {
        this.club = club;
    }

    public Person getGoalscorer() {
        return goalscorer;
    }

    public void setGoalscorer(Person goalscorer) {
        this.goalscorer = goalscorer;
    }

    public Person getAssist() {
        return assist;
    }

    public void setAssist(Person assist) {
        this.assist = assist;
    }

    public String getMinute() {
        return minute;
    }

    public void setMinute(String minute) {
        this.minute = minute;
    }

    public GoalType getType() {
        return type;
    }

    public void setType(GoalType type) {
        this.type = type;
    }

    public GoalStatus getStatus() {
        return status;
    }

    public void setStatus(GoalStatus status) {
        this.status = status;
    }
}
 

MJannek

Bekanntes Mitglied
Kann ich irgendwie festlegen, das bei erstellen einer neuen Entity erst die Spalte id aus BaseEntity hinzugefügt werden sollen. Danach die Spalten aus der jeweiligen Klasse, die BaseEntity erweitert (in der Reihenfolge, wie sie in der jeweiligen Klasse definiert sind). Und zum Schluss die restlichlichen Spalten aus BaseEntity.
Java:
package org.mjannek.database.entity;


import java.io.*;
import java.sql.*;
import java.time.*;
import javax.persistence.*;
import javax.persistence.ForeignKey;
import javax.persistence.Table;
import org.hibernate.annotations.*;
import org.mjannek.sport.buli.database.table.*;

@MappedSuperclass
@FilterDef(name = "deletedFilter", parameters = @ParamDef(name = "isDeleted", type = "boolean"))
@Filter(name = "deletedFilter", condition = "deleted = :isDeleted")
public abstract class BaseEntity implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @Column(name = "createdat", updatable = false, nullable = false)
    private Timestamp createdAt;

    @Column(name = "updatedat", nullable = false)
    private Timestamp updatedAt;

    @Column(name = "deleted")
    private Boolean deleted = false;

    @ManyToOne
    @JoinColumn(name = "createdby", updatable = false, foreignKey = @ForeignKey(name = "fk_baseentity_createdby"))
    private User createdBy;

    @ManyToOne
    @JoinColumn(name = "updatedby", foreignKey = @ForeignKey(name = "fk_baseentity_updatedby"))
    private User updatedBy;

    @Column(name = "deletedat")
    private Timestamp deletedAt;

    @ManyToOne
    @JoinColumn(name = "deletedby", foreignKey = @ForeignKey(name = "fk_baseentity_deletedby"))
    private User deletedBy;

    @Version
    private Integer version;

    // Basis-Konstruktor
    public BaseEntity() {
        this.deleted = false;
    }

    public BaseEntity(Integer id) {
        this.id = id;
        this.deleted = false;
    }

    @PrePersist
    protected void onCreate() {
        this.createdAt = Timestamp.valueOf(LocalDateTime.now());
        this.updatedAt = Timestamp.valueOf(LocalDateTime.now());
    }

    @PreUpdate
    protected void onUpdate() {
        this.updatedAt = Timestamp.valueOf(LocalDateTime.now());
    }

    // Getter and Setter
    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Timestamp getCreatedAt() {
        return createdAt;
    }

    public void setCreatedAt(Timestamp createdAt) {
        this.createdAt = createdAt;
    }

    public Timestamp getUpdatedAt() {
        return updatedAt;
    }

    public void setUpdatedAt(Timestamp updatedAt) {
        this.updatedAt = updatedAt;
    }

    public Boolean getDeleted() {
        return deleted;
    }

    public void setDeleted(Boolean deleted) {
        this.deleted = deleted;
    }

    public User getCreatedBy() {
        return createdBy;
    }

    public void setCreatedBy(User createdBy) {
        this.createdBy = createdBy;
    }

    public User getUpdatedBy() {
        return updatedBy;
    }

    public void setUpdatedBy(User updatedBy) {
        this.updatedBy = updatedBy;
    }

    public Timestamp getDeletedAt() {
        return deletedAt;
    }

    public void setDeletedAt(Timestamp deletedAt) {
        this.deletedAt = deletedAt;
    }

    public User getDeletedBy() {
        return deletedBy;
    }

    public void setDeletedBy(User deletedBy) {
        this.deletedBy = deletedBy;
    }

    public Integer getVersion() {
        return version;
    }

    public void setVersion(Integer version) {
        this.version = version;
    }

    // Utility Methods
    public void markAsDeleted(User deletedByUser) {
        this.deleted = true;
        this.deletedAt = Timestamp.valueOf(LocalDateTime.now());
        this.deletedBy = deletedByUser;
    }

    public void restore() {
        this.deleted = false;
        this.deletedAt = null;
        this.deletedBy = null;
        this.updatedAt = Timestamp.valueOf(LocalDateTime.now());
    }
    public String getTableName() {
        // Überprüfen, ob die Annotation @Table vorhanden ist
        Table tableAnnotation = this.getClass().getAnnotation(Table.class);
        if (tableAnnotation != null) {
            return tableAnnotation.name(); // Gibt den Tabellennamen zurück
        }
        return this.getClass().getSimpleName(); // Fallback, falls keine @Table-Annotation vorhanden ist
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        BaseEntity that = (BaseEntity) o;
        return id != null && id.equals(that.id);
    }

    @Override
    public int hashCode() {
        return getClass().hashCode();
    }

    @Override
    public String toString() {
        return "BaseEntity{" +
                "id=" + id +
                ", createdAt=" + createdAt +
                ", updatedAt=" + updatedAt +
                ", deleted=" + deleted +
                ", createdBy=" + (createdBy != null ? createdBy.getId() : "null") +
                ", updatedBy=" + (updatedBy != null ? updatedBy.getId() : "null") +
                ", deletedAt=" + deletedAt +
                ", deletedBy=" + (deletedBy != null ? deletedBy.getId() : "null") +
                ", version=" + version +
                '}';
    }
}
Java:
package org.mjannek.sport.buli.database.table;

import javax.persistence.*;
import org.mjannek.database.entity.*;


@Entity
@Table(name = "goaltypes")
public class GoalType extends BaseEntity {

    @Column(name = "name", nullable = false, length = 50)
    private String name;

    @Column(name = "description", columnDefinition = "CHARACTER SET utf8mb4")
    private String description;

    // Default Constructor
    public GoalType() {
    }

    // Constructor for setting fields
    public GoalType(String name, String description) {
        this.name = name;
        this.description = description;
    }

    // Getters and Setters
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }
}

Ich hab die KI mal gefragt und folgende Hilfsklasse erhalten, jedoch existiert in Table die Methode removeColumn nicht (Fehlermeldung: Cannot resolve method 'removeColumn'):
Java:
package org.mjannek.database.entity;

import org.hibernate.boot.spi.InFlightMetadataCollector;
import org.hibernate.boot.spi.MetadataContributor;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Table;
import org.jboss.jandex.IndexView;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class CustomMetadataContributor implements MetadataContributor {

    @Override
    public void contribute(InFlightMetadataCollector metadataCollector, IndexView indexView) {
        for (PersistentClass persistentClass : metadataCollector.getEntityBindingMap().values()) {
            Table table = persistentClass.getTable();

            // Sammle die existierenden Spalten in einer Liste
            List<Column> columns = new ArrayList<>();
            Iterator<Column> columnIterator = table.getColumnIterator();
            while (columnIterator.hasNext()) {
                columns.add(columnIterator.next());
            }

            // Finde die "id"-Spalte und erstelle eine neue Reihenfolge
            Column idColumn = null;
            List<Column> reorderedColumns = new ArrayList<>();
            for (Column column : columns) {
                if ("id".equals(column.getName())) {
                    idColumn = column;
                } else {
                    reorderedColumns.add(column);
                }
            }

            if (idColumn != null) {
                reorderedColumns.add(0, idColumn); // "id" an den Anfang setzen
            }

            // Leere die Spalten der Tabelle
            columns.forEach(table::removeColumn); // Entferne alle Spalten

            // Füge die Spalten in der neuen Reihenfolge hinzu
            for (Column column : reorderedColumns) {
                table.addColumn(column);
            }

            System.out.println("Spalten in Tabelle '" + table.getName() + "' neu geordnet.");
        }
    }
}
Derzeitige Spaltenreihenfolgen in der Tabelle goaltypes:

id
createdat
updatedat
deleted
createdby
updatedby
deletedat
deletedby
version
name
description

Gewünschte Reihenfolge:
id
name
description
createdat
updatedat
deleted
createdby
updatedby
deletedat
deletedby
version

Vielen Dank im Voraus für die Tipps
 

mihe7

Top Contributor
Kann ich irgendwie festlegen, das bei erstellen einer neuen Entity erst die Spalte id aus BaseEntity hinzugefügt werden sollen. Danach die Spalten aus der jeweiligen Klasse, die BaseEntity erweitert (in der Reihenfolge, wie sie in der jeweiligen Klasse definiert sind). Und zum Schluss die restlichlichen Spalten aus BaseEntity.
JPA: nein, Hibernate: https://robertniestroj.hashnode.dev/ordering-columns-in-a-table-in-jpahibernate, Eclipselink: http://marsparanoma.blogspot.com/2014/01/how-to-set-column-order-in-jpa-under.html - allerdings alles ohne Gewähr.
 

MJannek

Bekanntes Mitglied
Ich habe mich jetzt sehr lange mit JPA und Hibernate auseinander gesetzt und bin damit sehr zufrieden. Ich habe zu folgendem Problem aber noch nichts gefunden. Ich hatte mir eine Klasse BaseEntity erstellt, die die Spalten id, version, createdat, createdBy usw. erzeugt. Die Spalte createdBy ist ein User und verweist auf die jeweilige id aus User. Ich habe in BaseEntity createdBy mit folgendem Code erstellt:
Java:
@ManyToOne
@JoinColumn(name = "created_by", updatable = false, foreignKey = @ForeignKey(name = "fk_created_by"))
private User createdBy;
Dadurch, dass jede meiner Entitäten BaseEntity erweitert, bekomme ich folgende Fehlermeldung:
Code:
Caused by: java.sql.SQLException: Duplicate foreign key constraint name 'fk_created_by'
Ich verstehe, dass diese Exception gewurfen wird, da, der Name eines Foreign Keys eindeutig ist. Ich möchte jedoch einen Namen haben, der eindeutig erkennbar ist und nicht so aussieht, wie folgendes:
Java:
FK_i3jxgbc7gssw61t9fkf96l1s0
Außerdem möchte ich createdBy nicht in jeder Klasse neu definieren. Gibt es eine Möglichkeit für JPA und Hibernate das Schema der Benennung von Schlüsseln (Unique Keys, und Foreign Keys) allgemein einmal zu definieren, dass ein Name erzeugt wird, aus dem man sofort erkennt, worauf sich dieser Key bezieht? Also z.B. fk_table_name_column_name oder uk_table_name_column_name1_column_name2

Vielen Dank schonmal für die Tipps

Markus
 

KonradN

Super-Moderator
Mitarbeiter
Evtl. willst Du Dir einmal bei Hibernate ansehen:
  • Implicit Naming Strategy
  • Physical Naming Strategy

Beides sind Wege, bei denen Du selbst vorgeben kannst, wie die Foreign Keys benannt werden sollen.
(Aber vermutlich wird es wohl auf das Implicit Naming hinaus laufen.)

Mal einfach ein paar Links, die ich aber alle nicht im Detail geprüft habe:

Also mit Implicit Naming Strategy könnte dieses fk_.... z.B. so in der Art gelöst werden:
Java:
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.ImplicitForeignKeyNameSource;
import org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl;

public class CustomImplicitNamingStrategy extends ImplicitNamingStrategyJpaCompliantImpl {

    @Override
    public Identifier determineForeignKeyName(ImplicitForeignKeyNameSource source) {
        String tableName = source.getTableName().getText();
        String columnName = source.getColumnNames().stream()
                                  .map(Identifier::getText)
                                  .reduce((a, b) -> a + "_" + b)
                                  .orElse("default");
        String fkName = "FK_" + tableName + "_" + columnName;
        return toIdentifier(fkName, source.getBuildingContext());
    }
}

Und natürlich das Setzen in der xml Config also etwas wie:
XML:
<property name="hibernate.implicit_naming_strategy" value="com.example.CustomImplicitNamingStrategy"/>
 

MJannek

Bekanntes Mitglied
Evtl. willst Du Dir einmal bei Hibernate ansehen:
  • Implicit Naming Strategy
  • Physical Naming Strategy

Beides sind Wege, bei denen Du selbst vorgeben kannst, wie die Foreign Keys benannt werden sollen.
(Aber vermutlich wird es wohl auf das Implicit Naming hinaus laufen.)

Mal einfach ein paar Links, die ich aber alle nicht im Detail geprüft habe:

Also mit Implicit Naming Strategy könnte dieses fk_.... z.B. so in der Art gelöst werden:
Java:
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.ImplicitForeignKeyNameSource;
import org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl;

public class CustomImplicitNamingStrategy extends ImplicitNamingStrategyJpaCompliantImpl {

    @Override
    public Identifier determineForeignKeyName(ImplicitForeignKeyNameSource source) {
        String tableName = source.getTableName().getText();
        String columnName = source.getColumnNames().stream()
                                  .map(Identifier::getText)
                                  .reduce((a, b) -> a + "_" + b)
                                  .orElse("default");
        String fkName = "FK_" + tableName + "_" + columnName;
        return toIdentifier(fkName, source.getBuildingContext());
    }
}

Und natürlich das Setzen in der xml Config also etwas wie:
XML:
<property name="hibernate.implicit_naming_strategy" value="com.example.CustomImplicitNamingStrategy"/>
Ich habe es jetzt wie folgt um gesetzt:
Java:
package org.mjannek.sport.buli.database.config;

/*
    Created by: Markus
    Creaded at: 17.01.2025
*/


import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.ImplicitForeignKeyNameSource;
import org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl;

public class CustomImplicitNamingStrategy extends ImplicitNamingStrategyJpaCompliantImpl {

    @Override
    public Identifier determineForeignKeyName(ImplicitForeignKeyNameSource source) {
        String tableName = source.getTableName().getText();
        String columnName = source.getColumnNames().stream()
                .map(Identifier::getText)
                .reduce((a, b) -> a + "_" + b)
                .orElse("default");
        String fkName = "FK_" + tableName + "_" + columnName;
        return toIdentifier(fkName, source.getBuildingContext());
    }
}
Die folgende Datei heißt: hibernate.cfg.xml

Code:
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <!-- JDBC Database connection settings -->
        <property name="hibernate.connection.driver_class">com.mysql.cj.jdbc.Driver</property>
        <property name="hibernate.connection.url">jdbc:mysql://server:3306/schema?useUnicode=true&amp;characterEncoding=UTF-8</property>
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.connection.password">pw</property>

        <!-- JDBC connection pool settings -->
        <property name="hibernate.c3p0.min_size">5</property>
        <property name="hibernate.c3p0.max_size">20</property>
        <property name="hibernate.c3p0.timeout">300</property>
        <property name="hibernate.c3p0.max_statements">50</property>
        <property name="hibernate.c3p0.idle_test_period">3000</property>

        <!-- Specify custom dialect -->
        <property name="hibernate.dialect">org.hibernate.dialect.MySQL8Dialect</property>


        <property name="hibernate.implicit_naming_strategy">org.mjannek.sport.buli.database.config.CustomImplicitNamingStrategy</property>


        <!-- Echo all executed SQL to stdout -->
        <property name="hibernate.show_sql">false</property>
         <property name="hibernate.hbm2ddl.auto">update</property>

        <!-- Automatic detection of annotated entities -->
        <property name="hibernate.archive.autodetection">class,hbm</property>

        <!-- Current session context configuration -->
        <property name="hibernate.current_session_context_class">thread</property>

    </session-factory>
</hibernate-configuration>

Ich habe eine Folgende Testklasse, die BaseEntity aus Testzwecken nicht erweritert:
Java:
package org.mjannek.sport.buli.database.table;

/*
    Created by: Markus
    Creaded at: 17.01.2025
*/

import java.sql.*;
import java.time.*;
import javax.persistence.*;
import javax.persistence.Entity;
import javax.persistence.Table;
import lombok.*;
import lombok.experimental.*;
import java.io.*;
import java.lang.reflect.*;
import org.hibernate.annotations.*;
import org.mjannek.sport.buli.database.helper.*;

@Entity
@Table(name = "testEntities")
@Getter
@Setter
@FilterDef(name = "deletedFilter", parameters = @ParamDef(name = "isDeleted", type = "boolean"))
@Filter(name = "deletedFilter", condition = "deleted = :isDeleted")
@NoArgsConstructor
@AllArgsConstructor
@ToString
@SuperBuilder
public class TestEntity implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @EqualsAndHashCode.Include
    @ToString.Exclude
    private Integer id;

    @Version
    @ToString.Exclude
    private Integer version;

    @Column(name = "created_at", nullable = false)
    @ToString.Exclude
    private Timestamp createdAt;

    @ManyToOne
    @JoinColumn(name = "created_by", updatable = false)
    @ToString.Exclude
    private User createdBy;

    @Column(name = "updated_at", nullable = false)
    @ToString.Exclude
    private Timestamp updatedAt;

    @ManyToOne
    @JoinColumn(name = "updated_by", updatable = false)
    @ToString.Exclude
    private User updatedBy;

    @Column(name = "deleted")
    @Builder.Default
    @ToString.Exclude
    private Boolean deleted = false;

    @Column(name = "deleted_at")
    @ToString.Exclude
    private Timestamp deletedAt;

    @ManyToOne
    @JoinColumn(name = "deleted_by", updatable = false)
    @ToString.Exclude
    private User deletedBy;

    //Constructor to set id
    public TestEntity(Integer id) {
        this.id = id;
        this.deleted = false;
    }

    // Copy-Constructor
    public TestEntity(TestEntity other) {
        if (other == null) {
            throw new IllegalArgumentException("Das zu kopierende Objekt darf nicht null sein.");
        }
        for (Field field : other.getClass().getDeclaredFields()) {
            field.setAccessible(true);
            try {
                field.set(this, field.get(other));
            } catch (IllegalAccessException e) {
                throw new RuntimeException("Fehler beim Kopieren des Felds: " + field.getName(), e);
            }
        }
    }

    @PrePersist
    protected void onCreate() {
        this.createdAt = Timestamp.valueOf(LocalDateTime.now());
        this.createdBy = UserContext.getCurrentUser();
        this.updatedAt = Timestamp.valueOf(LocalDateTime.now());
        this.updatedBy = UserContext.getCurrentUser();
    }

    @PreUpdate
    protected void onUpdate() {
        this.updatedBy = UserContext.getCurrentUser();
        this.updatedAt = Timestamp.valueOf(LocalDateTime.now());
    }

    public void markAsDeleted(User deletedBy) {
        this.deleted = true;
        this.updatedAt = Timestamp.valueOf(LocalDateTime.now());
        this.updatedBy = deletedBy;
        this.deletedAt = Timestamp.valueOf(LocalDateTime.now());
        this.deletedBy = deletedBy;
    }

    public void restore(User restoredBy) {
        this.deleted = false;
        this.deletedAt = null;
        this.deletedBy = null;
        this.updatedAt = Timestamp.valueOf(LocalDateTime.now());
        this.updatedBy = restoredBy;
    }

    public String getTableName() {
        Table tableAnnotation = this.getClass().getAnnotation(Table.class);
        if (tableAnnotation != null) {
            return tableAnnotation.name();
        }

        return this.getClass().getSimpleName();
    }

    public void print() {
        System.out.println(this);
    }
}
Außerdem habe ich folgende Klasse:
Java:
package org.mjannek.database;

import javax.persistence.Entity;
import lombok.Getter;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.context.internal.ManagedSessionContext;
import org.reflections.Reflections;

public class HibernateUtil {
    @Getter
    private static final SessionFactory sessionFactory = buildSessionFactory();

    private static SessionFactory buildSessionFactory() {
        try {
            Configuration configuration = new Configuration();
            configuration.configure();

            // Automatische Erkennung aller Entitäten im Paket
            Reflections reflections = new Reflections("org.mjannek.sport.buli.database.table");
            for (Class<?> clazz : reflections.getTypesAnnotatedWith(Entity.class)) {
                configuration.addAnnotatedClass(clazz);
            }
            return configuration.buildSessionFactory();
        } catch (Throwable ex) {
            throw new ExceptionInInitializerError(ex);
        }
    }

    public static Session openSessionWithFilters() {
        Session session = HibernateUtil.getSessionFactory().openSession();

        // Binde die Session an den aktuellen Kontext
        ManagedSessionContext.bind(session);

        // Aktiviere den Filter für gelöschte Einträge
        session.enableFilter("deletedFilter").setParameter("isDeleted", false);

        // Teste die Session
        System.out.println("Session geöffnet: " + session.isOpen());

        return session;
    }

    public static void closeSession() {
        Session session = ManagedSessionContext.unbind(sessionFactory);
        if (session != null) {
            session.close();
        }
    }

    public static void shutdown() {
        getSessionFactory().close();
    }

    public static Session openSession() {
        return getSessionFactory().openSession();
    }
}

Ich habe jetzt das Problem dass die Foreign Keys wie folgt heißen:
1737116337232.png
Wo ist mein Fehler?
In meiner Main Methode rufe ich folgendes auf:

Java:
public class EntityFinder {
    public static void main(String[] args) throws Exception {
       Session session = HibernateUtil.openSessionWithFilters();

    }
}

Die Tabellen werden richtig eerstellt, joch ohne beachtung der ForeignKey Benennung
 

MJannek

Bekanntes Mitglied
Ich habe es jetzt zum Teil Lösen können:

Java:
package org.mjannek.database;

import javax.persistence.Entity;
import lombok.Getter;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.context.internal.ManagedSessionContext;
import org.mjannek.sport.buli.database.config.naming.implicit.*;
import org.mjannek.sport.buli.database.config.naming.physical.*;
import org.reflections.Reflections;

public class HibernateUtil {
    @Getter
    private static final SessionFactory sessionFactory = buildSessionFactory();

    private static SessionFactory buildSessionFactory() {
        try {
            Configuration configuration = new Configuration();
            configuration.setImplicitNamingStrategy(new CustomImplicitNamingStrategy());
            configuration.setPhysicalNamingStrategy(new CustomPhysicalNamingStrategy());
            configuration.configure();

            // Automatische Erkennung aller Entitäten im Paket
            Reflections reflections = new Reflections("org.mjannek.sport.buli.database.table");
            for (Class<?> clazz : reflections.getTypesAnnotatedWith(Entity.class)) {
                configuration.addAnnotatedClass(clazz);
            }
            return configuration.buildSessionFactory();
        } catch (Throwable ex) {
            throw new ExceptionInInitializerError(ex);
        }
    }

    public static Session openSessionWithFilters() {
        Session session = HibernateUtil.getSessionFactory().openSession();

        // Binde die Session an den aktuellen Kontext
        ManagedSessionContext.bind(session);

        // Aktiviere den Filter für gelöschte Einträge
        session.enableFilter("deletedFilter").setParameter("isDeleted", false);

        // Teste die Session
        System.out.println("Session geöffnet: " + session.isOpen());

        return session;
    }

    public static void closeSession() {
        Session session = ManagedSessionContext.unbind(sessionFactory);
        if (session != null) {
            session.close();
        }
    }

    public static void shutdown() {
        getSessionFactory().close();
    }

    public static Session openSession() {
        return getSessionFactory().openSession();
    }
}


Java:
package org.mjannek.sport.buli.database.table;

/*
    Created by: Markus
    Creaded at: 07.01.2025
*/

import java.io.*;
import java.lang.reflect.*;
import java.sql.*;
import java.time.*;
import java.util.*;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.ForeignKey;
import javax.persistence.Table;
import javax.persistence.*;
import lombok.*;
import lombok.experimental.*;
import org.hibernate.annotations.*;
import org.mjannek.sport.buli.database.annotation.*;
import org.mjannek.sport.buli.database.listener.*;

@Entity
@Table(name = "users", uniqueConstraints = {
        @UniqueConstraint(name = "uk_user_name", columnNames = "user_name")
})
@EntityListeners(EncryptionListener.class)
@FilterDef(name = "deletedFilter", parameters = @ParamDef(name = "isDeleted", type = "boolean"))
@Filter(name = "deletedFilter", condition = "deleted = :isDeleted")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString
@SuperBuilder
public class User implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @EqualsAndHashCode.Include
    private Integer id;

    @Column(name = "user_name", updatable = false, nullable = false, columnDefinition = "varchar(64) CHARACTER SET utf8mb4")
    private String name;

    @Encrypt
    @Column(name = "password", nullable = false, columnDefinition = "varchar(64) CHARACTER SET utf8mb4")
    @ToString.Exclude
    private String password;

    @ManyToOne
    @JoinColumn(name = "user_status_id")
    private UserStatus userStatus;

    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<UserStatusHistory> userStatusHistories;

    @Column(name = "state_changeable")
    @Builder.Default
    @ToString.Exclude
    private Boolean stateChangeable = true;

    @Version
    @ToString.Exclude
    private Integer version;

    @Column(name = "created_at", updatable = false, nullable = false)
    @ToString.Exclude
    private Timestamp createdAt;

    @Column(name = "updated_at", nullable = false)
    @ToString.Exclude
    private Timestamp updatedAt;

    @Column(name = "deleted")
    @Builder.Default
    @ToString.Exclude
    private Boolean deleted = false;

    @Column(name = "deleted_at")
    @ToString.Exclude
    private Timestamp deletedAt;

    //Constructor to set id
    public User(Integer id) {
        this.id = id;
        this.deleted = false;
    }

    // Copy-Constructor
    public User(User other) {
        if (other == null) {
            throw new IllegalArgumentException("Das zu kopierende Objekt darf nicht null sein.");
        }
        for (Field field : other.getClass().getDeclaredFields()) {
            field.setAccessible(true);
            try {
                field.set(this, field.get(other));
            } catch (IllegalAccessException e) {
                throw new RuntimeException("Fehler beim Kopieren des Felds: " + field.getName(), e);
            }
        }
    }

    @PrePersist
    protected void onCreate() {
        this.createdAt = Timestamp.valueOf(LocalDateTime.now());
        this.updatedAt = Timestamp.valueOf(LocalDateTime.now());
    }

    @PreUpdate
    protected void onUpdate() {
        this.updatedAt = Timestamp.valueOf(LocalDateTime.now());
    }

    public void markAsDeleted() {
        this.deleted = true;
        this.updatedAt = Timestamp.valueOf(LocalDateTime.now());
        this.deletedAt = Timestamp.valueOf(LocalDateTime.now());
    }

    public void restore() {
        this.deleted = false;
        this.deletedAt = null;
        this.updatedAt = Timestamp.valueOf(LocalDateTime.now());
    }

    public String getTableName() {
        Table tableAnnotation = this.getClass().getAnnotation(Table.class);
        if (tableAnnotation != null) {
            return tableAnnotation.name();
        }

        return this.getClass().getSimpleName();
}

    public void print(){
        System.out.println(this);
    }
}


Java:
package org.mjannek.sport.buli.database.table;

/*
    Created by: Markus
    Creaded at: 17.01.2025
*/

import java.sql.*;
import java.time.*;
import javax.persistence.*;
import javax.persistence.Entity;
import javax.persistence.Table;
import lombok.*;
import lombok.experimental.*;
import java.io.*;
import java.lang.reflect.*;
import org.hibernate.annotations.*;
import org.mjannek.sport.buli.database.helper.*;

@Entity
@Table(name = "testEntities")
@Getter
@Setter
@FilterDef(name = "deletedFilter", parameters = @ParamDef(name = "isDeleted", type = "boolean"))
@Filter(name = "deletedFilter", condition = "deleted = :isDeleted")
@NoArgsConstructor
@AllArgsConstructor
@ToString
@SuperBuilder
public class TestEntity implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @EqualsAndHashCode.Include
    @ToString.Exclude
    private Integer id;

    @Version
    @ToString.Exclude
    private Integer version;

    @Column(name = "created_at", nullable = false)
    @ToString.Exclude
    private Timestamp createdAt;

    @ManyToOne
    @JoinColumn(name = "created_by", updatable = false)
    @ToString.Exclude
    private User createdBy;

    @Column(name = "updated_at", nullable = false)
    @ToString.Exclude
    private Timestamp updatedAt;

    @ManyToOne
    @JoinColumn(name = "updated_by", updatable = false)
    @ToString.Exclude
    private User updatedBy;

    @Column(name = "deleted")
    @Builder.Default
    @ToString.Exclude
    private Boolean deleted = false;

    @Column(name = "deleted_at")
    @ToString.Exclude
    private Timestamp deletedAt;

    @ManyToOne
    @JoinColumn(name = "deleted_by", updatable = false)
    @ToString.Exclude
    private User deletedBy;

    //Constructor to set id
    public TestEntity(Integer id) {
        this.id = id;
        this.deleted = false;
    }

    // Copy-Constructor
    public TestEntity(TestEntity other) {
        if (other == null) {
            throw new IllegalArgumentException("Das zu kopierende Objekt darf nicht null sein.");
        }
        for (Field field : other.getClass().getDeclaredFields()) {
            field.setAccessible(true);
            try {
                field.set(this, field.get(other));
            } catch (IllegalAccessException e) {
                throw new RuntimeException("Fehler beim Kopieren des Felds: " + field.getName(), e);
            }
        }
    }

    @PrePersist
    protected void onCreate() {
        this.createdAt = Timestamp.valueOf(LocalDateTime.now());
        this.createdBy = UserContext.getCurrentUser();
        this.updatedAt = Timestamp.valueOf(LocalDateTime.now());
        this.updatedBy = UserContext.getCurrentUser();
    }

    @PreUpdate
    protected void onUpdate() {
        this.updatedBy = UserContext.getCurrentUser();
        this.updatedAt = Timestamp.valueOf(LocalDateTime.now());
    }

    public void markAsDeleted(User deletedBy) {
        this.deleted = true;
        this.updatedAt = Timestamp.valueOf(LocalDateTime.now());
        this.updatedBy = deletedBy;
        this.deletedAt = Timestamp.valueOf(LocalDateTime.now());
        this.deletedBy = deletedBy;
    }

    public void restore(User restoredBy) {
        this.deleted = false;
        this.deletedAt = null;
        this.deletedBy = null;
        this.updatedAt = Timestamp.valueOf(LocalDateTime.now());
        this.updatedBy = restoredBy;
    }

    public String getTableName() {
        Table tableAnnotation = this.getClass().getAnnotation(Table.class);
        if (tableAnnotation != null) {
            return tableAnnotation.name();
        }

        return this.getClass().getSimpleName();
    }

    public void print() {
        System.out.println(this);
    }
}

Jedoch erhalte ich


Code:
Caused by: java.lang.ClassCastException: class java.util.Collections$EmptyList cannot be cast to class java.lang.CharSequence (java.util.Collections$EmptyList and java.lang.CharSequence are in module java.base of loader 'bootstrap')

Was mache ich falsch
Außerdem werden die Tabellen
namei18n und name_i_18_n erzeugt, dabei ist name variabel
 

MJannek

Bekanntes Mitglied
Ich habe es jetzt zum Teil Lösen können:

Java:
package org.mjannek.database;

import javax.persistence.Entity;
import lombok.Getter;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.context.internal.ManagedSessionContext;
import org.mjannek.sport.buli.database.config.naming.implicit.*;
import org.mjannek.sport.buli.database.config.naming.physical.*;
import org.reflections.Reflections;

public class HibernateUtil {
    @Getter
    private static final SessionFactory sessionFactory = buildSessionFactory();

    private static SessionFactory buildSessionFactory() {
        try {
            Configuration configuration = new Configuration();
            configuration.setImplicitNamingStrategy(new CustomImplicitNamingStrategy());
            configuration.setPhysicalNamingStrategy(new CustomPhysicalNamingStrategy());
            configuration.configure();

            // Automatische Erkennung aller Entitäten im Paket
            Reflections reflections = new Reflections("org.mjannek.sport.buli.database.table");
            for (Class<?> clazz : reflections.getTypesAnnotatedWith(Entity.class)) {
                configuration.addAnnotatedClass(clazz);
            }
            return configuration.buildSessionFactory();
        } catch (Throwable ex) {
            throw new ExceptionInInitializerError(ex);
        }
    }

    public static Session openSessionWithFilters() {
        Session session = HibernateUtil.getSessionFactory().openSession();

        // Binde die Session an den aktuellen Kontext
        ManagedSessionContext.bind(session);

        // Aktiviere den Filter für gelöschte Einträge
        session.enableFilter("deletedFilter").setParameter("isDeleted", false);

        // Teste die Session
        System.out.println("Session geöffnet: " + session.isOpen());

        return session;
    }

    public static void closeSession() {
        Session session = ManagedSessionContext.unbind(sessionFactory);
        if (session != null) {
            session.close();
        }
    }

    public static void shutdown() {
        getSessionFactory().close();
    }

    public static Session openSession() {
        return getSessionFactory().openSession();
    }
}


Java:
package org.mjannek.sport.buli.database.table;

/*
    Created by: Markus
    Creaded at: 07.01.2025
*/

import java.io.*;
import java.lang.reflect.*;
import java.sql.*;
import java.time.*;
import java.util.*;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.ForeignKey;
import javax.persistence.Table;
import javax.persistence.*;
import lombok.*;
import lombok.experimental.*;
import org.hibernate.annotations.*;
import org.mjannek.sport.buli.database.annotation.*;
import org.mjannek.sport.buli.database.listener.*;

@Entity
@Table(name = "users", uniqueConstraints = {
        @UniqueConstraint(name = "uk_user_name", columnNames = "user_name")
})
@EntityListeners(EncryptionListener.class)
@FilterDef(name = "deletedFilter", parameters = @ParamDef(name = "isDeleted", type = "boolean"))
@Filter(name = "deletedFilter", condition = "deleted = :isDeleted")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString
@SuperBuilder
public class User implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @EqualsAndHashCode.Include
    private Integer id;

    @Column(name = "user_name", updatable = false, nullable = false, columnDefinition = "varchar(64) CHARACTER SET utf8mb4")
    private String name;

    @Encrypt
    @Column(name = "password", nullable = false, columnDefinition = "varchar(64) CHARACTER SET utf8mb4")
    @ToString.Exclude
    private String password;

    @ManyToOne
    @JoinColumn(name = "user_status_id")
    private UserStatus userStatus;

    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<UserStatusHistory> userStatusHistories;

    @Column(name = "state_changeable")
    @Builder.Default
    @ToString.Exclude
    private Boolean stateChangeable = true;

    @Version
    @ToString.Exclude
    private Integer version;

    @Column(name = "created_at", updatable = false, nullable = false)
    @ToString.Exclude
    private Timestamp createdAt;

    @Column(name = "updated_at", nullable = false)
    @ToString.Exclude
    private Timestamp updatedAt;

    @Column(name = "deleted")
    @Builder.Default
    @ToString.Exclude
    private Boolean deleted = false;

    @Column(name = "deleted_at")
    @ToString.Exclude
    private Timestamp deletedAt;

    //Constructor to set id
    public User(Integer id) {
        this.id = id;
        this.deleted = false;
    }

    // Copy-Constructor
    public User(User other) {
        if (other == null) {
            throw new IllegalArgumentException("Das zu kopierende Objekt darf nicht null sein.");
        }
        for (Field field : other.getClass().getDeclaredFields()) {
            field.setAccessible(true);
            try {
                field.set(this, field.get(other));
            } catch (IllegalAccessException e) {
                throw new RuntimeException("Fehler beim Kopieren des Felds: " + field.getName(), e);
            }
        }
    }

    @PrePersist
    protected void onCreate() {
        this.createdAt = Timestamp.valueOf(LocalDateTime.now());
        this.updatedAt = Timestamp.valueOf(LocalDateTime.now());
    }

    @PreUpdate
    protected void onUpdate() {
        this.updatedAt = Timestamp.valueOf(LocalDateTime.now());
    }

    public void markAsDeleted() {
        this.deleted = true;
        this.updatedAt = Timestamp.valueOf(LocalDateTime.now());
        this.deletedAt = Timestamp.valueOf(LocalDateTime.now());
    }

    public void restore() {
        this.deleted = false;
        this.deletedAt = null;
        this.updatedAt = Timestamp.valueOf(LocalDateTime.now());
    }

    public String getTableName() {
        Table tableAnnotation = this.getClass().getAnnotation(Table.class);
        if (tableAnnotation != null) {
            return tableAnnotation.name();
        }

        return this.getClass().getSimpleName();
}

    public void print(){
        System.out.println(this);
    }
}


Java:
package org.mjannek.sport.buli.database.table;

/*
    Created by: Markus
    Creaded at: 17.01.2025
*/

import java.sql.*;
import java.time.*;
import javax.persistence.*;
import javax.persistence.Entity;
import javax.persistence.Table;
import lombok.*;
import lombok.experimental.*;
import java.io.*;
import java.lang.reflect.*;
import org.hibernate.annotations.*;
import org.mjannek.sport.buli.database.helper.*;

@Entity
@Table(name = "testEntities")
@Getter
@Setter
@FilterDef(name = "deletedFilter", parameters = @ParamDef(name = "isDeleted", type = "boolean"))
@Filter(name = "deletedFilter", condition = "deleted = :isDeleted")
@NoArgsConstructor
@AllArgsConstructor
@ToString
@SuperBuilder
public class TestEntity implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @EqualsAndHashCode.Include
    @ToString.Exclude
    private Integer id;

    @Version
    @ToString.Exclude
    private Integer version;

    @Column(name = "created_at", nullable = false)
    @ToString.Exclude
    private Timestamp createdAt;

    @ManyToOne
    @JoinColumn(name = "created_by", updatable = false)
    @ToString.Exclude
    private User createdBy;

    @Column(name = "updated_at", nullable = false)
    @ToString.Exclude
    private Timestamp updatedAt;

    @ManyToOne
    @JoinColumn(name = "updated_by", updatable = false)
    @ToString.Exclude
    private User updatedBy;

    @Column(name = "deleted")
    @Builder.Default
    @ToString.Exclude
    private Boolean deleted = false;

    @Column(name = "deleted_at")
    @ToString.Exclude
    private Timestamp deletedAt;

    @ManyToOne
    @JoinColumn(name = "deleted_by", updatable = false)
    @ToString.Exclude
    private User deletedBy;

    //Constructor to set id
    public TestEntity(Integer id) {
        this.id = id;
        this.deleted = false;
    }

    // Copy-Constructor
    public TestEntity(TestEntity other) {
        if (other == null) {
            throw new IllegalArgumentException("Das zu kopierende Objekt darf nicht null sein.");
        }
        for (Field field : other.getClass().getDeclaredFields()) {
            field.setAccessible(true);
            try {
                field.set(this, field.get(other));
            } catch (IllegalAccessException e) {
                throw new RuntimeException("Fehler beim Kopieren des Felds: " + field.getName(), e);
            }
        }
    }

    @PrePersist
    protected void onCreate() {
        this.createdAt = Timestamp.valueOf(LocalDateTime.now());
        this.createdBy = UserContext.getCurrentUser();
        this.updatedAt = Timestamp.valueOf(LocalDateTime.now());
        this.updatedBy = UserContext.getCurrentUser();
    }

    @PreUpdate
    protected void onUpdate() {
        this.updatedBy = UserContext.getCurrentUser();
        this.updatedAt = Timestamp.valueOf(LocalDateTime.now());
    }

    public void markAsDeleted(User deletedBy) {
        this.deleted = true;
        this.updatedAt = Timestamp.valueOf(LocalDateTime.now());
        this.updatedBy = deletedBy;
        this.deletedAt = Timestamp.valueOf(LocalDateTime.now());
        this.deletedBy = deletedBy;
    }

    public void restore(User restoredBy) {
        this.deleted = false;
        this.deletedAt = null;
        this.deletedBy = null;
        this.updatedAt = Timestamp.valueOf(LocalDateTime.now());
        this.updatedBy = restoredBy;
    }

    public String getTableName() {
        Table tableAnnotation = this.getClass().getAnnotation(Table.class);
        if (tableAnnotation != null) {
            return tableAnnotation.name();
        }

        return this.getClass().getSimpleName();
    }

    public void print() {
        System.out.println(this);
    }
}

Jedoch erhalte ich


Code:
Caused by: java.lang.ClassCastException: class java.util.Collections$EmptyList cannot be cast to class java.lang.CharSequence (java.util.Collections$EmptyList and java.lang.CharSequence are in module java.base of loader 'bootstrap')

Was mache ich falsch
Außerdem werden die Tabellen
namei18n und name_i_18_n erzeugt, dabei ist name variabel
Problem gelöst: ein Table Name war leer
 

MJannek

Bekanntes Mitglied
Ich habe noch mal eine kurze Frage bzgl. effizienter Umsetzung: Ich habe folgende Basisklassen:
Java:
package org.mjannek.sport.buli.database.table;

import java.io.*;
import java.lang.reflect.*;
import java.sql.*;
import java.time.*;
import java.util.*;
import javax.persistence.Table;
import javax.persistence.*;
import lombok.*;
import lombok.experimental.*;
import org.hibernate.annotations.*;

/*
    Created by: Markus
    Creaded at: 17.01.2025
*/
@MappedSuperclass
@Getter
@Setter
@FilterDef(name = "deletedFilter", parameters = @ParamDef(name = "isDeleted", type = "boolean"))
@Filter(name = "deletedFilter", condition = "deleted = :isDeleted")
@NoArgsConstructor
@AllArgsConstructor
@ToString
@SuperBuilder
@EqualsAndHashCode
public class BaseEntityUser implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @EqualsAndHashCode.Include
    private Integer id;
    
    @Version
    @ToString.Exclude
    private Integer version;

    @Column(name = "created_at", nullable = false)
    @ToString.Exclude
    private Timestamp createdAt;

    @Column(name = "updated_at", nullable = false)
    @ToString.Exclude
    private Timestamp updatedAt;

    @Column(name = "deleted")
    @Builder.Default
    @ToString.Exclude
    private Boolean deleted = false;

    @Column(name = "deleted_at")
    @ToString.Exclude
    private Timestamp deletedAt;

    //Constructor to set id
    public BaseEntityUser(Integer id) {
        this.id = id;
        this.deleted = false;
    }

    // Copy-Constructor
    public BaseEntityUser(BaseEntityUser other) {
        if (other == null) {
            throw new IllegalArgumentException("Das zu kopierende Objekt darf nicht null sein.");
        }
        for (Field field : other.getClass().getDeclaredFields()) {
            field.setAccessible(true);
            try {
                field.set(this, field.get(other));
            } catch (IllegalAccessException e) {
                throw new RuntimeException("Fehler beim Kopieren des Felds: " + field.getName(), e);
            }
        }
    }

    @PrePersist
    protected void onCreate() {
        this.createdAt = Timestamp.valueOf(LocalDateTime.now());
        this.updatedAt = Timestamp.valueOf(LocalDateTime.now());
    }

    @PreUpdate
    protected void onUpdate() {
        this.updatedAt = Timestamp.valueOf(LocalDateTime.now());
    }

    public void markAsDeleted() {
        this.deleted = true;
        this.updatedAt = Timestamp.valueOf(LocalDateTime.now());
        this.deletedAt = Timestamp.valueOf(LocalDateTime.now());
    }

    public void restore() {
        this.deleted = false;
        this.deletedAt = null;
        this.updatedAt = Timestamp.valueOf(LocalDateTime.now());
    }

    public String getTableName() {
        Table tableAnnotation = this.getClass().getAnnotation(Table.class);
        if (tableAnnotation != null) {
            return tableAnnotation.name();
        }

        return this.getClass().getSimpleName();
    }

    public void print() {
        System.out.println(this);
    }
}

und
Java:
package org.mjannek.sport.buli.database.table;


import javax.persistence.*;
import lombok.*;
import lombok.experimental.*;
import org.hibernate.annotations.*;
import org.mjannek.sport.buli.database.helper.*;

/*
    Created by: Markus
    Creaded at: 17.01.2025
*/
@MappedSuperclass
@Getter
@Setter
@FilterDef(name = "deletedFilter", parameters = @ParamDef(name = "isDeleted", type = "boolean"))
@Filter(name = "deletedFilter", condition = "deleted = :isDeleted")
@NoArgsConstructor
@AllArgsConstructor
@ToString(callSuper = true)
@SuperBuilder
@EqualsAndHashCode(callSuper = true)
public class BaseEntity extends BaseEntityUser {

    @ManyToOne
    @JoinColumn(name = "created_by", updatable = false)
    @ToString.Exclude
    private User createdBy;

    @ManyToOne
    @JoinColumn(name = "updated_by", updatable = false)
    @ToString.Exclude
    private User updatedBy;

    @ManyToOne
    @JoinColumn(name = "deleted_by", updatable = false)
    @ToString.Exclude
    private User deletedBy;

    //Constructor to set id
    public BaseEntity(Integer id) {
        super(id);
    }

    // Copy-Constructor
    public BaseEntity(BaseEntity other) {
        super(other);
    }

    protected void onCreate() {
        super.onCreate();
        this.createdBy = UserContext.getCurrentUser();
        this.updatedBy = UserContext.getCurrentUser();
    }

    protected void onUpdate() {
        super.onUpdate();
        this.updatedBy = UserContext.getCurrentUser();
    }

    public void markAsDeleted(User deletedBy) {
        super.markAsDeleted();
        this.updatedBy = deletedBy;
        this.deletedBy = deletedBy;
    }

    public void restore(User restoredBy) {
        super.restore();
        this.deletedBy = null;
        this.updatedBy = restoredBy;
    }
}

Ich habe mir für Übersetungen u.a. folgende Klassen erstellt:
Code:
package org.mjannek.sport.buli.database.table;

/*
    Created by: Markus
    Creaded at: 07.01.2025
*/

import java.sql.*;
import java.util.*;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.*;
import lombok.*;
import lombok.experimental.*;
import org.hibernate.annotations.*;

@Entity
@Table(name = "languages", uniqueConstraints = {
        @UniqueConstraint(columnNames = "language_name")
}
)
@FilterDef(name = "deletedFilter", parameters = @ParamDef(name = "isDeleted", type = "boolean"))
@Filter(name = "deletedFilter", condition = "deleted = :isDeleted")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString(callSuper = true)
@SuperBuilder
@EqualsAndHashCode(callSuper = true)
public class Language extends BaseEntityUser {

    @Column(name = "lower_locale_name", nullable = false)
    private Locale lowerLocaleName;

    @Column(name = "upper_locale_name", nullable = false)
    private Locale upperLocaleName;

    @Column(name = "language_name", nullable = false, columnDefinition = "varchar(255) CHARACTER SET utf8mb4")
    private String languageName;

    @Column(name = "description", columnDefinition = "varchar(255) CHARACTER SET utf8mb4")
    private String description;

    @OneToMany(mappedBy = "languageToTranslate", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<LanguageI18n> translations;

    //Constructor to set id
    public Language(Integer id) {
        super(id);
    }

    // Copy-Constructor
    public Language(Language other) {
        super(other);
    }

}

Java:
package org.mjannek.sport.buli.database.table;

import java.sql.*;
import java.time.*;
import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.*;
import lombok.*;
import lombok.experimental.*;
import org.hibernate.annotations.*;

@Entity
@Table(name = "languagesI18n", uniqueConstraints = {
        @UniqueConstraint(columnNames = {"language_id"})
})
@FilterDef(name = "deletedFilter", parameters = @ParamDef(name = "isDeleted", type = "boolean"))
@Filter(name = "deletedFilter", condition = "deleted = :isDeleted")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString(callSuper = true)
@SuperBuilder
@EqualsAndHashCode(callSuper = true)
public class LanguageI18n extends BaseEntity {

    @ManyToOne
    @JoinColumn(name = "language_to_translate_id")
    private Language languageToTranslate;

    @ManyToOne
    @JoinColumn(name = "language_id")
    private Language language;

    @Column(name = "language_name", nullable = false, columnDefinition = "varchar(255) CHARACTER SET utf8mb4")
    private String languageName;

    @Column(name = "language_description", columnDefinition = "varchar(255) CHARACTER SET utf8mb4")
    private String languageDescription;

    // Constructor to set id
    public LanguageI18n(Integer id) {
        super(id);
    }

    // Copy-Constructor
    public LanguageI18n(LanguageI18n other) {
        super(other);
    }

}


Java:
package org.mjannek.sport.buli.database.table;

/*
    Created by: Markus
    Creaded at: 10.01.2025
*/

import java.util.*;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.*;
import lombok.*;
import lombok.experimental.*;
import org.hibernate.annotations.*;

@Entity
@Table(name = "cities", uniqueConstraints = {
        @UniqueConstraint(columnNames = {"nation_id", "city_name"})
}
)
@FilterDef(name = "deletedFilter", parameters = @ParamDef(name = "isDeleted", type = "boolean"))
@Filter(name = "deletedFilter", condition = "deleted = :isDeleted")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString(callSuper = true)
@SuperBuilder
@EqualsAndHashCode(callSuper = true)
public class City extends BaseEntity {

    @ManyToOne
    @JoinColumn(name = "nation_id")
    private Nation nation;

    @Column(name = "city_name", nullable = false, columnDefinition = "varchar(255) CHARACTER SET utf8mb4")
    private String cityName;

    @Column(name = "description", columnDefinition = "varchar(255) CHARACTER SET utf8mb4")
    private String description;

    @OneToMany(mappedBy = "city", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<CityI18n> translations;

    //Constructor to set id
    public City(Integer id) {
        super(id);
    }

    // Copy-Constructor
    public City(City other) {
        super(other);
    }

}

Java:
package org.mjannek.sport.buli.database.table;

/*
    Created by: Markus
    Creaded at: 10.01.2025
*/

import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.*;
import lombok.*;
import lombok.experimental.*;
import org.hibernate.annotations.*;

@Entity
@Table(name = "citiesI18n", uniqueConstraints = {
        @UniqueConstraint(columnNames = {"city_id", "language_id"})
}
)
@FilterDef(name = "deletedFilter", parameters = @ParamDef(name = "isDeleted", type = "boolean"))
@Filter(name = "deletedFilter", condition = "deleted = :isDeleted")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString(callSuper = true)
@SuperBuilder
@EqualsAndHashCode(callSuper = true)
public class CityI18n extends BaseEntity {

    @ManyToOne
    @JoinColumn(name = "city_id")
    private City city;

    @ManyToOne
    @JoinColumn(name = "language_id")
    private Language language;

    @Column(name = "city_name", nullable = false, columnDefinition = "varchar(255) CHARACTER SET utf8mb4")
    private String cityName;

    @Column(name = "city_description", columnDefinition = "varchar(255) CHARACTER SET utf8mb4")
    private String cityDescription;

    //Constructor to set id
    public CityI18n(Integer id) {
        super(id);
    }

    // Copy-Constructor
    public CityI18n(CityI18n other) {
        super(other);
    }

}

Java:
package org.mjannek.sport.buli.database.table;

/*
    Created by: Markus
    Creaded at: 10.01.2025
*/

import java.time.*;
import java.util.*;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.*;
import lombok.*;
import lombok.experimental.*;
import org.hibernate.annotations.*;
import org.mjannek.sport.buli.database.type.*;

@Entity
@Table(name = "clubs", uniqueConstraints = {
        @UniqueConstraint(columnNames = "club_name")
}
)
@FilterDef(name = "deletedFilter", parameters = @ParamDef(name = "isDeleted", type = "boolean"))
@Filter(name = "deletedFilter", condition = "deleted = :isDeleted")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString(callSuper = true)
@SuperBuilder
@EqualsAndHashCode(callSuper = true)
public class Club extends BaseEntity {

    @Column(name = "long_name", nullable = false, columnDefinition = "varchar(255) CHARACTER SET utf8mb4")
    private String longName;

    @Column(name = "short_name", nullable = false, columnDefinition = "varchar(3) CHARACTER SET utf8mb4")
    private String shortName;

    @Column(name = "foreground_color", columnDefinition = "varchar(7) CHARACTER SET utf8mb4")
    private String foregroundColor;

    @Column(name = "background_color", columnDefinition = "varchar(7) CHARACTER SET utf8mb4")
    private String backgroundColor;

    @Column(name = "founding_date")
    private LocalDate foundingDate;

    @Column(name = "club_name", nullable = false, columnDefinition = "varchar(255) CHARACTER SET utf8mb4")
    private String clubName;

    @ManyToOne
    @JoinColumn(name = "city_id")
    private City city;

    @Column(name = "full_name", columnDefinition = "varchar(255) CHARACTER SET utf8mb4")
    private String fullName;

    @Column(name = "link", columnDefinition = "varchar(255) CHARACTER SET utf8mb4")
    private Link link;

    @Column(name = "link_name", columnDefinition = "varchar(255) CHARACTER SET utf8mb4")
    private String linkName;

    @Column(name = "website")
    private String website;

    @Column(name = "description", columnDefinition = "varchar(255) CHARACTER SET utf8mb4")
    private String description;

    @ManyToOne
    @JoinColumn(name = "club_type_id")
    private ClubType clubType;

    @OneToMany(mappedBy = "club", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<ClubI18n> translations;

    //Constructor to set id
    public Club(Integer id) {
        super(id);
    }

    // Copy-Constructor
    public Club(Club other) {
        super(other);
    }

}

Java:
package org.mjannek.sport.buli.database.table;

/*
    Created by: Markus
    Creaded at: 10.01.2025
*/

import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.*;
import lombok.*;
import lombok.experimental.*;
import org.hibernate.annotations.*;

@Entity
@Table(name = "clubsI18n", uniqueConstraints = {
        @UniqueConstraint(columnNames = {"club_id", "language_id"})
}
)
@FilterDef(name = "deletedFilter", parameters = @ParamDef(name = "isDeleted", type = "boolean"))
@Filter(name = "deletedFilter", condition = "deleted = :isDeleted")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString(callSuper = true)
@SuperBuilder
@EqualsAndHashCode(callSuper = true)
public class ClubI18n extends BaseEntity {

    @ManyToOne
    @JoinColumn(name = "club_id")
    private Club club;

    @ManyToOne
    @JoinColumn(name = "language_id")
    private Language language;

    @Column(name = "long_name", nullable = false, columnDefinition = "varchar(255) CHARACTER SET utf8mb4")
    private String longName;

    @Column(name = "club_name", nullable = false)
    private String clubName;

    @Column(name = "full_name", columnDefinition = "varchar(255) CHARACTER SET utf8mb4")
    private String fullName;

    @Column(name = "club_description", columnDefinition = "varchar(255) CHARACTER SET utf8mb4")
    private String clubDescription;

    //Constructor to set id
    public ClubI18n(Integer id) {
        super(id);
    }

    // Copy-Constructor
    public ClubI18n(ClubI18n other) {
        super(other);
    }

}

Java:
package org.mjannek.sport.buli.database.table;

/*
    Created by: Markus
    Creaded at: 12.01.2025
*/

import java.util.*;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.*;
import lombok.*;
import lombok.experimental.*;
import org.hibernate.annotations.*;


@Entity
@Table(name = "matchEventTypes", uniqueConstraints = {
        @UniqueConstraint(columnNames = "match_event_type_name")
}
)
@FilterDef(name = "deletedFilter", parameters = @ParamDef(name = "isDeleted", type = "boolean"))
@Filter(name = "deletedFilter", condition = "deleted = :isDeleted")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString(callSuper = true)
@SuperBuilder
@EqualsAndHashCode(callSuper = true)
public class MatchEventType extends BaseEntity {

    @Column(name = "match_event_type_name", nullable = false, columnDefinition = "varchar(255) CHARACTER SET utf8mb4")
    private String matchEventTypeName;

    @Column(name = "description", columnDefinition = "varchar(255) CHARACTER SET utf8mb4")
    private String description;

    @OneToMany(mappedBy = "matchEventType", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<MatchEventTypeI18n> translations;

    //Constructor to set id
    public MatchEventType(Integer id) {
        super(id);
    }

    // Copy-Constructor
    public MatchEventType(MatchEventType other) {
        super(other);
    }

}
Java:
package org.mjannek.sport.buli.database.table;

/*
    Created by: Markus
    Creaded at: 12.01.2025
*/

import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.*;
import lombok.*;
import lombok.experimental.*;
import org.hibernate.annotations.*;

@Entity
@Table(name = "matchEventTypesI18n", uniqueConstraints = {
        @UniqueConstraint(columnNames = {"match_event_type_id", "language_id"})
}
)
@FilterDef(name = "deletedFilter", parameters = @ParamDef(name = "isDeleted", type = "boolean"))
@Filter(name = "deletedFilter", condition = "deleted = :isDeleted")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString(callSuper = true)
@SuperBuilder
@EqualsAndHashCode(callSuper = true)
public class MatchEventTypeI18n extends BaseEntity {

    @ManyToOne
    @JoinColumn(name = "match_event_type_id")
    private MatchEventType matchEventType;

    @ManyToOne
    @JoinColumn(name = "language_id")
    private Language language;

    @Column(name = "match_event_type_name", nullable = false, columnDefinition = "varchar(255) CHARACTER SET utf8mb4")
    private String matchEventTypeName;

    @Column(name = "match_event_type_description", columnDefinition = "varchar(255) CHARACTER SET utf8mb4")
    private String matchEventTypeDescription;

    //Constructor to set id
    public MatchEventTypeI18n(Integer id) {
        super(id);
    }

    // Copy-Constructor
    public MatchEventTypeI18n(MatchEventTypeI18n other) {
        super(other);
    }

}


Jetzt meine Frage wäre es sinnvoller statt der jetztigen Umsetzung mit Klasse Name und Klasse NameI18n nur die Klassen Name zu erzeugen und innerhalb dieser auf eine neue Klasse bzw. Tabelle Translation? Wie müsste das umgesetzt werden, da ich zum einen in jeder Klasse Name eine beliebige Anzahl von Werten übersetzen möchte. Z.B. matchEventTypeName und matchEventTypeDescription für MatchEventType bzw. longName, clubName. fullName und clubDescription für Club?
Die Spalten in Language sollen nach Möglichkeit so bestehen bleiben.

Vielen Dank für die Tipps.

Markus
 

MJannek

Bekanntes Mitglied
Ich habe es jetzt erstmal wie folgt umgesetzt, weiß aber nicht ob es noch besser geht:

Java:
package org.mjannek.sport.buli.database.list;

/*
    Created by: Markus
    Creaded at: 20.01.2025
*/
import java.util.*;
import java.util.Map;
import org.mjannek.sport.buli.database.table.*;

public interface TranslationList extends List<Translation> {

    // Suche alle Übersetzungen für ein bestimmtes Feld
    List<Translation> findByFieldName(String fieldName);

    // Suche alle Übersetzungen für eine bestimmte Sprache
    List<Translation> findByLanguage(Language language);

    // Finde eine spezifische Übersetzung basierend auf Feldnamen und Sprache
    Translation findTranslation(String fieldName, Language language);

    // Fügt eine Übersetzung hinzu oder aktualisiert eine vorhandene
    void addOrUpdateTranslation(Translation translation);

    // Entferne eine Übersetzung basierend auf Feldnamen und Sprache
    boolean removeTranslation(String fieldName, Language language);

    // Überprüfen, ob eine Übersetzung für ein bestimmtes Feld und eine bestimmte Sprache existiert
    boolean exists(String fieldName, Language language);

    // Konvertiere die Liste in eine Map mit Schlüssel=Feldname:Sprache, Wert=Übersetzungswert
    Map<String, String> toMap();

    // Gruppiere die Übersetzungen nach Sprache
    Map<Language, List<Translation>> groupByLanguage();
}

Java:
package org.mjannek.sport.buli.database.list;

/*
    Created by: Markus
    Creaded at: 20.01.2025
*/
import java.util.*;
import java.util.stream.Collectors;
import org.mjannek.sport.buli.database.table.*;

public class TranslationListImpl extends ArrayList<Translation> implements TranslationList {

    @Override
    public List<Translation> findByFieldName(String fieldName) {
        return this.stream()
                .filter(translation -> translation.getFieldName().equals(fieldName))
                .collect(Collectors.toList());
    }

    @Override
    public List<Translation> findByLanguage(Language language) {
        return this.stream()
                .filter(translation -> translation.getLanguage().equals(language))
                .collect(Collectors.toList());
    }

    @Override
    public Translation findTranslation(String fieldName, Language language) {
        return this.stream()
                .filter(translation -> translation.getFieldName().equals(fieldName)
                        && translation.getLanguage().equals(language))
                .findFirst()
                .orElse(null);
    }

    @Override
    public void addOrUpdateTranslation(Translation translation) {
        Translation existing = findTranslation(translation.getFieldName(), translation.getLanguage());
        if (existing != null) {
            existing.setTranslationValue(translation.getTranslationValue());
        } else {
            this.add(translation);
        }
    }

    @Override
    public boolean removeTranslation(String fieldName, Language language) {
        return this.removeIf(translation -> translation.getFieldName().equals(fieldName)
                && translation.getLanguage().equals(language));
    }

    @Override
    public boolean exists(String fieldName, Language language) {
        return this.stream()
                .anyMatch(translation -> translation.getFieldName().equals(fieldName)
                        && translation.getLanguage().equals(language));
    }

    @Override
    public Map<String, String> toMap() {
        return this.stream()
                .collect(Collectors.toMap(
                        translation -> translation.getFieldName() + ":" + translation.getLanguage().getLanguageName(),
                        Translation::getTranslationValue
                ));
    }

    @Override
    public Map<Language, List<Translation>> groupByLanguage() {
        return this.stream()
                .collect(Collectors.groupingBy(Translation::getLanguage));
    }
}


Java:
package org.mjannek.sport.buli.database.table;

/*
    Created by: Markus
    Creaded at: 20.01.2025
*/

import javax.persistence.Entity;
import javax.persistence.*;
import javax.persistence.Table;
import lombok.*;
import lombok.experimental.*;
import org.hibernate.annotations.*;

@Entity
@Table(name = "translations", uniqueConstraints = {@UniqueConstraint(columnNames = {"entity_id", "language_id", "entity_type"})})
@Getter
@Setter
@FilterDef(name = "deletedFilter", parameters = @ParamDef(name = "isDeleted", type = "boolean"))
@Filter(name = "deletedFilter", condition = "deleted = :isDeleted")
@NoArgsConstructor
@AllArgsConstructor
@ToString
@SuperBuilder
public class Translation extends BaseEntityUser {

    @Column(name = "entity_id", nullable = false)
    private Integer entityId;

    @Column(name = "entity_type", nullable = false)
    private String entityType;

    @ManyToOne
    @JoinColumn(name = "language_id", nullable = false)
    private Language language;

    @Column(name = "field_name", nullable = false)
    private String fieldName;

    @Column(name = "translation_value", nullable = false)
    private String translationValue;

    //Constructor to set id
    public Translation(Integer id) {
        super(id);
    }

    // Copy-Constructor
    public Translation(Translation other) {
        super(other);
    }
}

Java:
package org.mjannek.sport.buli.database.table;

/*
    Created by: Markus
    Creaded at: 07.01.2025
*/

import java.util.*;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.*;
import lombok.*;
import lombok.experimental.*;
import org.hibernate.annotations.*;
import org.mjannek.sport.buli.database.list.*;

@Entity
@Table(name = "languages", uniqueConstraints = {
        @UniqueConstraint(columnNames = {"lower_locale_name", "upper_locale_name", "language_name"})
}
)
@FilterDef(name = "deletedFilter", parameters = @ParamDef(name = "isDeleted", type = "boolean"))
@Filter(name = "deletedFilter", condition = "deleted = :isDeleted")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString(callSuper = true)
@SuperBuilder
@EqualsAndHashCode(callSuper = true)
public class Language extends BaseEntityUser {

    @Column(name = "lower_locale_name")
    private Locale lowerLocaleName;

    @Column(name = "upper_locale_name")
    private Locale upperLocaleName;

    @Column(name = "language_name")
    private String languageName;

    @Column(name = "description")
    private String description;

    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
    @JoinColumn(name = "entity_id", referencedColumnName = "id")
    @Builder.Default
    private List<Translation> translations = new TranslationListImpl();

    //Constructor to set id
    public Language(Integer id) {
        super(id);
    }

    // Copy-Constructor
    public Language(Language other) {
        super(other);
    }

}


Java:
package org.mjannek.sport.buli.database.table;

/*
    Created by: Markus
    Creaded at: 10.01.2025
*/

import java.util.*;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.*;
import lombok.*;
import lombok.experimental.*;
import org.hibernate.annotations.*;
import org.mjannek.sport.buli.database.list.*;

@Entity
@Table(name = "matchTypes", uniqueConstraints = @UniqueConstraint(columnNames = "match_type_name"))
@FilterDef(name = "deletedFilter", parameters = @ParamDef(name = "isDeleted", type = "boolean"))
@Filter(name = "deletedFilter", condition = "deleted = :isDeleted")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString(callSuper = true)
@SuperBuilder
@EqualsAndHashCode(callSuper = true)
public class MatchType extends BaseEntity {

    @Column(name = "match_type_name")
    private String matchTypeName;

    @Column(name = "description")
    private String description;

    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
    @JoinColumn(name = "entity_id", referencedColumnName = "id")
    @Builder.Default
    private List<Translation> translations = new TranslationListImpl();

    // Constructor to set id
    public MatchType(Integer id) {
        super(id);
    }

    // Copy-Constructor
    public MatchType(MatchType other) {
        super(other);
    }
}

Für Veränderungs- /Verbesserungsvorschläge bin ich offen.

Vielen Dank

Markus
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
F mysql_num_rows() - Äquivalent Datenbankprogrammierung 5

Ähnliche Java Themen


Oben