wake-up-neo.com

Festlegen von Standardwerten für Spalten in JPA

Ist es möglich, einen Standardwert für Spalten in JPA festzulegen, und wenn ja, wie erfolgt dies mithilfe von Anmerkungen?

228
homaxto

Tatsächlich ist es in JPA möglich, obwohl ein kleiner Hack die columnDefinition -Eigenschaft der @Column - Annotation verwendet, zum Beispiel:

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

Sie können Folgendes tun:

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

Dort! Sie haben gerade Null als Standardwert verwendet.

Beachten Sie, dass dies hilfreich ist, wenn Sie nur von dieser Anwendung aus auf die Datenbank zugreifen. Wenn die Datenbank auch von anderen Anwendungen verwendet wird, sollten Sie diese Prüfung über die Datenbank durchführen. Verwenden Sie dazu das Annotationsattribut Cameron's columnDefinition oder einige andere Weise.

291
Pablo Venturino

ein anderer Ansatz ist die Verwendung von javax.persistence.PrePersist

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

Im Jahr 2017 hat JPA nur noch @Column(columnDefinition='...'), in das Sie die Literal-SQL-Definition der Spalte einfügen. Das ist ziemlich unflexibel und zwingt Sie, auch die anderen Aspekte wie Typ zu deklarieren, was die Sichtweise der JPA-Implementierung in dieser Angelegenheit kurzschließt.

Der Ruhezustand hat jedoch Folgendes:

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

Gibt den DEFAULT-Wert an, der über DDL auf die zugeordnete Spalte angewendet werden soll.

Zwei Anmerkungen dazu:

1) Hab keine Angst davor, nicht dem Standard zu entsprechen. Als JBoss-Entwickler habe ich einige Spezifikationsprozesse gesehen. Die Spezifikation ist im Grunde die Basis, die die großen Akteure in einem bestimmten Bereich bereit sind, sich für die nächsten zehn Jahre zur Unterstützung zu verpflichten. Es ist wahr für die Sicherheit, für Messaging, ORM ist kein Unterschied (obwohl JPA ziemlich viel abdeckt). Als Entwickler habe ich die Erfahrung gemacht, dass Sie in einer komplexen Anwendung früher oder später sowieso eine Nicht-Standard-API benötigen. Und @ColumnDefault ist ein Beispiel, bei dem die Nachteile einer nicht standardmäßigen Lösung überwunden werden.

2) Es ist schön, wie jeder @PrePersist- oder Konstruktormitglied-Initialisierung winkt. Aber das ist nicht dasselbe. Wie wäre es mit SQL-Massenupdates? Wie wäre es mit Anweisungen, die die Spalte nicht setzen? DEFAULT hat seine Rolle und diese kann nicht durch Initialisieren von Java class member) ersetzt werden.

41
Ondra Žižka

JPA unterstützt das nicht und es wäre nützlich, wenn dies der Fall wäre. Die Verwendung von columnDefinition ist DB-spezifisch und in vielen Fällen nicht akzeptabel. Das Festlegen eines Standardwerts in der Klasse reicht nicht aus, wenn Sie einen Datensatz mit Nullwerten abrufen (was normalerweise der Fall ist, wenn Sie alte DBUnit-Tests erneut ausführen). Was ich mache ist das:

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 hilft dabei sehr.

13
Marco

Da ich auf Google gestoßen bin, während ich versucht habe, das gleiche Problem zu lösen, werde ich nur die Lösung einwerfen, die ich mir ausgedacht habe, falls jemand sie nützlich findet.

Aus meiner Sicht gibt es wirklich nur 1 Lösung für dieses Problem - @PrePersist. Wenn Sie dies in @PrePersist tun, müssen Sie überprüfen, ob der Wert bereits festgelegt wurde.

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

Ich habe das Problem gerade getestet. Es funktioniert prima. Danke für den Hinweis.


Über die Kommentare:

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

Dieser nicht Setzt den Standardspaltenwert in der Datenbank (natürlich).

8
asd

du kannst die Java reflect api:

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

Das ist üblich:

    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

Ich benutze columnDefinition und es funktioniert sehr gut

@Column(columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP")

private Date createdDate;
7
Tong

Dies ist mit der Spaltenanmerkung nicht möglich. Ich denke, die einzige Möglichkeit ist, den Standardwert festzulegen, wenn ein Objekt erstellt wird. Vielleicht ist der Standardkonstruktor der richtige Ort dafür.

6
Timo

In meinem Fall habe ich den Hibernate-Core-Quellcode geändert, um eine neue Annotation @DefaultValue Einzuführen:

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 })
[email protected](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 );

Dies ist eine reine Lösung für den Ruhezustand.

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);
}

In meinem Fall, da das Feld LocalDateTime ist, habe ich dies verwendet, wird es aus Gründen der Herstellerunabhängigkeit empfohlen

3
Mohammed Rafeeq

Weder JPA-Annotationen noch Hibernate-Annotationen unterstützen den Begriff eines Standardspaltenwerts. Um diese Einschränkung zu umgehen, legen Sie alle Standardwerte fest, bevor Sie in der Sitzung einen Ruhezustand save() oder update() aufrufen. Dies ist so genau wie möglich (kurz vor dem Festlegen der Standardwerte im Ruhezustand) und ahmt das Verhalten der Datenbank nach, die beim Speichern einer Zeile in einer Tabelle Standardwerte festlegt.

Im Gegensatz zum Festlegen der Standardwerte in der Modellklasse, wie in dieser alternativen Antwort vorgeschlagen, stellt dieser Ansatz auch sicher, dass Kriterienabfragen ein Example -Objekt verwenden als prototyp für die suche wird nach wie vor gearbeitet. Wenn Sie den Standardwert eines nullfähigen Attributs (eines Attributs, das keinen primitiven Typ hat) in einer Modellklasse festlegen, wird die zugehörige Spalte bei einer Ruhezustandsabfrage nicht mehr ignoriert, da sie zuvor null war.

2
Derek Mahar
  1. @Column(columnDefinition='...') funktioniert nicht, wenn Sie beim Einfügen der Daten die Standardeinschränkung in der Datenbank festlegen.
  2. Sie müssen insertable = false Erstellen und columnDefinition='...' Aus der Annotation entfernen, damit die Datenbank automatisch den Standardwert aus der Datenbank einfügt.
  3. Z.B. Wenn Sie varchar einstellen, ist das Geschlecht in der Datenbank standardmäßig männlich.
  4. Sie müssen nur insertable = false In Hibernate/JPA hinzufügen, es wird funktionieren.
1
Appesh

Wenn Sie ein Double verwenden, können Sie Folgendes verwenden:

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

private Double grolsh;

Ja, es ist db-spezifisch.

1
Gal Bracha

Sie können den Standardwert im Datenbankdesigner oder beim Erstellen der Tabelle definieren. In SQL Server können Sie beispielsweise den Standardtresor eines Datumsfelds auf (getDate()) setzen. Verwenden insertable=false wie in Ihrer Spaltendefinition angegeben. JPA gibt diese Spalte bei Einfügungen nicht an, und die Datenbank generiert den Wert für Sie.

0
Dave Anderson

Dies ist in JPA nicht möglich.

Folgendes können Sie mit der Spaltenanmerkung tun : http://Java.Sun.com/javaee/5/docs/api/ javax/persistence/Column.html

0
PEELY