webentwicklung-frage-antwort-db.com.de

Konfigurieren Sie JPA so, dass PostgreSQL den Primärschlüsselwert generiert

Unser Projekt verwendet also die PostgreSQL-Datenbank und wir verwenden JPA für den Betrieb der Datenbank ..__ Wir haben die Entitäten aus der Datenbank mit automatischem Ersteller in Netbeans 7.1.2 erstellt.

Nach kleinen Änderungen werden unsere Primärschlüsselwerte beschrieben als:

@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Basic(optional = false)
@NotNull
@Column(name = "idwebuser", nullable = false)
private Integer idwebuser;

Das Problem ist, dass die Anwendung jetzt nicht flexibel ist, denn wenn wir die Datenbank direkt ändern (mithilfe von SQL oder einem anderen Tool), anstatt durch die Java-App zu gehen, ist der generierte Wert niedriger als der tatsächliche Datenbank-ID-Wert die Schaffung neuer Entitäten.

Gibt es eine Möglichkeit, dass die JPA die ID die Datenbank automatisch generieren lässt und sie dann nach dem Erstellungsprozess abrufen kann? Oder was könnte eine bessere Lösung sein? Vielen Dank.

EDIT Genauer gesagt: Wir haben eine Tabelle mit Benutzern, und mein Problem ist, dass die JPA bei Verwendung eines beliebigen Strategietyps eine neue Entität mit einer durch ihre Generator-ID angegebenen Einheit einfügt. Was für mich falsch ist, denn wenn ich selbst Änderungen an der Tabelle vornehme und neue Einträge hinzufüge, ist der GeneratedValue für Anwendung niedriger als die aktuelle ID - was uns zu einer Ausnahme mit doppelter ID führt. Können wir es beheben? ;)

Eine kurze Anmerkung zur Antwort Es gab eine kleine Lüge von meiner Seite, weil wir einen PG-Admin -> View ersten 100 Zeilen und bearbeitete Zeilen von dort anstatt von select verwendet haben. Es stellt sich auf jeden Fall heraus, dass dieser Editor den Aktualisierungsprozess der ID irgendwie überspringt, und selbst in der DB, wenn wir eine korrekte INSERT schreiben, wird diese mit einer falschen ID ausgeführt. Es war also im Grunde mehr ein Problem des Editors als die Datenbank und die Anwendung ...

jetzt funktioniert es sogar mit @GeneratedValue(strategy=GenerationType.IDENTITY)

34
Atais

In Anbetracht der Tabellendefinition:

CREATE TABLE webuser(
    idwebuser SERIAL PRIMARY KEY,
    ...
)

Verwenden Sie das Mapping:

@Entity
@Table(name="webuser")
class Webuser {

    @Id
    @SequenceGenerator(name="webuser_idwebuser_seq",
                       sequenceName="webuser_idwebuser_seq",
                       allocationSize=1)
    @GeneratedValue(strategy = GenerationType.SEQUENCE,
                    generator="webuser_idwebuser_seq")
    @Column(name = "idwebuser", updatable=false)
    private Integer id;

    // ....

}

Die Benennung tablename_columname_seq ist die PostgreSQL-Standardsequenz, die für SERIAL benannt wird. Ich empfehle Ihnen, sich daran zu halten.

Der allocationSize=1 ist wichtig, wenn Sie Hibernate benötigen, um mit anderen Clients an der Datenbank zusammenzuarbeiten.

Beachten Sie, dass diese Sequenz "Lücken" enthält, wenn Transaktionen rückgängig gemacht werden. Transaktionen können aus verschiedenen Gründen rückgängig gemacht werden. Ihre Bewerbung sollte darauf ausgelegt sein.

  • Nehmen Sie niemals an, dass für eine ID n eine ID n-1 oder n+1 vorhanden ist.
  • Nehmen Sie niemals an, dass die ID n vor einer ID kleiner als n oder nach einer ID größer als n hinzugefügt oder festgeschrieben wurde. Wenn Sie wirklich vorsichtig mit der Verwendung von Sequenzen sind, können Sie dies tun, sollten Sie aber niemals versuchen; Nehmen Sie stattdessen einen Zeitstempel in Ihre Tabelle auf.
  • Niemals eine ID hinzufügen oder von ihr abziehen. Vergleichen Sie sie auf Gleichheit und sonst nichts.

In der PostgreSQL-Dokumentation finden Sie die Sequenzen und die seriellen Datentypen .

Sie erklären, dass die Tabellendefinition oben eine Abkürzung für Folgendes ist:

CREATE SEQUENCE idwebuser_id_seq;
CREATE TABLE webuser(
    idwebuser integer primary key default nextval('idwebuser_id_seq'),
    ...
)
ALTER SEQUENCE idwebuser_id_seq OWNED BY webuser.idwebuser;

... was erklären sollte, warum wir eine @SequenceGenerator-Annotation hinzugefügt haben, um die Sequenz zu beschreiben.


Wenn Sie wirklich eine lückenlose Sequenz benötigen (z. B. Scheck- oder Rechnungsnummerierung), siehe lückenlose Sequenzen aber ernsthaft, vermeiden Sie dieses Design und niemals verwenden Sie es für einen Primärschlüssel.


Note: Wenn Ihre Tabellendefinition stattdessen so aussieht:

CREATE TABLE webuser(
    idwebuser integer primary key,
    ...
)

