JPAの列のデフォルト値の設定


245

JPAの列のデフォルト値を設定することはできますか?また、アノテーションを使用してどのように行うのですか?


私はそれを非表示にしたいので、列のデフォルト値を別の列の値として配置できるJPA
仕様

回答:


230

実際には、JPAでも可能ですが、次のようにcolumnDefinition@Columnアノテーションのプロパティを使用して少しハックします。

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

3
10は整数部分で、2は数値の小数部分なので、1234567890.12がサポートされます。
Nathan Feger、

23
また、このcolumnDefinitionは必ずしもデータベースから独立しているわけではなく、Javaのデータ型が自動的に関連付けられることはありません。
Nathan Feger、

47
nullフィールドを持つエンティティを作成して永続化すると、値が設定されなくなります。解決策ではない...
Pascal Thivent 2010

18
いいえ@ NathanFeger、10は長さ、2は小数部です。したがって、1234567890.12はサポートされている数値ではありませんが、12345678.90は有効です。
GPrimola 2014年

4
insertable=false列がNULL可能である場合(および不要な列引数を回避する場合)が必要になります。
eckes

314

次のことができます。

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

そこ!デフォルト値としてゼロを使用しました。

これは、このアプリケーションからデータベースにのみアクセスしている場合に役立ちます。他のアプリケーションもデータベースを使用する場合は、Cameronの columnDefinitionアノテーション属性を使用するか、または他の方法でデータベースからこのチェックを行う必要があります。


38
これは、エンティティを挿入後に正しい値にしたい場合に適切な方法です。+1
Pascal Thivent 2010

16
モデルクラスでnull許容属性(非プリミティブ型の属性)のデフォルト値を設定すると、Exampleオブジェクトを検索のプロトタイプとして使用する条件クエリが中断されます。デフォルト値を設定した後、Hibernateサンプルクエリは、以前はnullだったために無視されていた関連列を無視しなくなりました。より良い方法は、Hibernate save()またはを呼び出す直前にすべてのデフォルト値を設定することですupdate()。これは、行を保存するときにデフォルト値を設定するデータベースの動作をよりよく模倣します。
デレク・マハール

1
@Harry:これは、例を検索のプロトタイプとして使用する条件クエリを使用している場合を除いて、デフォルト値を設定する正しい方法です。
Derek Mahar、2008

12
これは、デフォルトが非プリミティブ型(nullたとえば設定)に設定されていることを保証するものではありません。とを使用する@PrePersist@PreUpdate、より良いオプションです。
Jasper

3
これは、列のデフォルト値を指定する正しい方法です。columnDefinitionプロパティはデータベースに依存せず@PrePersist、挿入前の設定を上書きします。「デフォルト値」はそれ以外のものであり、値が明示的に設定されていない場合はデフォルト値が使用されます。
UtkuÖzdemir2014

110

別のアプローチはjavax.persistence.PrePersistを使用することです

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

1
タイムスタンプの追加と更新には、このようなアプローチを使用しました。それはとてもうまくいきました。
Spina 2013

10
これはif (createdt != null) createdt = new Date();何かすべきではないですか?現在、これは明示的に指定された値を上書きするため、実際にはデフォルトではないようです。
Max Nanasy 2014年

16
@MaxNanasyif (createdt == null) createdt = new Date();
シェーン

クライアントとデータベースが異なるタイムゾーンにあるかどうかは重要ですか?「TIMESTAMP DEFAULT CURRENT_TIMESTAMP」のようなものを使用するとデータベースサーバーの現在の時刻が取得され、「createdt = new Date()」ではJavaコードの現在の時刻が取得されると想像しますが、これら2つの時刻は同じでない場合があります。クライアントは別のタイムゾーンのサーバーに接続しています。
FrustratedWithFormsDesigner 2015年

nullチェックを追加しました。
OndraŽižka2017年

49

2017年でも、JPA 2.1に@Column(columnDefinition='...')は、列のリテラルSQL定義を配置するだけのものがあります。これは非常に柔軟性がなく、タイプなどの他の側面も宣言する必要があり、JPA実装のビューを短絡させます。

しかし休止状態には、これがあります:

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

DDLを介して関連する列に適用するDEFAULT値を識別します。

これに対する2つの注意:

1)非標準になることを恐れないでください。JBoss開発者として働いている私は、かなりの数の仕様プロセスを見てきました。仕様は基本的に、特定の分野の大手企業が次の10年程度のサポートを約束するベースラインです。セキュリティ、メッセージングの場合、ORMは違いはありません(JPAはかなりの範囲をカバーしています)。開発者としての私の経験は、複雑なアプリケーションでは遅かれ早かれ、非標準のAPIが必要になるということです。また@ColumnDefault、非標準のソリューションを使用することのマイナス面を上回っている例です。

