web-dev-qa-db-fra.com

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

j'ai l'application gwt connectée à la base de données postgres sur le backend, et une classe Java 'Judgment' mappant la table 'jugements' dans la base de données.

Caused by: org.hibernate.exception.SQLGrammarException: could not get next sequence value
...
Caused by: org.postgresql.util.PSQLException: ERROR: relation "hibernate_sequence" does not exist

ma classe de jugement ressemble à ceci

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

    private static final long serialVersionUID = -7049957706738879274L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "JUD_ID")
    private Long _judId;
...

et mes jugements à la table sont: 

   Column    |            Type             |                        Modifiers                        
-------------+-----------------------------+---------------------------------------------------------
 jud_id      | bigint                      | not null default nextval('judgements_id_seq'::regclass)
 rating      | character varying(255)      | 
 last_update | timestamp without time zone | 
 user_id     | character varying(255)      | 
 id          | integer                     | 
Indexes:
    "judgements_pkey" PRIMARY KEY, btree (jud_id)
Foreign-key constraints:
    "judgements_id_fkey" FOREIGN KEY (id) REFERENCES recommendations(id)
    "judgements_user_id_fkey" FOREIGN KEY (user_id) REFERENCES users(user_id)

et j'ai un nom de séquence 'judgements_id_seq' dans DB

quelqu'un peut-il me dire ce qui ne va pas ??? Merci.

42
user468587

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.


(UPDATE: il semble que les versions les plus récentes d'Hibernate peuvent utiliser les séquences par table par défaut lorsque GenerationType.IDENTITY est spécifié. Testez votre version et utilisez-la à la place de l'option ci-dessous si elle vous convient.)


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'elle obtient une valeur d'une séquence, elle peut utiliser cette valeur et les 49 valeurs situées en dessous comme clés uniques. Si vos séquences de base de données incrémentent de 1 (valeur par défaut), cela entraînera des violations uniques, car Hibernate essaiera de réutiliser des clés existantes.

Notez que l’obtention d’une clé à la fois sera entraîne un aller-retour supplémentaire par insertion. Autant que je sache, Hibernate n'est pas capable d'utiliser INSERT ... RETURNING pour renvoyer efficacement les clés générées, pas plus qu'il ne semble utiliser l'interface de 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 explicitement, ce qui entraînera deux allers-retours. Pour en réduire le coût, vous pouvez définir un incrément plus important sur les séquences de clés comportant de nombreuses insertions, sans oublier de le définir sur le mappage et de la séquence de base de données sous-jacente. Ainsi Hibernate appellera nextval moins souvent et mettra en cache des blocs de clés à distribuer au fur et à mesure.

Comme vous pouvez le constater ci-dessus, je ne suis pas du tout d'accord avec les choix de conception d'Hibernate effectués ici, du moins en ce qui concerne son utilisation avec PostgreSQL. Ils doivent utiliser getGeneratedKeys ou INSERT ... RETURNING avec DEFAULT pour la clé, ce qui permet à la base de données de s'en occuper sans que Hibernate ait à se préoccuper des noms des séquences ou de leur accès explicite.

En passant, si vous utilisez Hibernate avec Pg, vous voudrez peut-être aussi un déclencheur d'oplock pour Pg afin de permettre au verrouillage optimiste d'Hibernate d'interagir en toute sécurité avec le verrouillage normal de la base de données. Sans cela, vos mises à jour d'Hibernate auront tendance à bloquer les modifications apportées via d'autres clients SQL classiques. Demande-moi comment je sais.

79
Craig Ringer

Je semble me souvenir d’avoir utilisé @GeneratedValue(strategy = GenerationType.IDENTITY) pour que Hibernate utilise des colonnes "série" sur PostgreSQL.

41
davidg

Vous devez définir votre colonne @GeneratedId avec la stratégie GenerationType.IDENTITY au lieu de GenerationType.AUTO.

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "JUD_ID")
private Long _judId;
11
virag

J'ai eu la même erreur avant, Tapez cette requête dans votre base de données CREATE SEQUENCE hibernate_sequence START WITH 1 INCREMENT BY 1 NOCYCLE;

ça marche pour moi, bonne chance ~

7
splatter_fadli

J'aimerais également ajouter quelques notes sur une migration de MySQL à PostgreSQL:

  1. Dans votre DDL, dans la dénomination d’objet, préférez l’utilisation du caractère '_' (trait de soulignement) pour séparer les mots de la convention de casse du chameau. Ce dernier fonctionne bien dans MySQL mais pose beaucoup de problèmes dans PostgreSQL.
  2. La stratégie IDENTITY pour l'annotation @GeneratedValue dans les champs d'identité de classe de votre modèle fonctionne bien pour PostgreSQLDialect dans Hibernate 3.2 et versions supérieures. De plus, la stratégie AUTO est le paramètre typique de MySQLDialect.
  3. Si vous annotez vos classes de modèle avec @Table et définissez une valeur littérale égale à leur nom, assurez-vous d'avoir créé les tables à stocker sous un schéma public.

Pour autant que je m'en souvienne maintenant, espérons que ces astuces vous permettront de gagner quelques minutes d'essais et d'erreurs!

2
RickB

Je pense que vous avez déjà assez de réponses, mais j'ai exactement la même erreur et mon problème était un autre. Et j'ai perdu un peu de temps à essayer de le résoudre.

Dans mon cas, le problème était le propriétaire de la séquence dans Postgres. Par conséquent, si l'une des solutions ci-dessus n'a pas résolu votre problème, vérifiez si le propriétaire de la séquence est l'utilisateur/le rôle qui doit disposer de l'autorisation.

Suit un échantillon:

CREATE SEQUENCE seq_abcd
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;

ALTER TABLE public.seq_abcd OWNER TO USER_APP;

J'espère que cela peut être utile à n'importe qui.

2
Ventura

Utilisation de GeneratedValue et GenericGenerator avec la stratégie native:

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "id_native")
@GenericGenerator(name = "id_native", strategy = "native")
@Column(name = "id", updatable = false, nullable = false)
private Long id;

Je devais créer un appel de séquence hibernate_sequence car Hibernate recherchait une telle séquence par défaut:

create sequence hibernate_sequence start with 1 increment by 50;
grant usage, select on all sequences in schema public to my_user_name;
0
Stephane

La base de données que nous utilisons doit être mentionnée sous search_path dans le fichier de configuration SQL de Postgres. Cela peut être fait en modifiant le fichier de configuration Postgressql en définissant search_path avec le nom de la base de données, par exemple: TESTDB. 

  1. Recherchez le fichier postgressql.conf dans le dossier de données de la base de données Postgres SQL.
  2. Définissez search_path = "$ user", public, TESTDB;
  3. Redémarrez le service Postgres SQL pour affecter le changement.

Cela a fonctionné pour moi après avoir apporté le changement ci-dessus.

0
Chaitanya Ankam