Probleme mit Records und JPA

8u3631984

Bekanntes Mitglied
Hallo ich bin mal wieder ein bisschen am Probieren und möchte gerne meine Entitäten mit Records implementieren.
Dazu habe ich mir mal ein Spielprojekt aufgesetzt. Ich verwende zuerst SpringDataJPA

Hier meine Klasse :
Ich habe es alles in einer Klasse geschrieben, da es ein kleines Spiel Projekt werden soll.

Java:
package com.example.spring.data.jpa;

import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.data.jpa.repository.JpaRepository;

import jakarta.persistence.Entity;
import jakarta.persistence.Id;

@SpringBootApplication
public class SpringDataJpaApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringDataJpaApplication.class, args);
    }

    @Bean
    ApplicationRunner demo(CustomerRepository repository) {
        return args -> {
            repository.saveAndFlush(new Customer(null, "Customer A"));
            repository.findAll().forEach(System.out::println);
        };

    }

}

/**
 * Customer
 * Long ID, String name
 */
@Entity
record Customer(@Id Long ID, String name) {
}

/**
 * CustomerRepository extends CrudRepository<Customer, Long>
 */
interface CustomerRepository extends JpaRepository<Customer, Long> {

}

Wenn ich den Code nun ausführe bekomme ich folgende Exception :
Java:
org.springframework.orm.jpa.JpaSystemException: Identifier of entity 'com.example.spring.data.jpa.Customer' must be manually assigned before calling 'persist()

Hat jemand eine Idee woran das liegen könnte.
Meine Vermutung ist, dass er die nicht weiß wie er die ID generieren soll.
In anderen Projekten hab eich dazu
@GeneratedValue
verwendet. Allerdings scheint das bei record nicht zu funktionieren
 

LimDul

Top Contributor
Ein Record ist unveränderlich. Das heißt, du musst beim Erzeugen der Daten eines Rekords bereits die ID vergeben. Ein GeneratedValue kann nicht funktionieren, da dann JPA die ID erst nachträglich ergänzen will.

Sprich hier:
Java:
new Customer(null, "Customer A")
Erzeugst du ein Customer Objekt mit id "nulL". Und da es unveränderlich ist hat es auf alle Ewigkeit die ID null. Und das kann man natürlich nicht speichern.
 

8u3631984

Bekanntes Mitglied
Danke dir für deine erklärung !
Ich habe anstelle eines Records nun eine Klasse verwendet und damit klappt es :
Java:
@AllArgsConstructor
@NoArgsConstructor
@Entity
@ToString
class Customer {
    @Id
    @GeneratedValue
    public Long ID;
    public String name;

}

Ich habe bei youtube das folgende Vidoe gesehen :

Dort werden auch Records verwendet allerdings mit Spring Data JDBC - macht das einen Unterschied ?
 

8u3631984

Bekanntes Mitglied
Ein Record ist unveränderlich. Das heißt, du musst beim Erzeugen der Daten eines Rekords bereits die ID vergeben. Ein GeneratedValue kann nicht funktionieren, da dann JPA die ID erst nachträglich ergänzen will.

Sprich hier:
Java:
new Customer(null, "Customer A")
Erzeugst du ein Customer Objekt mit id "nulL". Und da es unveränderlich ist hat es auf alle Ewigkeit die ID null. Und das kann man natürlich nicht speichern.
Ich habe noch mal ein bisschen experimentiert :
Java:
@SpringBootApplication
public class SpringDataJdbcApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringDataJdbcApplication.class, args);
    }

    @Bean
    ApplicationRunner demo(TeamRepository repository) {

        return args -> {

            var playerTeam1 = Set.of(new Player(null, "Player 1"),
                    new Player(null, "Player 2"),
                    new Player(null, "Player 3"),
                    new Player(null, "Player 4"),
                    new Player(null, "Player 5"));

            var playerTeam2 = Set.of(new Player(null, "Player 12"),
                    new Player(null, "Player 22"),
                    new Player(null, "Player 32"),
                    new Player(null, "Player 42"),
                    new Player(null, "Player 5"));

            var team1 = repository.save(new Team(null, "Team 1", playerTeam1));
            var team2 = repository.save(new Team(null, "Team 2", playerTeam2));
            repository.findAll().forEach(System.out::println);
        };
    }
}

record Team(@Id Integer id, String name, Set<Player> players) {
}

@Table(name = "TEAMPLAYER")
record Player(@Id Integer id, String name) {
}

interface TeamRepository extends ListCrudRepository<Team, Integer> {
}

SQL:
CREATE TABLE team(
    id integer  generated by default as identity primary key,
    name text
);
CREATE TABLE teamplayer(
    id integer  generated by default as identity primary key,
    team bigint not null references team(id),
    name text
);

Was mich hier stutzig macht, die Record werden richtig erzeugt und erhalten auch eine ID.
Code:
Team[id=1, name=Team 1, players=[Player[id=1, name=Player 4], Player[id=2, name=Player 3], Player[id=3, name=Player 2], Player[id=4, name=Player 1], Player[id=5, name=Player 5]]]
Team[id=2, name=Team 2, players=[Player[id=6, name=Player 42], Player[id=7, name=Player 32], Player[id=8, name=Player 22], Player[id=9, name=Player 12], Player[id=10, name=Player 5]]]

Ich würde gerne verstehen, was hier passiert
 

LimDul

Top Contributor
Hier überlässt du komplett der DB die IDs zu generieren. Sprich, du übergibst keine IDs, die erzeugt die Datenbank. (Anders als z.B. bei Sequencen, wo Hibernate die ID aus der Sequence liest und dann in das Objekt einfügst)

Du solltest das auch sehen, wenn du die SQL Statements protokolliert - dann wird vermutlich kein ID wert von Spring/Hibernate an die Datenbank geschickt. Da save ein neues Objekt zurückgibt, hast du da in dem neuen Objekt natürlich die ID drin.
 

Ähnliche Java Themen

Neue Themen


Oben