JPAの列のデフォルト値を設定することはできますか?また、アノテーションを使用してどのように行うのですか?
JPAの列のデフォルト値を設定することはできますか?また、アノテーションを使用してどのように行うのですか?
回答:
実際には、JPAでも可能ですが、次のようにcolumnDefinition
、@Column
アノテーションのプロパティを使用して少しハックします。
@Column(name="Price", columnDefinition="Decimal(10,2) default '100.00'")
insertable=false
列がNULL可能である場合(および不要な列引数を回避する場合)が必要になります。
次のことができます。
@Column(name="price")
private double price = 0.0;
そこ!デフォルト値としてゼロを使用しました。
これは、このアプリケーションからデータベースにのみアクセスしている場合に役立ちます。他のアプリケーションもデータベースを使用する場合は、Cameronの columnDefinitionアノテーション属性を使用するか、または他の方法でデータベースからこのチェックを行う必要があります。
Example
オブジェクトを検索のプロトタイプとして使用する条件クエリが中断されます。デフォルト値を設定した後、Hibernateサンプルクエリは、以前はnullだったために無視されていた関連列を無視しなくなりました。より良い方法は、Hibernate save()
またはを呼び出す直前にすべてのデフォルト値を設定することですupdate()
。これは、行を保存するときにデフォルト値を設定するデータベースの動作をよりよく模倣します。
null
たとえば設定)に設定されていることを保証するものではありません。とを使用する@PrePersist
と@PreUpdate
、より良いオプションです。
columnDefinition
プロパティはデータベースに依存せず@PrePersist
、挿入前の設定を上書きします。「デフォルト値」はそれ以外のものであり、値が明示的に設定されていない場合はデフォルト値が使用されます。
別のアプローチはjavax.persistence.PrePersistを使用することです
@PrePersist
void preInsert() {
if (this.createdTime == null)
this.createdTime = new Date();
}
if (createdt != null) createdt = new Date();
何かすべきではないですか?現在、これは明示的に指定された値を上書きするため、実際にはデフォルトではないようです。
if (createdt == null) createdt = new Date();
null
チェックを追加しました。
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クラスメンバーを初期化しても代用できません。
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の自動ボクシングはその点で大いに役立ちます。
同じ問題を解決しようとしているときにGoogleからこれに出会ったのを見て、誰かが便利だと思った場合に備えて、私が作成したソリューションを投入します。
私の観点から見ると、この問題の解決策は本当に1つしかありません-@PrePersistです。@PrePersistで行う場合は、値がすでに設定されているかどうかを確認する必要があります。
@PrePersist
1- 私は間違いなくOPのユースケースを選びます。@Column(columnDefinition=...)
とてもエレガントに見えません。
@Column(columnDefinition="tinyint(1) default 1")
問題をテストしました。正常に動作します。ヒントをありがとう。
コメントについて:
@Column(name="price")
private double price = 0.0;
これは、データベースのデフォルトの列値を設定しません(もちろん)。
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();
}
}
}
Field[] fields = object.getClass().getDeclaredFields();
してfor()
もかまいません。また、誤って変更しfinal
たくないので、パラメータ/キャッチされた例外に追加しますobject
。また、チェックを追加しnull
ますif (null == object) { throw new NullPointerException("Parameter 'object' is null"); }
。これにより、object.getClass()
安全に呼び出すことができ、がトリガーされませんNPE
。その理由は、怠惰なプログラマーによる間違いを避けるためです。;-)
私が使用しcolumnDefinition
、それは非常にうまくいきます
@Column(columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP")
private Date createdDate;
私の場合、新しいアノテーションを導入するために、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 );
まあ、これは休止状態のみのソリューションです。
@Column(columnDefinition='...')
データの挿入中にデータベースにデフォルトの制約を設定すると機能しません。insertable = false
および削除する必要がありcolumnDefinition='...'
ます。そうすると、データベースはデータベースからデフォルト値を自動的に挿入します。insertable = false
Hibernate / JPA を追加するだけで機能します。@PrePersist
void preInsert() {
if (this.dateOfConsent == null)
this.dateOfConsent = LocalDateTime.now();
if(this.consentExpiry==null)
this.consentExpiry = this.dateOfConsent.plusMonths(3);
}
私の場合、フィールドがLocalDateTimeであるためにこれを使用しましたが、ベンダーに依存しないため推奨されます
JPAアノテーションもHibernateアノテーションも、デフォルトの列値の概念をサポートしていません。この制限の回避策として、Hibernateを呼び出す直前save()
またはupdate()
セッションですべてのデフォルト値を設定します。これは(Hibernateがデフォルト値を設定する前に)可能な限り厳密に、テーブルに行を保存するときにデフォルト値を設定するデータベースの動作を模倣しています。
この代替回答が示唆するようにモデルクラスでデフォルト値を設定するのとは異なり、このアプローチは、Example
オブジェクトを検索のプロトタイプとして使用する基準クエリが以前と同様に機能し続けることも保証します。モデルクラスでnull可能な属性(非プリミティブタイプの属性)のデフォルト値を設定すると、Hibernateの例によるqueryは、以前はnullだったために無視されていた関連列を無視しなくなりました。
これはJPAでは不可能です。
ここでは何ができ柱アノテーションで行います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
nullable=false
失敗しSqlException
ますCaused by: java.sql.SQLException: Column 'opening_times_created' cannot be null
。ここで、「作成された」タイムスタンプをに設定するのを忘れていましたopeningTime.setOpeningCreated(new Date())
。これは一貫性を保つための良い方法ですが、それは質問者が尋ねなかったものです。