und Sie fügen mit (unsicher, nicht verwenden) hinein:

INSERT INTO webuser(idwebuser, ...) VALUES ( 
    (SELECT max(idwebuser) FROM webuser)+1, ...
);

oder (unsicher, tun Sie dies niemals):

INSERT INTO webuser(idwebuser, ...) VALUES ( 
    (SELECT count(idwebuser) FROM webuser), ...
);

dann Sie machen es falsch und sollten zu einer Sequenz (wie oben gezeigt) oder zu einer korrekten lückenlosen Sequenzimplementierung mit einer gesperrten Counter-Tabelle wechseln (wieder oben und siehe " lückenlose Sequenz postgresql "in Google). Beide oben genannten Dinge tun das Falsche, wenn in der Datenbank mehr als eine Verbindung arbeitet.

70
Craig Ringer

Es scheint, dass Sie den Sequenzgenerator wie folgt verwenden müssen:

@GeneratedValue(generator="YOUR_SEQ",strategy=GenerationType.SEQUENCE)
2
Denis Zevakhin

Bitte versuchen Sie, GenerationType.TABLE anstelle von GenerationType.IDENTITY zu verwenden. Die Datenbank erstellt eine separate Tabelle, die zum Erzeugen eindeutiger Primärschlüssel verwendet wird, und speichert auch die zuletzt verwendete ID-Nummer.

1
Leszek

Es funktioniert bei mir

  1. erstelle eine Tabelle wie diese, benutze SERIAL.
CREATE TABLE webuser(
    idwebuser SERIAL PRIMARY KEY,
    ...
)
  1. fügen Sie @GeneratedValue (strategy = GenerationType.IDENTITY) im Feld id hinzu.
@Entity
@Table(name="webuser")
class Webuser {

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

    // ....

}
0
hang gao

Sie können sich auch einige Mühe sparen, indem Sie ein Skript schreiben, um eine Massenkonvertierung des generischen GenerationType.IDENTITY zu der von der ausgewählten Antwort vorgeschlagenen Lösung. Das folgende Skript hat einige leichte Abhängigkeiten von der Formatierung der Java) - Quelldatei und nimmt Änderungen ohne Backups vor.

Nach dem Ausführen des Skripts:

  1. Suchen und Ersetzen import javax.persistence.Table; mit import javax.persistence.Table; import javax.persistence.SequenceGenerator;.
  2. Formatieren Sie den Quellcode in NetBeans wie folgt:
    1. Wählen Sie alle zu formatierenden Quelldateien aus.
    2. Drücken Sie Alt+Shift+F
    3. Bestätigen Sie die Neuformatierung.

Speichern Sie das folgende Skript als update-sequences.sh oder ähnliches:

#!/bin/bash

# Change this to the directory name (package name) where the entities reside.
PACKAGE=com/domain/project/entities

# Change this to the path where the Java source files are located.
cd src/main/Java

for i in $(find $PACKAGE/*.Java -type f); do
  # Only process classes that have an IDENTITY sequence.
  if grep "GenerationType.IDENTITY" $i > /dev/null; then
    # Extract the table name line.
    LINE_TABLE_NAME=$(grep -m 1 @Table $i | awk '{print $4;}')
    # Trim the quotes (if present).
    TABLE_NAME=${LINE_TABLE_NAME//\"}
    # Trim the comma (if present).
    TABLE_NAME=${TABLE_NAME//,}

    # Extract the column name line.
    LINE_COLUMN_NAME=$(grep -m 1 -C1 -A3 @Id $i | tail -1)
    COLUMN_NAME=$(echo $LINE_COLUMN_NAME | awk '{print $4;}')
    COLUMN_NAME=${COLUMN_NAME//\"}
    COLUMN_NAME=${COLUMN_NAME//,}

    # PostgreSQL sequence name.
    SEQUENCE_NAME="${TABLE_NAME}_${COLUMN_NAME}_seq"

    LINE_SEQ_GENERATOR="@SequenceGenerator( name = \"$SEQUENCE_NAME\", sequenceName = \"$SEQUENCE_NAME\", allocationSize = 1 )"
    LINE_GENERATED_VAL="@GeneratedValue( strategy = GenerationType.SEQUENCE, generator = \"$SEQUENCE_NAME\" )"
    LINE_COLUMN="@Column( name = \"$COLUMN_NAME\", updatable = false )\n"

    # These will depend on source code formatting.
    DELIM_BEGIN="@GeneratedValue( strategy = GenerationType.IDENTITY )"
    # @Basic( optional = false ) is also replaced.
    DELIM_ENDED="@Column( name = \"$COLUMN_NAME\" )"

    # Replace these lines...
    #
    # $DELIM_BEGIN
    # $DELIM_ENDED
    #
    # With these lines...
    #
    # $LINE_SEQ_GENERATOR
    # $LINE_GENERATED_VAL
    # $LINE_COLUMN

    sed -i -n "/$DELIM_BEGIN/{:a;N;/$DELIM_ENDED/!ba;N;s/.*\n/$LINE_SEQ_GENERATOR\n$LINE_GENERATED_VAL\n$LINE_COLUMN/};p" $i
  else
    echo "Skipping $i ..."
  fi
done

Bei der Generierung der CRUD-Anwendung mit NetBeans enthalten die ID-Attribute keine bearbeitbaren Eingabefelder.

0
Dave Jarvis