Est-il possible de définir une valeur par défaut pour les colonnes dans JPA, et si, comment cela se fait-il à l'aide d'annotations?
Est-il possible de définir une valeur par défaut pour les colonnes dans JPA, et si, comment cela se fait-il à l'aide d'annotations?
Réponses:
En fait, c'est possible dans JPA, bien qu'un peu de piratage utilise la columnDefinition
propriété de l' @Column
annotation, par exemple:
@Column(name="Price", columnDefinition="Decimal(10,2) default '100.00'")
insertable=false
si la colonne est nullable (et pour éviter l'argument de colonne inutile).
Vous pouvez effectuer les opérations suivantes:
@Column(name="price")
private double price = 0.0;
Là! Vous venez d'utiliser zéro comme valeur par défaut.
Notez que cela vous servira si vous accédez uniquement à la base de données à partir de cette application. Si d'autres applications utilisent également la base de données, vous devez effectuer cette vérification à partir de la base de données à l'aide de l' attribut d' annotation columnDefinition de Cameron , ou d'une autre manière.
Example
objet comme prototype pour la recherche. Après avoir défini une valeur par défaut, un exemple de requête Hibernate n'ignorera plus la colonne associée là où auparavant elle l'ignorerait car elle était nulle. Une meilleure approche consiste à définir toutes les valeurs par défaut juste avant d'appeler un Hibernate save()
ou update()
. Cela imite mieux le comportement de la base de données qui définit les valeurs par défaut lors de l'enregistrement d'une ligne.
null
par exemple). Utiliser @PrePersist
et @PreUpdate
est une meilleure option à mon humble avis.
columnDefinition
La propriété n'est pas indépendante de la base de données et @PrePersist
remplace votre paramètre avant l'insertion, "valeur par défaut" est autre chose, la valeur par défaut est utilisée lorsque la valeur n'est pas définie explicitement.
une autre approche utilise javax.persistence.PrePersist
@PrePersist
void preInsert() {
if (this.createdTime == null)
this.createdTime = new Date();
}
if (createdt != null) createdt = new Date();
quelque chose? À l'heure actuelle, cela remplacera une valeur explicitement spécifiée, ce qui semble ne pas être vraiment une valeur par défaut.
if (createdt == null) createdt = new Date();
null
chèque.
En 2017, JPA 2.1 n'a encore @Column(columnDefinition='...')
de la définition littérale SQL de la colonne. Ce qui est assez peu flexible et vous oblige à déclarer également les autres aspects comme le type, court-circuitant le point de vue de l'implémentation JPA à ce sujet.
Hibernate cependant, a ceci:
@Column(length = 4096, nullable = false)
@org.hibernate.annotations.ColumnDefault("")
private String description;
Identifie la valeur DEFAULT à appliquer à la colonne associée via DDL.
Deux notes à cela:
1) N'ayez pas peur de devenir non standard. En tant que développeur JBoss, j'ai vu pas mal de processus de spécification. La spécification est fondamentalement la référence que les grands acteurs dans un domaine donné sont prêts à s'engager à soutenir pour la prochaine décennie. C'est vrai pour la sécurité, pour la messagerie, ORM ne fait aucune différence (bien que JPA couvre beaucoup). Mon expérience en tant que développeur est que dans une application complexe, vous aurez besoin de toute façon tôt ou tard d'une API non standard. Et @ColumnDefault
est un exemple lorsqu'il dépasse les inconvénients de l'utilisation d'une solution non standard.
2) C'est agréable de voir comment tout le monde agite l'initialisation de @PrePersist ou du membre constructeur. Mais ce n'est pas pareil. Qu'en est-il des mises à jour SQL groupées? Que diriez-vous des déclarations qui ne définissent pas la colonne? DEFAULT
a son rôle et ce n'est pas substituable en initialisant un membre de classe Java.
JPA ne supporte pas cela et ce serait utile si c'était le cas. L'utilisation de columnDefinition est spécifique à la base de données et n'est pas acceptable dans de nombreux cas. la définition d'une valeur par défaut dans la classe n'est pas suffisante lorsque vous récupérez un enregistrement ayant des valeurs nulles (ce qui se produit généralement lorsque vous réexécutez d'anciens tests DBUnit). Ce que je fais c'est ceci:
public class MyObject
{
int attrib = 0;
/** Default is 0 */
@Column ( nullable = true )
public int getAttrib()
/** Falls to default = 0 when null */
public void setAttrib ( Integer attrib ) {
this.attrib = attrib == null ? 0 : attrib;
}
}
La boxe automatique Java y contribue beaucoup.
Voyant que je suis tombé sur Google en essayant de résoudre le même problème, je vais juste jeter la solution que j'ai préparée au cas où quelqu'un la trouverait utile.
De mon point de vue, il n'y a vraiment qu'une seule solution à ce problème - @PrePersist. Si vous le faites dans @PrePersist, vous devez vérifier si la valeur a déjà été définie.
@PrePersist
J'opterais certainement pour le cas d'utilisation d'OP. @Column(columnDefinition=...)
ne semble pas très élégant.
@Column(columnDefinition="tinyint(1) default 1")
Je viens de tester le problème. Cela fonctionne très bien. Merci pour l'astuce.
À propos des commentaires:
@Column(name="price")
private double price = 0.0;
Celui-ci ne définit pas la valeur de colonne par défaut dans la base de données (bien sûr).
vous pouvez utiliser l'api java reflect:
@PrePersist
void preInsert() {
PrePersistUtil.pre(this);
}
Ceci est courant:
public class PrePersistUtil {
private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
public static void pre(Object object){
try {
Field[] fields = object.getClass().getDeclaredFields();
for(Field field : fields){
field.setAccessible(true);
if (field.getType().getName().equals("java.lang.Long")
&& field.get(object) == null){
field.set(object,0L);
}else if (field.getType().getName().equals("java.lang.String")
&& field.get(object) == null){
field.set(object,"");
}else if (field.getType().getName().equals("java.util.Date")
&& field.get(object) == null){
field.set(object,sdf.parse("1900-01-01"));
}else if (field.getType().getName().equals("java.lang.Double")
&& field.get(object) == null){
field.set(object,0.0d);
}else if (field.getType().getName().equals("java.lang.Integer")
&& field.get(object) == null){
field.set(object,0);
}else if (field.getType().getName().equals("java.lang.Float")
&& field.get(object) == null){
field.set(object,0.0f);
}
}
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ParseException e) {
e.printStackTrace();
}
}
}
Field[] fields = object.getClass().getDeclaredFields();
dans for()
pourrait également être correct. Et ajoutez également final
à vos exceptions de paramètre / prises car vous ne voulez pas object
être modifié par accident. Ajoutez également un contrôle sur null
: if (null == object) { throw new NullPointerException("Parameter 'object' is null"); }
. Cela garantit que c'est object.getClass()
sûr à invoquer et ne déclenche pas un NPE
. La raison est d'éviter les erreurs des programmeurs paresseux. ;-)
J'utilise columnDefinition
et ça marche très bien
@Column(columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP")
private Date createdDate;
Vous ne pouvez pas faire cela avec l'annotation de colonne. Je pense que la seule façon est de définir la valeur par défaut lors de la création d'un objet. Peut-être que le constructeur par défaut serait le bon endroit pour le faire.
@Column
around. Et je manque également de définir des commentaires (tirés de Java doctag).
Dans mon cas, j'ai modifié le code source hibernate-core, enfin, pour introduire une nouvelle annotation @DefaultValue
:
commit 34199cba96b6b1dc42d0d19c066bd4d119b553d5
Author: Lenik <xjl at 99jsj.com>
Date: Wed Dec 21 13:28:33 2011 +0800
Add default-value ddl support with annotation @DefaultValue.
diff --git a/hibernate-core/src/main/java/org/hibernate/annotations/DefaultValue.java b/hibernate-core/src/main/java/org/hibernate/annotations/DefaultValue.java
new file mode 100644
index 0000000..b3e605e
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/annotations/DefaultValue.java
@@ -0,0 +1,35 @@
+package org.hibernate.annotations;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+
+/**
+ * Specify a default value for the column.
+ *
+ * This is used to generate the auto DDL.
+ *
+ * WARNING: This is not part of JPA 2.0 specification.
+ *
+ * @author 谢继雷
+ */
+@java.lang.annotation.Target({ FIELD, METHOD })
+@Retention(RUNTIME)
+public @interface DefaultValue {
+
+ /**
+ * The default value sql fragment.
+ *
+ * For string values, you need to quote the value like 'foo'.
+ *
+ * Because different database implementation may use different
+ * quoting format, so this is not portable. But for simple values
+ * like number and strings, this is generally enough for use.
+ */
+ String value();
+
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/Ejb3Column.java b/hibernate-core/src/main/java/org/hibernate/cfg/Ejb3Column.java
index b289b1e..ac57f1a 100644
--- a/hibernate-core/src/main/java/org/hibernate/cfg/Ejb3Column.java
+++ b/hibernate-core/src/main/java/org/hibernate/cfg/Ejb3Column.java
@@ -29,6 +29,7 @@ import org.hibernate.AnnotationException;
import org.hibernate.AssertionFailure;
import org.hibernate.annotations.ColumnTransformer;
import org.hibernate.annotations.ColumnTransformers;
+import org.hibernate.annotations.DefaultValue;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.cfg.annotations.Nullability;
import org.hibernate.mapping.Column;
@@ -65,6 +66,7 @@ public class Ejb3Column {
private String propertyName;
private boolean unique;
private boolean nullable = true;
+ private String defaultValue;
private String formulaString;
private Formula formula;
private Table table;
@@ -175,7 +177,15 @@ public class Ejb3Column {
return mappingColumn.isNullable();
}
- public Ejb3Column() {
+ public String getDefaultValue() {
+ return defaultValue;
+ }
+
+ public void setDefaultValue(String defaultValue) {
+ this.defaultValue = defaultValue;
+ }
+
+ public Ejb3Column() {
}
public void bind() {
@@ -186,7 +196,7 @@ public class Ejb3Column {
}
else {
initMappingColumn(
- logicalColumnName, propertyName, length, precision, scale, nullable, sqlType, unique, true
+ logicalColumnName, propertyName, length, precision, scale, nullable, sqlType, unique, defaultValue, true
);
log.debug( "Binding column: " + toString());
}
@@ -201,6 +211,7 @@ public class Ejb3Column {
boolean nullable,
String sqlType,
boolean unique,
+ String defaultValue,
boolean applyNamingStrategy) {
if ( StringHelper.isNotEmpty( formulaString ) ) {
this.formula = new Formula();
@@ -217,6 +228,7 @@ public class Ejb3Column {
this.mappingColumn.setNullable( nullable );
this.mappingColumn.setSqlType( sqlType );
this.mappingColumn.setUnique( unique );
+ this.mappingColumn.setDefaultValue(defaultValue);
if(writeExpression != null && !writeExpression.matches("[^?]*\\?[^?]*")) {
throw new AnnotationException(
@@ -454,6 +466,11 @@ public class Ejb3Column {
else {
column.setLogicalColumnName( columnName );
}
+ DefaultValue _defaultValue = inferredData.getProperty().getAnnotation(DefaultValue.class);
+ if (_defaultValue != null) {
+ String defaultValue = _defaultValue.value();
+ column.setDefaultValue(defaultValue);
+ }
column.setPropertyName(
BinderHelper.getRelativePath( propertyHolder, inferredData.getPropertyName() )
diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/Ejb3JoinColumn.java b/hibernate-core/src/main/java/org/hibernate/cfg/Ejb3JoinColumn.java
index e57636a..3d871f7 100644
--- a/hibernate-core/src/main/java/org/hibernate/cfg/Ejb3JoinColumn.java
+++ b/hibernate-core/src/main/java/org/hibernate/cfg/Ejb3JoinColumn.java
@@ -423,6 +424,7 @@ public class Ejb3JoinColumn extends Ejb3Column {
getMappingColumn() != null ? getMappingColumn().isNullable() : false,
referencedColumn.getSqlType(),
getMappingColumn() != null ? getMappingColumn().isUnique() : false,
+ null, // default-value
false
);
linkWithValue( value );
@@ -502,6 +504,7 @@ public class Ejb3JoinColumn extends Ejb3Column {
getMappingColumn().isNullable(),
column.getSqlType(),
getMappingColumn().isUnique(),
+ null, // default-value
false //We do copy no strategy here
);
linkWithValue( value );
Eh bien, c'est une solution d'hibernation uniquement.
@Column(columnDefinition='...')
ne fonctionne pas lorsque vous définissez la contrainte par défaut dans la base de données lors de l'insertion des données.insertable = false
et supprimer columnDefinition='...'
de l'annotation, puis la base de données insérera automatiquement la valeur par défaut de la base de données.insertable = false
Hibernate / JPA, cela fonctionnera.@PrePersist
void preInsert() {
if (this.dateOfConsent == null)
this.dateOfConsent = LocalDateTime.now();
if(this.consentExpiry==null)
this.consentExpiry = this.dateOfConsent.plusMonths(3);
}
Dans mon cas, en raison du champ LocalDateTime que j'ai utilisé, il est recommandé en raison de l'indépendance du fournisseur
Ni les annotations JPA ni Hibernate ne prennent en charge la notion de valeur de colonne par défaut. Pour contourner cette limitation, définissez toutes les valeurs par défaut juste avant d'appeler une mise en veille prolongée save()
ou update()
sur la session. Cela aussi étroitement que possible (à moins qu'Hibernate ne définisse les valeurs par défaut) imite le comportement de la base de données qui définit les valeurs par défaut lorsqu'elle enregistre une ligne dans une table.
Contrairement à la définition des valeurs par défaut dans la classe de modèle comme le suggère cette réponse alternative , cette approche garantit également que les requêtes de critères qui utilisent un Example
objet comme prototype pour la recherche continueront de fonctionner comme auparavant. Lorsque vous définissez la valeur par défaut d'un attribut nullable (celui qui a un type non primitif) dans une classe de modèle, une requête Hibernate par exemple n'ignorera plus la colonne associée là où auparavant elle l'ignorerait car elle était nulle.
Ce n'est pas possible dans JPA.
Voici ce que vous pouvez faire avec l'annotation de colonne: http://java.sun.com/javaee/5/docs/api/javax/persistence/Column.html
Si vous utilisez un double, vous pouvez utiliser ce qui suit:
@Column(columnDefinition="double precision default '96'")
private Double grolsh;
Oui, c'est spécifique à la base de données.
Vous pouvez définir la valeur par défaut dans le concepteur de base de données ou lorsque vous créez la table. Par exemple, dans SQL Server, vous pouvez définir le coffre-fort par défaut d'un champ Date sur ( getDate()
). Utilisez insertable=false
comme indiqué dans la définition de votre colonne. JPA ne spécifiera pas cette colonne dans les insertions et la base de données générera la valeur pour vous.
Tu dois insertable=false
dans votre @Column
annotation. JPA ignorera alors cette colonne lors de l'insertion dans la base de données et la valeur par défaut sera utilisée.
Voir ce lien: http://mariemjabloun.blogspot.com/2014/03/resolved-set-database-default-value-in.html
nullable=false
échouera avec SqlException
: Caused by: java.sql.SQLException: Column 'opening_times_created' cannot be null
. Ici, j'ai oublié de définir l'horodatage "créé" avec openingTime.setOpeningCreated(new Date())
. C'est une bonne façon d'avoir de la cohérence, mais c'est ce que l'interrogateur ne demandait pas.