Est-il possible d'utiliser une séquence DB pour une colonne qui n'est pas l'identifiant / ne fait pas partie d'un identifiant composite ?
J'utilise hibernate comme fournisseur jpa et j'ai une table qui contient des colonnes qui sont des valeurs générées (en utilisant une séquence), bien qu'elles ne fassent pas partie de l'identifiant.
Ce que je veux, c'est utiliser une séquence pour créer une nouvelle valeur pour une entité, où la colonne de la séquence ne fait PAS (partie de) la clé primaire:
@Entity
@Table(name = "MyTable")
public class MyEntity {
//...
@Id //... etc
public Long getId() {
return id;
}
//note NO @Id here! but this doesn't work...
@GeneratedValue(strategy = GenerationType.AUTO, generator = "myGen")
@SequenceGenerator(name = "myGen", sequenceName = "MY_SEQUENCE")
@Column(name = "SEQ_VAL", unique = false, nullable = false, insertable = true, updatable = true)
public Long getMySequencedValue(){
return myVal;
}
}
Puis quand je fais ça:
em.persist(new MyEntity());
l'id sera généré, mais la mySequenceVal
propriété sera également générée par mon fournisseur JPA.
Pour clarifier les choses: je souhaite qu'Hibernate génère la valeur de la mySequencedValue
propriété. Je sais qu'Hibernate peut gérer les valeurs générées par la base de données, mais je ne veux pas utiliser de déclencheur ou autre chose que Hibernate lui-même pour générer la valeur de ma propriété. Si Hibernate peut générer des valeurs pour les clés primaires, pourquoi ne peut-il pas générer pour une propriété simple?
@GeneratedValue
les champs qui ne sont pas id. Veuillez voter pour être inclus dans 2.2 java.net/jira/browse/JPA_SPEC-113J'ai trouvé que cela
@Column(columnDefinition="serial")
fonctionne parfaitement mais uniquement pour PostgreSQL. Pour moi, c'était la solution parfaite, car la deuxième entité est une option «moche».la source
columnDefinition=
bit dit essentiellement à Hiberate de ne pas essayer de générer la définition de colonne et d'utiliser à la place le texte que vous avez donné. Essentiellement, votre DDL pour la colonne sera littéralement simplement name + columnDefinition. Dans ce cas (PostgreSQL),mycolumn serial
est une colonne valide dans une table.@Column(columnDefinition = "integer auto_increment")
@Column(insertable = false, updatable = false, columnDefinition="serial")
d'empêcher Hibernate d'essayer d'insérer des valeurs nulles ou de mettre à jour le champ. Vous devez ensuite interroger à nouveau la base de données pour obtenir l'ID généré après une insertion si vous devez l'utiliser immédiatement.Je sais que c'est une question très ancienne, mais elle se montre d'abord sur les résultats et jpa a beaucoup changé depuis la question.
La bonne façon de le faire maintenant est avec l'
@Generated
annotation. Vous pouvez définir la séquence, définir la valeur par défaut dans la colonne sur cette séquence, puis mapper la colonne comme:la source
Hibernate le soutient définitivement. À partir de la documentation:
"Les propriétés générées sont des propriétés dont les valeurs sont générées par la base de données. En règle générale, les applications Hibernate devaient actualiser les objets contenant les propriétés pour lesquelles la base de données générait des valeurs. Cependant, le marquage des propriétés comme générées permet à l'application de déléguer cette responsabilité à Hibernate. Essentiellement, chaque fois qu'Hibernate émet un SQL INSERT ou UPDATE pour une entité qui a défini des propriétés générées, il émet immédiatement une sélection par la suite pour récupérer les valeurs générées. "
Pour les propriétés générées lors de l'insertion uniquement, votre mappage de propriétés (.hbm.xml) ressemblerait à ceci:
Pour les propriétés générées lors de l'insertion et de la mise à jour, votre mappage de propriété (.hbm.xml) ressemblerait à ceci:
Malheureusement, je ne connais pas JPA, donc je ne sais pas si cette fonctionnalité est exposée via JPA (je soupçonne peut-être pas)
Vous devriez également pouvoir exclure la propriété des insertions et des mises à jour, puis appeler "manuellement" session.refresh (obj); après l'avoir inséré / mis à jour pour charger la valeur générée à partir de la base de données.
Voici comment vous excluriez l'utilisation de la propriété dans les instructions d'insertion et de mise à jour:
Encore une fois, je ne sais pas si JPA expose ces fonctionnalités Hibernate, mais Hibernate les prend en charge.
la source
En guise de suivi, voici comment je l'ai fait fonctionner:
la source
SQLQuery sqlQuery = getSession().createSQLQuery("select NAMED_SEQ.nextval seq from dual"); sqlQuery.addScalar("seq", LongType.INSTANCE); return (Long) sqlQuery.uniqueResult();
J'ai corrigé la génération d'UUID (ou de séquences) avec Hibernate en utilisant l'
@PrePersist
annotation:la source
Bien qu'il s'agisse d'un ancien fil de discussion, je souhaite partager ma solution et j'espère obtenir des commentaires à ce sujet. Soyez averti que je n'ai testé cette solution qu'avec ma base de données locale dans certains cas de test JUnit. Ce n'est donc pas une fonctionnalité productive jusqu'à présent.
J'ai résolu ce problème pour mon en introduisant une annotation personnalisée appelée Séquence sans propriété. C'est juste un marqueur pour les champs auxquels une valeur doit être attribuée à partir d'une séquence incrémentée.
En utilisant cette annotation, j'ai marqué mes entités.
Pour garder les choses indépendantes de la base de données, j'ai introduit une entité appelée SequenceNumber qui contient la valeur actuelle de la séquence et la taille de l'incrément. J'ai choisi le className comme clé unique pour que chaque classe d'entité obtienne sa propre séquence.
La dernière étape et la plus difficile est un PreInsertListener qui gère l'attribution des numéros de séquence. Notez que j'ai utilisé le ressort comme récipient à grains.
Comme vous pouvez le voir à partir du code ci-dessus, l'auditeur a utilisé une instance SequenceNumber par classe d'entité et réserve un couple de numéros de séquence définis par incrementValue de l'entité SequenceNumber. S'il manque de numéros de séquence, il charge l'entité SequenceNumber pour la classe cible et réserve les valeurs incrementValue pour les appels suivants. De cette façon, je n'ai pas besoin d'interroger la base de données chaque fois qu'une valeur de séquence est nécessaire. Notez la StatelessSession qui est en cours d'ouverture pour réserver le prochain ensemble de numéros de séquence. Vous ne pouvez pas utiliser la même session que l'entité cible est actuellement persistante car cela entraînerait une ConcurrentModificationException dans EntityPersister.
J'espère que cela aide quelqu'un.
la source
Si vous utilisez postgresql
et que j'utilise dans Spring Boot 1.5.6
la source
seq_order
et faire référence du champ,nextval('seq_order'::regclass)
Je cours dans la même situation que vous et je n'ai pas non plus trouvé de réponses sérieuses s'il est fondamentalement possible de générer des propriétés sans identifiant avec JPA ou non.
Ma solution est d'appeler la séquence avec une requête JPA native pour définir la propriété à la main avant de la persister.
Ce n'est pas satisfaisant mais cela fonctionne comme une solution de contournement pour le moment.
Mario
la source
J'ai trouvé cette note spécifique dans la session 9.1.9 Annotation GeneratedValue de la spécification JPA: "[43] Les applications portables ne devraient pas utiliser l'annotation GeneratedValue sur d'autres champs ou propriétés persistants." Donc, je suppose qu'il n'est pas possible de générer automatiquement de la valeur pour les valeurs de clé non primaire au moins en utilisant simplement JPA.
la source
On dirait que le fil est vieux, je voulais juste ajouter ma solution ici (Utilisation d'AspectJ - AOP au printemps).
La solution consiste à créer une annotation personnalisée
@InjectSequenceValue
comme suit.Vous pouvez maintenant annoter n'importe quel champ de l'entité, de sorte que la valeur du champ sous-jacent (Long / Integer) soit injectée au moment de l'exécution à l'aide de la valeur suivante de la séquence.
Annotez comme ceci.
Jusqu'à présent, nous avons marqué le champ dont nous avons besoin pour injecter la valeur de séquence.Nous allons donc voir comment injecter la valeur de séquence dans les champs marqués, cela se fait en créant le point coupé dans AspectJ.
Nous déclencherons l'injection juste avant que la
save/persist
méthode ne soit exécutée, ce qui se fait dans la classe ci-dessous.Vous pouvez maintenant annoter n'importe quelle entité comme ci-dessous.
la source
"Je ne veux pas utiliser de déclencheur ou autre chose que Hibernate lui-même pour générer la valeur de ma propriété"
Dans ce cas, que diriez-vous de créer une implémentation de UserType qui génère la valeur requise et de configurer les métadonnées pour utiliser ce UserType pour la persistance de la propriété mySequenceVal?
la source
Ce n'est pas la même chose que d'utiliser une séquence. Lorsque vous utilisez une séquence, vous n'insérez ni ne mettez à jour rien. Vous récupérez simplement la valeur de séquence suivante. Il semble que Hibernate ne le prend pas en charge.
la source
Si vous avez une colonne avec le type UNIQUEIDENTIFIER et la génération par défaut nécessaire à l'insertion mais que la colonne n'est pas PK
En db vous aurez
Dans ce cas vous ne définirez pas de générateur pour une valeur dont vous avez besoin (ce sera automatiquement grâce à
columnDefinition="UNIQUEIDENTIFIER"
). La même chose que vous pouvez essayer pour d'autres types de colonnesla source
J'ai trouvé une solution de contournement pour cela sur les bases de données MySql en utilisant @PostConstruct et JdbcTemplate dans une application Spring. Cela peut être faisable avec d'autres bases de données, mais le cas d'utilisation que je vais présenter est basé sur mon expérience avec MySql, car il utilise auto_increment.
Tout d'abord, j'avais essayé de définir une colonne comme auto_increment en utilisant la propriété ColumnDefinition de l'annotation @Column, mais cela ne fonctionnait pas car la colonne devait être une clé pour être auto-incrémentielle, mais apparemment, la colonne ne serait pas définie comme un index jusqu'à ce qu'il ait été défini, provoquant un blocage.
Voici où je suis venu avec l'idée de créer la colonne sans la définition auto_increment, et de l'ajouter après la création de la base de données. Cela est possible en utilisant l'annotation @PostConstruct, qui provoque l'appel d'une méthode juste après que l'application a initialisé les beans, associée à la méthode de mise à jour de JdbcTemplate.
Le code est comme suit:
Dans mon entité:
Dans une classe PostConstructComponent:
la source
Je souhaite proposer une alternative à la solution acceptée de @Morten Berg, qui a mieux fonctionné pour moi.
Cette approche permet de définir le champ avec le
Number
type réellement souhaité -Long
dans mon cas d'utilisation - au lieu deGeneralSequenceNumber
. Cela peut être utile, par exemple pour la (dé-) sérialisation JSON.L'inconvénient est qu'il nécessite un peu plus de surcharge de la base de données.
Tout d'abord, nous avons besoin d'un
ActualEntity
dans lequel nous voulons auto-incrémenter legenerated
typeLong
:Ensuite, nous avons besoin d'une entité d'assistance
Generated
. Je l'ai placé package-private à côté deActualEntity
, pour qu'il reste un détail d'implémentation du package:Enfin, nous avons besoin d'un endroit pour accrocher juste avant d'enregistrer le fichier
ActualEntity
. Là, nous créons et persistons uneGenerated
instance. Ceci fournit alors une séquence de base de données généréeid
de typeLong
. Nous utilisons cette valeur en l'écrivant dansActualEntity.generated
.Dans mon cas d'utilisation, j'ai implémenté cela à l'aide d'un Spring Data REST
@RepositoryEventHandler
, qui est appelé juste avant que leActualEntity
get ne persiste. Il doit démontrer le principe:Je ne l'ai pas testé dans une application réelle, alors profitez-en avec précaution.
la source
J'ai été dans une situation comme vous (séquence JPA / Hibernate pour le champ non @Id) et j'ai fini par créer un déclencheur dans mon schéma db qui ajoute un numéro de séquence unique lors de l'insertion. Je ne l'ai jamais fait fonctionner avec JPA / Hibernate
la source
Après avoir passé des heures, cela m'a parfaitement aidé à résoudre mon problème:
Pour Oracle 12c:
Pour H2:
Faites également:
la source