Définition des valeurs par défaut pour les colonnes dans JPA

est-il possible de définir une valeur par défaut pour les colonnes dans JPA, et si, Comment est-ce fait en utilisant des annotations?

202
demandé sur Manuel Drieschmanns 2008-10-13 12:55:50

18 réponses

en fait, il est possible dans JPA, bien qu'un peu d'un piratage en utilisant la propriété columnDefinition de la @Column annotation, par exemple:

@Column(name="Price", columnDefinition="Decimal(10,2) default '100.00'")
193
répondu Cameron Pope 2011-10-05 09:12:15

vous pouvez faire ce qui suit:

@Column(name="price")
private double price = 0.0;

là! Vous venez d'utiliser zéro comme valeur par défaut.

notez que cela vous sera utile si vous accédez seulement à 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 en utilisant l'attribut de Cameron columnDefinition annotation, ou d'une autre manière.

252
répondu Pablo Venturino 2017-05-23 11:47:29

une autre approche utilise javax.persistance.PrePersist

@PrePersist
void preInsert() {
   if (this.createdTime == null)
       this.createdTime = new Date();
}
89
répondu Husin Wijaya 2017-10-02 04:17:54

en 2017, JPA a encore seulement @Column(columnDefinition='...') à laquelle vous mettez la définition SQL littérale de la colonne. Ce qui est tout à fait inflexible et vous oblige également à déclarer les autres aspects comme le type, court-circuitant le point de vue de la mise en œuvre de L'app sur cette question.

Hibernate a cependant ceci:

@Column(length = 4096, nullable = false)
@org.hibernate.annotations.ColumnDefault("")
private String description;

indique la valeur par défaut à appliquer à la colonne associée via DDL.

deux notes à cela:

1) Ne pas avoir peur de devenir non-standard. Travaillant en tant que développeur JBoss, j'ai vu pas mal de processus de spécification. La spécification est essentiellement la ligne de base que les grands acteurs dans compte tenu de champ sont prêts à s'engager à soutenir pour la prochaine décennie. C'est vrai pour la sécurité, pour la messagerie, ORM n'est pas une différence (bien que JPA couvre pas mal de choses). Mon expérience en tant que développeur est que dans une application complexe, tôt ou tard vous aurez besoin d'une API non standard de toute façon. Et @ColumnDefault est un exemple quand il l'emporte sur les négatifs de l'utilisation d'une solution non standard.

2) c'est bien comme tout le monde salue @PrePersist ou l'initialisation d'un membre constructeur. Mais ce n'est PAS la même. Que diriez-vous des mises à jour SQL en vrac? Pourquoi pas des déclarations qui ne positionnent pas la colonne? DEFAULT a son rôle et ce n'est pas substituable en initialisant Java class member.

25
répondu Ondra Žižka 2018-10-02 03:58:25

JPA ne soutient pas cela et il serait utile si elle l'a fait. L'utilisation de colonnedefinition est spécifique aux DB et n'est pas acceptable dans de nombreux cas. définir une valeur par défaut dans la classe n'est pas suffisant lorsque vous récupérez un enregistrement ayant des valeurs nulles (ce qui arrive typiquement lorsque vous relancez 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;
    }
}

Java auto-boxing aide beaucoup là-dedans.

13
répondu Marco 2011-03-11 01:49:28

vu que je suis tombé sur cela de Google tout en essayant de résoudre le même problème, je vais juste jeter dans la solution que j'ai concocté au cas où quelqu'un le trouve 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.

8
répondu TC1 2010-11-12 20:20:41
@Column(columnDefinition="tinyint(1) default 1")

je viens de tester la question. ça marche très bien. Merci pour le tuyau.


à propos des commentaires:

@Column(name="price") 
private double price = 0.0;

celui-ci ne définit pas la valeur par défaut de la colonne dans la base de données (bien sûr).

7
répondu asd 2011-11-13 10:49:41

j'utilise columnDefinition et ça marche très bien

@Column(columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP")

private Date createdDate;
6
répondu Tong 2012-01-05 21:54:03

vous ne pouvez pas faire ça avec l'annotation de la colonne. Je pense que le seul moyen est de définir la valeur par défaut lorsqu'un objet est créé. Peut-être que le constructeur par défaut serait le bon endroit pour le faire.

6
répondu Timo 2012-06-08 11:58:08

vous pouvez utiliser l'api Java reflect:

    @PrePersist
    void preInsert() {
       PrePersistUtil.pre(this);
    }

c'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();
            }
        }
    }
6
répondu Thomas Zhang 2016-04-06 03:58:25

dans mon cas, j'ai modifié hibernate-core source code, bien, 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 seulement.

5
répondu Xiè Jìléi 2011-12-21 05:40:21

ni les annotations JPA ni les annotations Hibernate ne supportent la notion d'une valeur de colonne par défaut. Pour contourner cette limitation, définissez toutes les valeurs par défaut juste avant d'invoquer une hibernation save() ou update() sur la session. Ceci imite le plus possible le comportement de la base de données qui définit les valeurs par défaut quand elle sauve une ligne dans une table.

contrairement à la définition des valeurs par défaut dans la classe model comme ceci réponse alternative suggère, cette approche garantit également que les requêtes critères qui utilisent un objet Example comme prototype pour la recherche continueront à fonctionner comme avant. Lorsque vous définissez la valeur par défaut d'un attribut nullable (un qui a un type non primitif) dans une classe model, une interrogation par exemple Hibernate n'ignorera plus la colonne associée où auparavant elle l'ignorerait parce qu'elle était nulle.

2
répondu Derek Mahar 2017-05-23 12:34:44

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 db.

1
répondu Gal Bracha 2012-02-08 10:24:18
  1. @Column(columnDefinition='...') ne fonctionne pas lorsque vous définissez la contrainte par défaut dans la base de données tout en insérant les données.
  2. vous devez faire 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.
  3. E. G. quand vous définissez varchar le sexe est masculin par défaut dans la base de données.
  4. il suffit d'ajouter insertable = false dans Hibernate/JPA, ça va marcher.
1
répondu Appesh 2017-06-28 08:31:21
@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 étant LocalDateTime Je l'ai utilisé, il est recommandé en raison de l'indépendance du vendeur

1
répondu Mohammed Rafeeq 2017-10-11 12:43:54

ce n'est pas possible dans JPA.

Voici ce que vous peut faire avec la Colonne d'annotation: http://java.sun.com/javaee/5/docs/api/javax/persistence/Column.html

0
répondu fiddlesticks 2008-10-13 09:10:06

vous pouvez définir la valeur par défaut dans le créateur de base de données, ou quand vous créez la table. Par exemple, dans SQL Server, vous pouvez définir la chambre forte par défaut d'un champ Date à ( getDate() ). Utilisez insertable=false comme indiqué dans la définition de votre colonne. JPA ne spécifiera pas cette colonne sur les inserts et la base de données générera la valeur pour vous.

0
répondu Dave Anderson 2016-03-04 08:33:49

Vous devez insertable=false en vous @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

-2
répondu MariemJab 2015-02-06 14:25:06