Apakah mungkin untuk menetapkan nilai default untuk kolom di JPA, dan jika, bagaimana cara menggunakan anotasi?
Apakah mungkin untuk menetapkan nilai default untuk kolom di JPA, dan jika, bagaimana cara menggunakan anotasi?
Jawaban:
Sebenarnya ini dimungkinkan di JPA, walaupun sedikit peretasan menggunakan columnDefinition
properti @Column
anotasi, misalnya:
@Column(name="Price", columnDefinition="Decimal(10,2) default '100.00'")
insertable=false
jika kolom tersebut dapat dibatalkan (dan untuk menghindari argumen kolom yang tidak dibutuhkan).
Anda dapat melakukan hal berikut:
@Column(name="price")
private double price = 0.0;
Sana! Anda baru saja menggunakan nol sebagai nilai default.
Catatan ini akan membantu Anda jika Anda hanya mengakses database dari aplikasi ini. Jika aplikasi lain juga menggunakan database, maka Anda harus melakukan pemeriksaan ini dari database menggunakan atribut anotasi columnDefinition Cameron's , atau cara lain.
Example
objek sebagai prototipe untuk pencarian. Setelah menetapkan nilai default, kueri contoh Hibernate tidak akan lagi mengabaikan kolom terkait di mana sebelumnya akan mengabaikannya karena itu nol. Pendekatan yang lebih baik adalah mengatur semua nilai default sesaat sebelum menjalankan Hibernate save()
atau update()
. Ini lebih baik meniru perilaku database yang menetapkan nilai default saat menyimpan baris.
null
misalnya). Menggunakan @PrePersist
dan @PreUpdate
merupakan pilihan yang lebih baik.
columnDefinition
properti tidak independen-database dan @PrePersist
menimpa pengaturan Anda sebelum menyisipkan, "nilai default" adalah sesuatu yang lain, nilai default digunakan ketika nilai tidak diatur secara eksplisit.
pendekatan lain menggunakan javax.persistence.PrePersist
@PrePersist
void preInsert() {
if (this.createdTime == null)
this.createdTime = new Date();
}
if (createdt != null) createdt = new Date();
atau sesuatu? Saat ini, ini akan menimpa nilai yang ditentukan secara eksplisit, yang membuatnya tidak benar-benar default.
if (createdt == null) createdt = new Date();
null
cek.
Pada 2017, JPA 2.1 masih memiliki hanya @Column(columnDefinition='...')
yang Anda menempatkan definisi SQL literal kolom. Yang cukup tidak fleksibel dan memaksa Anda untuk juga mendeklarasikan aspek-aspek lain seperti jenis, hubungan pendek pandangan implementasi JPA tentang masalah itu.
Hibernate, punya ini:
@Column(length = 4096, nullable = false)
@org.hibernate.annotations.ColumnDefault("")
private String description;
Mengidentifikasi nilai DEFAULT untuk diterapkan ke kolom terkait melalui DDL.
Dua catatan untuk itu:
1) Jangan takut menjadi tidak standar. Bekerja sebagai pengembang JBoss, saya telah melihat beberapa proses spesifikasi. Spesifikasi ini pada dasarnya adalah garis dasar bahwa para pemain besar di bidang yang diberikan bersedia berkomitmen untuk mendukung untuk dekade berikutnya. Itu benar untuk keamanan, untuk olahpesan, ORM tidak ada bedanya (walaupun JPA mencakup cukup banyak). Pengalaman saya sebagai pengembang adalah bahwa dalam aplikasi yang kompleks, cepat atau lambat Anda akan memerlukan API yang tidak standar. Dan@ColumnDefault
merupakan contoh ketika itu melampaui negatif menggunakan solusi non-standar.
2) Menyenangkan bagaimana semua orang melambaikan inisialisasi @PrePersist atau anggota konstruktor. Tapi itu TIDAK sama. Bagaimana dengan pembaruan SQL massal? Bagaimana dengan pernyataan yang tidak mengatur kolom? DEFAULT
memiliki perannya dan itu tidak dapat diganti dengan menginisialisasi anggota kelas Java.
JPA tidak mendukung itu dan akan berguna jika itu terjadi. Menggunakan columnDefinition khusus untuk DB dan tidak dapat diterima dalam banyak kasus. pengaturan default di kelas tidak cukup ketika Anda mengambil catatan yang memiliki nilai nol (yang biasanya terjadi ketika Anda menjalankan kembali tes DBUnit lama). Apa yang saya lakukan adalah ini:
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 sangat membantu dalam hal itu.
Melihat ketika saya menemukan ini dari Google ketika mencoba untuk memecahkan masalah yang sama, saya hanya akan melempar solusi yang saya buat seandainya seseorang merasa berguna.
Dari sudut pandang saya hanya ada 1 solusi untuk masalah ini - @PrePersist. Jika Anda melakukannya di @PrePersist, Anda harus memeriksa apakah nilainya sudah ditetapkan.
@PrePersist
usecase OP. @Column(columnDefinition=...)
sepertinya tidak elegan.
@Column(columnDefinition="tinyint(1) default 1")
Saya baru saja menguji masalahnya. Ini bekerja dengan baik. Terima kasih atas petunjuknya.
Tentang komentar:
@Column(name="price")
private double price = 0.0;
Yang ini tidak menetapkan nilai kolom default di database (tentu saja).
Anda dapat menggunakan java reflect api:
@PrePersist
void preInsert() {
PrePersistUtil.pre(this);
}
Ini biasa:
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();
dalam for()
mungkin juga oke. Dan juga tambahkan final
ke parameter / pengecualian yang ditangkap karena Anda tidak ingin object
dimodifikasi secara tidak sengaja. Juga menambahkan cek pada null
: if (null == object) { throw new NullPointerException("Parameter 'object' is null"); }
. Itu memastikan bahwa object.getClass()
itu aman untuk memanggil dan tidak memicu NPE
. Alasannya adalah untuk menghindari kesalahan oleh programmer yang malas. ;-)
Saya menggunakan columnDefinition
dan itu bekerja dengan sangat baik
@Column(columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP")
private Date createdDate;
Anda tidak dapat melakukan ini dengan anotasi kolom. Saya pikir satu-satunya cara adalah menetapkan nilai default ketika sebuah objek dibuat. Mungkin konstruktor default akan menjadi tempat yang tepat untuk melakukan itu.
@Column
sekitar. Dan saya juga kehilangan komentar yang ditetapkan (diambil dari Java doctag).
Dalam kasus saya, saya memodifikasi kode sumber hibernate-core, untuk memperkenalkan anotasi baru @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 );
Nah, ini adalah solusi hibernate-only.
@Column(columnDefinition='...')
tidak berfungsi ketika Anda menetapkan batasan default dalam database saat memasukkan data.insertable = false
dan menghapus columnDefinition='...'
dari anotasi, maka database akan secara otomatis memasukkan nilai default dari database.insertable = false
Hibernate / JPA, itu akan berfungsi.@PrePersist
void preInsert() {
if (this.dateOfConsent == null)
this.dateOfConsent = LocalDateTime.now();
if(this.consentExpiry==null)
this.consentExpiry = this.dateOfConsent.plusMonths(3);
}
Dalam kasus saya karena bidang ini adalah LocalDateTime yang saya gunakan ini, direkomendasikan karena independensi vendor
Baik anotasi JPA maupun Hibernate tidak mendukung gagasan nilai kolom default. Sebagai solusi untuk batasan ini, setel semua nilai default sesaat sebelum Anda menjalankan Hibernate save()
atau update()
pada sesi. Ini sedekat mungkin (pendek dari Hibernate pengaturan nilai default) meniru perilaku database yang menetapkan nilai default saat menyimpan baris dalam tabel.
Tidak seperti pengaturan nilai default di kelas model seperti yang disarankan jawaban alternatif ini , pendekatan ini juga memastikan bahwa kriteria kueri yang menggunakan Example
objek sebagai prototipe untuk pencarian akan terus bekerja seperti sebelumnya. Ketika Anda menetapkan nilai default atribut nullable (yang memiliki tipe non-primitif) di kelas model, contoh-contoh Hibernate tidak akan lagi mengabaikan kolom terkait di mana sebelumnya akan mengabaikannya karena itu nol.
Ini tidak mungkin di JPA.
Inilah yang dapat Anda lakukan dengan anotasi Kolom: http://java.sun.com/javaee/5/docs/api/javax/persistence/Column.html
Jika Anda menggunakan ganda, Anda dapat menggunakan yang berikut:
@Column(columnDefinition="double precision default '96'")
private Double grolsh;
Ya itu spesifik db.
Anda bisa menentukan nilai default di perancang database, atau saat Anda membuat tabel. Sebagai contoh di SQL Server Anda dapat mengatur kubah default bidang tanggal ke ( getDate()
). Gunakan insertable=false
seperti yang disebutkan dalam definisi kolom Anda. JPA tidak akan menentukan kolom pada sisipan dan database akan menghasilkan nilai untuk Anda.
Anda perlu insertable=false
di dalam @Column
anotasi Anda . JPA akan mengabaikan kolom itu saat memasukkan dalam database dan nilai default akan digunakan.
Lihat tautan ini: http://mariemjabloun.blogspot.com/2014/03/resolved-set-database-default-value-in.html
nullable=false
akan gagal dengan SqlException
: Caused by: java.sql.SQLException: Column 'opening_times_created' cannot be null
. Di sini saya lupa mengatur cap waktu "dibuat" dengan openingTime.setOpeningCreated(new Date())
. Ini adalah cara yang bagus untuk memiliki konsistensi tetapi itu yang tidak ditanyakan si penanya.