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)
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.
n
, il existe un identifiant n-1
ou n+1
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.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.
Il semble que vous deviez utiliser le générateur de séquence comme ceci:
@GeneratedValue(generator="YOUR_SEQ",strategy=GenerationType.SEQUENCE)
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é.
Ça marche pour moi
CREATE TABLE webuser(
idwebuser SERIAL PRIMARY KEY,
...
)
@Entity
@Table(name="webuser")
class Webuser {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
// ....
}