web-dev-qa-db-fra.com

Configurer JPA pour permettre à PostgreSQL de générer la valeur de la clé primaire

Notre projet utilise donc la base de données PostgreSQL et nous utilisons JPA pour l’exploitation de la base de données . Nous avons créé les entités à partir de la base de données avec le créateur automatique dans Netbeans 7.1.2.

Après de petits changements, nos valeurs de clé primaire sont décrites comme suit:

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

Le problème est qu’à présent, l’application n’est plus flexible, car lorsque nous modifions directement la base de données (à l’aide de SQL ou d’un autre outil) au lieu de passer par l’application Java, la valeur générée est inférieure à la valeur réelle de l’ID de la base de données. la création de nouvelles entités.

Existe-t-il une possibilité que le JPA laisse simplement la base de données générer l'identifiant automatiquement, puis l'obtenir après le processus de création? .__ ou quelle solution pourrait être meilleure? Merci.

EDIT Plus spécifiquement: Nous avons un tableau d’utilisateurs et mon problème est que, quel que soit le type de génération de stratégie utilisé, le JPA insère une nouvelle entité avec un identifiant spécifié par son générateur. Ce qui est faux pour moi, car si j'apporte moi-même des modifications à la table, en ajoutant de nouvelles entrées, la valeur GeneratedValue pour l'application est inférieure à l'ID actuel - ce qui nous conduit à une exception avec l'ID dupliqué . Pouvons-nous résoudre ce problème? ;)?

une petite note sur la réponse Il y avait un petit mensonge de mon côté parce que nous avons utilisé un PG Admin -> Voir les 100 premières lignes et édité des lignes à partir de là au lieu d'utiliser select. Quoi qu'il en soit, il s'avère que cet éditeur ignore en quelque sorte le processus de mise à jour de l'ID. Ainsi, même dans la base de données, lorsque nous écrivons un INSERT approprié, celui-ci est exécuté avec un ID incorrect. C'était donc essentiellement un problème de l'éditeur que nous utilisions plutôt que de la base de données et de l'application ...

maintenant, il fonctionne même avec @GeneratedValue(strategy=GenerationType.IDENTITY)

34
Atais

Compte tenu de la définition du tableau:

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

Utilisez la cartographie:

@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;

    // ....

}

Le nom tablename_columname_seq est le nom de séquence par défaut de PostgreSQL pour SERIAL et je vous recommande de vous y tenir.

Le allocationSize=1 est important si vous avez besoin qu'Hibernate coopère avec d'autres clients de la base de données.

Notez que cette séquence comportera des "lacunes" si les transactions sont annulées. Les transactions peuvent être annulées pour toutes sortes de raisons. Votre application doit être conçue pour y faire face.

  • Ne présumez jamais que, pour tout identifiant n, il existe un identifiant n-1 ou n+1
  • Ne supposez jamais que l'identifiant n a été ajouté ou validé avant un identifiant inférieur à n ou après un identifiant supérieur à n. Si vous faites très attention à la façon dont vous utilisez les séquences, vous pouvez le faire, mais vous ne devriez jamais essayer; enregistrez plutôt un horodatage dans votre table.
  • Ne jamais ajouter ou soustraire à un identifiant. Comparez-les pour l'égalité et rien d'autre.

Voir les documentation de PostgreSQL pour les séquences et les types de données série .

Ils expliquent que la définition de la table ci-dessus est essentiellement un raccourci pour:

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;

... ce qui devrait aider à expliquer pourquoi nous avons ajouté une annotation @SequenceGenerator pour décrire la séquence.


Si vous devez vraiment avoir une séquence sans espace (par exemple, numérotation de chèques ou de factures), voyez séquences sans blanc mais évitez sérieusement cette conception, et never utilisez-la pour une clé primaire.


Note: Si votre définition de table ressemble à ceci à la place:

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

et vous y insérez en utilisant (non sécurisé, n'utilisez pas):

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

ou (dangereux, ne faites jamais ceci):

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

then vous vous trompez et devez basculer vers une séquence (comme indiqué ci-dessus) ou vers une implémentation correcte gapless à l'aide d'une table de compteur verrouillée (voir à nouveau ci-dessus et voir "postgresql séquence sans intervalle" dans Google). Les deux opérations ci-dessus sont inefficaces s'il existe plusieurs connexions sur la base de données.

70
Craig Ringer

Il semble que vous deviez utiliser le générateur de séquence comme ceci:

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

S'il vous plaît, essayez d'utiliser GenerationType.TABLE au lieu de GenerationType.IDENTITY. La base de données créera une table séparée qui servira à générer des clés primaires uniques. Elle enregistrera également le dernier numéro d'identification utilisé.

1
Leszek

Ça marche pour moi

  1. créer une table comme celle-ci, utilisez SERIAL.
CREATE TABLE webuser(
    idwebuser SERIAL PRIMARY KEY,
    ...
)
  1. ajoutez @GeneratedValue (strategy = GenerationType.IDENTITY) dans le champ id.
@Entity
@Table(name="webuser")
class Webuser {

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

    // ....

}
0
hang gao