Установка значений по умолчанию для столбцов в JPA

можно ли установить значение по умолчанию для столбцов в JPA, и если да, то как это делается с помощью аннотаций?

18 ответов


на самом деле это возможно в JPA, хотя немного взломать с помощью columnDefinition свойства @Column аннотации, например:

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

вы можете сделать следующее:

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

там! Вы только что использовали ноль в качестве значения по умолчанию.

обратите внимание, это будет служить вам, если вы только доступ к базе данных из этого приложения. Если другие приложения также используют базу данных, то вы должны сделать эту проверку из базы данных, используя Кэмерон columnDefinition атрибут аннотации или каким-либо другим способом.


другой подход использует javax.стойкость.PrePersist

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

в 2017 году JPA по-прежнему имеет только @Column(columnDefinition='...') к которому вы помещаете буквальное определение SQL столбца. Это довольно негибко и заставляет вас также объявлять другие аспекты, такие как тип, короткое замыкание представления реализации JPA по этому вопросу.

Hibernate, хотя, имеет это:

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

определяет значение по умолчанию для применения к связанному столбцу через DDL.

две заметки к этому:

1) Не бойтесь идти нестандартным. Работая разработчиком JBoss, я видел довольно много процессов спецификации. Спецификация-это в основном базовая линия, которую крупные игроки в данной области готовы поддержать в течение следующего десятилетия или около того. Это верно для безопасности, для обмена сообщениями, ORM нет разница (хотя JPA охватывает довольно много). Мой опыт разработчика заключается в том, что в сложном приложении рано или поздно вам все равно понадобится нестандартный API. И @ColumnDefault пример, когда он outweigts негативы использования нестандартное решение.

2) приятно, как все волны @PrePersist или инициализация члена конструктора. Но это не одно и то же. Как насчет массовых обновлений SQL? Как насчет утверждений, которые не задают столбец? DEFAULT имеет роль и это не заменяемый путем инициализации члена класса Java.


JPA не поддерживает это, и было бы полезно, если бы это было так. Использование columnDefinition зависит от БД и во многих случаях неприемлемо. установка значения по умолчанию в классе недостаточно при получении записи с нулевыми значениями (что обычно происходит при повторном запуске старых тестов DBUnit). Вот что я делаю:--2-->

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 очень помогает в этом.


видя, как я наткнулся на это из Google, пытаясь решить ту же проблему, я просто собираюсь бросить решение, которое я приготовил, если кто-то найдет его полезным.

с моей точки зрения есть только 1 решение этой проблемы -- @PrePersist. Если вы делаете это в @PrePersist,вы должны проверить, установлено ли значение уже.


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

Я только что проверил проблему. Он работает просто отлично. Спасибо за подсказку.


об комментариев:

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

этот не установить значение столбца по умолчанию в базе данных (естественно).


Я использую columnDefinition и он работает очень хорошо

@Column(columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP")

private Date createdDate;

вы не можете сделать это с помощью аннотации колонки. Я думаю, что единственный способ задать значение по умолчанию при создании объекта. Возможно, конструктор по умолчанию будет правильным местом для этого.


вы можете использовать api отражения java:

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

Это распространено:

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

в моем случае я изменил исходный код hibernate-core, ну, чтобы ввести новую аннотацию @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 );

Ну, это решение только для гибернации.


ни JPA, ни Hibernate аннотации не поддерживают понятие значения столбца по умолчанию. В качестве обходного пути к этому ограничению установите все значения по умолчанию перед вызовом Hibernate save() или update() на сессии. Это максимально близко (за исключением спящего режима, устанавливающего значения по умолчанию) имитирует поведение базы данных, которая устанавливает значения по умолчанию при сохранении строки в таблице.

В отличие от установки значений по умолчанию в классе модели, как это альтернатива ответ предполагает, что этот подход также гарантирует, что запросы критериев, которые используют Example объект как прототип для поиска будет продолжать работать как и раньше. Когда вы устанавливаете значение по умолчанию атрибута nullable (который имеет непримитивный тип) в классе модели, запрос Hibernate по примеру больше не будет игнорировать связанный столбец, где ранее он игнорировал бы его, потому что он был null.


Если вы используете double, вы можете использовать следующее:

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

private Double grolsh;

Да, это специфично для db.


  1. @Column(columnDefinition='...') не работает, когда вы установите ограничение по умолчанию в базе данных при вставке данных.
  2. вам нужно сделать insertable = false и удалить columnDefinition='...' из аннотации, затем база данных автоматически вставит значение по умолчанию из базы данных.
  3. Е. Г. когда вы установите тип varchar пол-мужчина по умолчанию в базе данных.
  4. вам просто нужно добавить insertable = false в спящем режиме / JPA он будет работать.

@PrePersist
void preInsert() {
    if (this.dateOfConsent == null)
        this.dateOfConsent = LocalDateTime.now();
    if(this.consentExpiry==null)
        this.consentExpiry = this.dateOfConsent.plusMonths(3);
}

в моем случае из-за поля LocalDateTime я использовал это, рекомендуется из-за независимости поставщика


Это невозможно в JPA.

вот что вы can сделайте с аннотацией столбца:http://java.sun.com/javaee/5/docs/api/javax/persistence/Column.html


можно определить значение по умолчанию в конструкторе баз данных или при создании таблицы. Например, в SQL Server можно задать для хранилища по умолчанию поля даты значение (getDate()). Использовать insertable=false как указано в определении столбца. JPA не будет указывать этот столбец при вставках, и база данных будет генерировать значение для вас.


вам нужно insertable=false вы @Column Примечание. JPA будет игнорировать этот столбец при вставке в базу данных, и будет использоваться значение по умолчанию.

см. эту ссылку : http://mariemjabloun.blogspot.com/2014/03/resolved-set-database-default-value-in.html