PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

hibernate n'a pas pu obtenir la valeur de la séquence suivante

Le dialecte PostgreSQL d'Hibernate n'est pas très brillant. Il ne connaît pas vos séquences par SERIAL et suppose qu'il existe une séquence globale à l'échelle de la base de données appelée "hibernate_sequence" qu'il peut utiliser.

(MISE À JOUR  :Il semble que les nouvelles versions d'Hibernate peuvent utiliser les séquences par défaut par table lorsque GenerationType.IDENTITY est spécifié. Testez votre version et utilisez-la à la place de celle ci-dessous si elle fonctionne pour vous.)

Vous devez modifier vos mappages pour spécifier explicitement chaque séquence. C'est ennuyeux, répétitif et inutile.

@Entity
@Table(name = "JUDGEMENTS")
public class Judgement implements Serializable, Cloneable {

    private static final long serialVersionUID = -7049957706738879274L;

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator="judgements_id_seq")
    @SequenceGenerator(name="judgements_id_seq", sequenceName="judgements_id_seq", allocationSize=1)
    @Column(name = "JUD_ID")
    private Long _judId;
...

Le allocationSize=1 est assez important. Si vous l'omettez, Hibernate supposera aveuglément que la séquence est définie avec INCREMENT 50 ainsi, lorsqu'il obtient une valeur d'une séquence, il peut utiliser cette valeur et les 49 valeurs en dessous en tant que clés générées uniques. Si vos séquences de base de données incrémentent de 1 - la valeur par défaut - cela entraînera des violations uniques car Hibernate essaiera de réutiliser les clés existantes.

Notez que l'obtention d'une clé à la fois permet entraîner un aller-retour supplémentaire par insert. Autant que je sache, Hibernate n'est pas capable d'utiliser INSERT ... RETURNING pour renvoyer efficacement les clés générées, et il ne peut apparemment pas non plus utiliser l'interface des clés générées par JDBC. Si vous lui dites d'utiliser une séquence, il appellera nextval pour obtenir la valeur puis insert cela explicitement, résultant en deux allers-retours. Pour réduire le coût de cela, vous pouvez définir un incrément plus important sur les séquences de touches avec beaucoup d'inserts, en vous rappelant de le définir sur le mappage et la séquence de base de données sous-jacente. Cela amènera Hibernate à appeler nextval moins fréquemment et cache des blocs de clés à distribuer au fur et à mesure.

Je suis sûr que vous pouvez voir d'après ce qui précède que je ne suis pas d'accord avec les choix de conception Hibernate faits ici, du moins du point de vue de son utilisation avec PostgreSQL. Ils devraient utiliser getGeneratedKeys ou en utilisant INSERT ... RETURNING avec DEFAULT pour la clé, laissant la base de données s'en occuper sans qu'Hibernate ait à se soucier des noms des séquences ou d'un accès explicite à celles-ci.

BTW, si vous utilisez Hibernate avec Pg, vous souhaiterez peut-être également un déclencheur oplock pour Pg pour permettre au verrouillage optimiste d'Hibernate d'interagir en toute sécurité avec le verrouillage normal de la base de données. Sans cela ou quelque chose comme ça, vos mises à jour Hibernate auront tendance à encombrer les modifications apportées via d'autres clients SQL réguliers. Demandez-moi comment je sais.