web-dev-qa-db-fra.com

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 se fait-il à l'aide d'annotations?

228
homaxto

En réalité, cela est possible dans JPA, bien qu’un petit peu de piratage utilisant la propriété columnDefinition de l’annotation @Column, par exemple:

@Column(name="Price", columnDefinition="Decimal(10,2) default '100.00'")
220
Cameron Pope

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 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 alors effectuer cette vérification à partir de la base de données à l'aide de Cameron columnDefinition attribut d'annotation, ou d'autres autre manière.

291
Pablo Venturino

une autre approche utilise javax.persistence.PrePersist

@PrePersist
void preInsert() {
   if (this.createdTime == null)
       this.createdTime = new Date();
}
103
Husin Wijaya

En 2017, JPA n'a toujours que @Column(columnDefinition='...') à laquelle vous insérez la définition SQL littérale de la colonne. Ce qui est assez rigide et vous oblige à déclarer également les autres aspects, tels que le type, court-circuitant le point de vue de la mise en œuvre de JPA sur 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 d'aller non standard. En travaillant en tant que développeur JBoss, j'ai vu pas mal de processus de spécification. La spécification est fondamentalement la base de référence que les grands acteurs dans un domaine donné sont prêts à s'engager à soutenir pour la prochaine décennie environ. C’est vrai pour la sécurité, pour la messagerie, ORM n’a aucune différence (même si JPA en couvre beaucoup). Mon expérience de développeur est que, dans une application complexe, tôt ou tard, vous aurez de toute façon besoin d'une API non standard. Et @ColumnDefault est un exemple lorsqu'il surpasse les inconvénients de l'utilisation d'une solution non standard.

2) C'est gentil comme tout le monde agite l'initialisation de @PrePersist ou d'un membre constructeur. Mais ce n'est pas la même chose. Qu'en est-il des mises à jour SQL en masse? Qu'en est-il des déclarations qui ne définissent pas la colonne? DEFAULT a son rôle et ce n'est pas substituable par l'initialisation de Java membre de la classe.

41
Ondra Žižka

JPA n'appuie pas cela et ce serait utile si elle le faisait. L'utilisation de columnDefinition est spécifique à la base de données et n'est pas acceptable dans de nombreux cas. définir une valeur par défaut dans la classe ne suffit pas lorsque vous récupérez un enregistrement ayant une valeur nulle (ce qui se produit généralement lorsque vous réexécutez d'anciens tests DBUnit). Ce que je fais 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 aide beaucoup à cela.

13
Marco

Vu que je suis tombé sur Google et que je suis tombé sur ce problème tout en essayant de résoudre le même problème, je vais ajouter la solution que j'ai concoctée au cas où quelqu'un le trouverait utile.

De mon point de vue, il n'y a vraiment qu'une solution à ce problème - @PrePersist. Si vous le faites dans @PrePersist, vous devez vérifier si la valeur a déjà été définie.

9
TC1
@Column(columnDefinition="tinyint(1) default 1")

Je viens de tester le problème. Cela fonctionne très bien. Merci pour le conseil.


A propos des commentaires:

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

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

8
asd

vous pouvez utiliser l'API Java reflect:

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

Ceci est commun:

    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();
            }
        }
    }
7
Thomas Zhang

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

@Column(columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP")

private Date createdDate;
7
Tong

Vous ne pouvez pas faire cela avec l'annotation de 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
Timo

Dans mon cas, j’ai modifié le code source d’hibernate-core, eh 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 谢继雷
+ */
[email protected]({ 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, il s’agit d’une solution pour hibernation uniquement.

5
Xiè Jìléi
@PrePersist
void preInsert() {
    if (this.dateOfConsent == null)
        this.dateOfConsent = LocalDateTime.now();
    if(this.consentExpiry==null)
        this.consentExpiry = this.dateOfConsent.plusMonths(3);
}

Dans mon cas, étant donné que le champ étant LocalDateTime, je l’ai utilisé, il est recommandé en raison de l’indépendance du fournisseur.

3
Mohammed Rafeeq

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 veille prolongée Hibernate save() ou update() sur la session. C’est aussi fidèle que possible (à moins que Hibernate ne définisse les valeurs par défaut) de reproduire 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 indiqué par réponse alternative , cette approche garantit également que les requêtes de critères utilisant un objet Example comme prototype de la recherche continueront de fonctionner comme avant. . Lorsque vous définissez la valeur par défaut d'un attribut nullable (ayant un type non primitif) dans une classe de modèle, une requête par exemple Hibernate n'ignorera plus la colonne associée alors qu'elle l'ignorait auparavant car elle était nulle.

2
Derek Mahar
  1. @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.
  2. Vous devez créer insertable = false et supprimer columnDefinition='...' de l'annotation. La base de données insérera automatiquement la valeur par défaut à partir de la base de données.
  3. Par exemple. Lorsque vous définissez varchar, le sexe est masculin par défaut dans la base de données.
  4. Vous devez simplement ajouter insertable = false dans Hibernate/JPA, cela fonctionnera.
1
Appesh

Si vous utilisez un double, vous pouvez utiliser les éléments suivants:

@Column(columnDefinition="double precision default '96'")

private Double grolsh;

Oui, c'est spécifique.

1
Gal Bracha

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 lors des insertions et la base de données générera la valeur pour vous.

0
Dave Anderson

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

0
PEELY