2)誰もが@PrePersistまたはコンストラクタメンバーの初期化を振るのは素晴らしいことです。しかし、それは同じではありません。一括SQL更新はどうですか?列を設定しないステートメントはどうですか?DEFAULTこれには役割があり、Javaクラスメンバーを初期化しても代用できません。


Wildfly 12で使用できるhibernate-annotationsのバージョンは何ですか?これの特定のバージョンでエラーをキャッチしますか?よろしくお願いします!
フェルナンドパイ

オンドラありがとう。2019年には他に解決策はありますか?
アラン

1
残念ながら、在庫はありません。
jwenting

13

JPAはこれをサポートしていません。サポートしていると便利です。columnDefinitionの使用はDB固有であり、多くの場合許容できません。null値を持つレコードを取得する場合、クラスにデフォルトを設定するだけでは不十分です(これは通常、古いDBUnitテストを再実行するときに発生します)。私がすることはこれです:

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の自動ボクシングはその点で大いに役立ちます。


セッターを乱用しないでください!これらは、オブジェクトフィールドにパラメータを設定するためのものです。これ以上何もない!デフォルト値にはデフォルトコンストラクターを使用するほうがよい。
Roland

@Rolandは理論的にはいいのですが、アプリケーションがnull値を含まないようにしたい場所にすでにnull値が含まれている既存のデータベースを処理する場合は、常に機能するとは限りません。最初にデータベースを変換して、列を非ヌルにすると同時に、適切なデフォルトを設定する必要があります。次に、実際にヌル可能であると想定する他のアプリケーションからのバックラッシュに対処します(つまり、データベースを変更することもできます)。
jwenting

#JPAとセッター/ゲッターに関して言えば、アプリケーションが成長し続け、メンテナンスの悪夢となる1日は常に純粋なセッター/ゲッターでなければなりません。ここでの最高のアドバイスはKISSです(私はゲイではありません、笑)。
Roland

10

同じ問題を解決しようとしているときにGoogleからこれに出会ったのを見て、誰かが便利だと思った場合に備えて、私が作成したソリューションを投入します。

私の観点から見ると、この問題の解決策は本当に1つしかありません-@PrePersistです。@PrePersistで行う場合は、値がすでに設定されているかどうかを確認する必要があります。


4
+ @PrePersist1- 私は間違いなくOPのユースケースを選びます。@Column(columnDefinition=...)とてもエレガントに見えません。
Piotr Nowicki

@PrePersistは、ストアにnull値のデータがすでに存在する場合は役に立ちません。魔法のようにデータベース変換を行うわけではありません。
jwenting

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

問題をテストしました。正常に動作します。ヒントをありがとう。


コメントについて:

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

これは、データベースのデフォルトの列値を設定しません(もちろん)。


9
オブジェクトレベルでは正常に機能しません(エンティティへの挿入後、データベースのデフォルト値は取得されません)。Javaレベルでのデフォルト。
Pascal Thivent、2010

これは機能します(特に、データベースにinsertable = falseを指定した場合は、JPAがテーブルと列を選択した場合でも、キャッシュされたバージョンが更新されないので問題はありません)。少なくともhibernateでは機能しません。
ラウンド

9

JavaリフレクトAPIを使用できます。

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

2
私はこのソリューションが好きですが、弱点が1つあるので、デフォルト値を設定する前に、列がnull可能かどうかを確認することが重要だと思います。
ルイスカルロス

必要に応じて、コードをからに配置Field[] fields = object.getClass().getDeclaredFields();してfor()もかまいません。また、誤って変更しfinalたくないので、パラメータ/キャッチされた例外に追加しますobject。また、チェックを追加しnullますif (null == object) { throw new NullPointerException("Parameter 'object' is null"); }。これにより、object.getClass()安全に呼び出すことができ、がトリガーされませんNPE。その理由は、怠惰なプログラマーによる間違いを避けるためです。;-)
ローランド

7

私が使用しcolumnDefinition、それは非常にうまくいきます

@Column(columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP")

private Date createdDate;

6
これは、JPA実装ベンダーを特定するように見えます。
Udoが2012年

DDLベンダーを特定するだけです。しかし、とにかくベンダー固有のDDLハッキングが発生する可能性があります。ただし、前述のように(insertable = falseの場合でも)値はDBには表示されますが、セッションのエンティティキャッシュには表示されません(少なくとも休止状態では表示されません)。
eckes

7

列の注釈を使用してこれを行うことはできません。オブジェクトを作成するときにデフォルト値を設定するのが唯一の方法だと思います。おそらく、デフォルトのコンストラクターがそれを行うのに適切な場所でしょう。


良いアイデア。悲しいことに、@Column周りに一般的な注釈や属性はありません。また、設定されているコメント(Java doctagから取得)もありません。
Roland

5

私の場合、新しいアノテーションを導入するために、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 );

まあ、これは休止状態のみのソリューションです。


2
オープンソースプロジェクトに積極的に貢献してくれた人々の努力に感謝しますが、JPAはOR / Mに加えて標準のJava仕様であるため、OPはデフォルト値を指定するJPAの方法を求め、パッチは機能しましたHibernateのみ。これがNHibernateであり、永続性のスーパーパート仕様がない場合(NPAはMS EFでさえサポートされていません)、そのような種類のパッチを提案していました。真実は、JPAはORMの要件に比べてかなり制限されていることです(1つの例:セカンダリインデックスがない)。とにかく賛辞の努力に
USR-ローカルΕΨΗΕΛΩΝ

これはメンテナンスの問題を引き起こしませんか?休止状態のアップグレードのたびに、またはすべての新規インストールで、これらの変更をやり直す必要がありますか?
user1242321

質問はJPAに関するものであり、Hibernateについても言及されていないため、質問の答えにはなりません
Neil Stockton

5
  1. @Column(columnDefinition='...') データの挿入中にデータベースにデフォルトの制約を設定すると機能しません。
  2. アノテーションを作成insertable = falseおよび削除する必要がありcolumnDefinition='...'ます。そうすると、データベースはデータベースからデフォルト値を自動的に挿入します。
  3. たとえば、varcharを設定すると、データベースの性別はデフォルトで男性になります。
  4. insertable = falseHibernate / JPA を追加するだけで機能します。

いい答えだ。そして、これがExplain-about-insertable-false-updatable-false
Shihe Zhang

そして、その値を時々設定したいのであれば、良いオプションではありません。
Shihe Zhang

@ShiheZhang falseを使用すると、値を設定できませんか?
fvildoso

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

私の場合、フィールドがLocalDateTimeであるためにこれを使用しましたが、ベンダーに依存しないため推奨されます


2

JPAアノテーションもHibernateアノテーションも、デフォルトの列値の概念をサポートしていません。この制限の回避策として、Hibernateを呼び出す直前save()またはupdate()セッションですべてのデフォルト値を設定します。これは(Hibernateがデフォルト値を設定する前に)可能な限り厳密に、テーブルに行を保存するときにデフォルト値を設定するデータベースの動作を模倣しています。

この代替回答が示唆するようにモデルクラスでデフォルト値を設定するのとは異なり、このアプローチは、Exampleオブジェクトを検索のプロトタイプとして使用する基準クエリが以前と同様に機能し続けることも保証します。モデルクラスでnull可能な属性(非プリミティブタイプの属性)のデフォルト値を設定すると、Hibernateの例によるqueryは、以前はnullだったために無視されていた関連列を無視しなくなりました。


以前のソリューションでは、著者はColumnDefault( "")を変更しました
nikolai.serdiuk

@ nikolai.serdiukそのColumnDefaultアノテーションは、この回答が書かれてから数年後に追加されました。2010年は正解で、そのような注釈はありませんでした(実際、注釈はなく、xml構成のみがありました)。
jwenting

1

これはJPAでは不可能です。

ここでは何ができ柱アノテーションで行いますhttp://java.sun.com/javaee/5/docs/api/javax/persistence/Column.html


1
デフォルト値を指定できないことは、JPAアノテーションの重大な欠点のようです。
Derek Mahar

2
JDOはORM定義でそれを許可しているので、JPAはこれを含める必要があります... 1日
user383680

Cameron Popeが答えたように、columnDefinition属性を使用すると、間違いなくそれ行うことができます。
IntelliData 2016

@DerekMaharは、JPA仕様の唯一の欠点ではありません。それは良い仕様ですが、完璧ではありません
jwenting

1

doubleを使用している場合は、以下を使用できます。

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

private Double grolsh;

はい、それはdb固有です。


0

デフォルト値は、データベースデザイナで定義するか、テーブルを作成するときに定義できます。たとえばSQL Serverでは、日付フィールドのデフォルトのデータ保管庫を(getDate())に設定できます。insertable=false列の定義で述べたように使用します。JPAは挿入時にその列を指定せず、データベースが値を生成します。


-2

注釈を入れる必要がinsertable=falseあります@Column。JPAはデータベースへの挿入時にその列を無視し、デフォルト値が使用されます。

このリンクを参照してください:http : //mariemjabloun.blogspot.com/2014/03/resolved-set-database-default-value-in.html


2
次に、与えられた値@runtimeをどのように挿入しますか?
Ashish Ratan 2015

はい、そうです。:でnullable=false失敗しSqlExceptionますCaused by: java.sql.SQLException: Column 'opening_times_created' cannot be null。ここで、「作成された」タイムスタンプをに設定するのを忘れていましたopeningTime.setOpeningCreated(new Date())。これは一貫性を保つための良い方法ですが、それは質問者が尋ねなかったものです。
Roland
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.