JPA'daki sütunlar için varsayılan bir değer ayarlamak mümkün mü ve ek açıklamalar kullanılarak nasıl yapılır?
JPA'daki sütunlar için varsayılan bir değer ayarlamak mümkün mü ve ek açıklamalar kullanılarak nasıl yapılır?
Yanıtlar:
Aslında ek açıklama columnDefinition
özelliğini kullanarak biraz hack olmasına rağmen, JPA'da mümkündür @Column
, örneğin:
@Column(name="Price", columnDefinition="Decimal(10,2) default '100.00'")
insertable=false
Sütunun boş bırakılabilir olması gerekir (ve gereksiz sütun bağımsız değişkeninden kaçınmak için).
Aşağıdakileri yapabilirsiniz:
@Column(name="price")
private double price = 0.0;
Orada! Varsayılan değer olarak sıfır kullandınız.
Yalnızca bu uygulamadan veritabanına erişiyorsanız bunun size hizmet edeceğini unutmayın. Diğer uygulamalar da veritabanını kullanıyorsa, bu denetimi Cameron'ın columnDefinition ek açıklama özelliğini veya başka bir yolu kullanarak veritabanından yapmalısınız .
Example
nesneyi arama için prototip olarak kullanan ölçüt sorgularını kırar . Varsayılan bir değer ayarlandıktan sonra, Hazırda Bekleme örneği sorgusu artık ilişkili olduğu sütunu yok saymayacaktır; burada daha önce boş olduğu için yok sayılır. Daha iyi bir yaklaşım, Hazırda Bekletme save()
veya işlevini başlatmadan hemen önce tüm varsayılan değerleri ayarlamaktır update()
. Bu, bir satırı kaydettiğinde varsayılan değerleri ayarlayan veritabanının davranışını daha iyi taklit eder.
null
örneğin ayar ). Kullanılması @PrePersist
ve @PreUpdate
daha iyi bir seçenektir IMHO.
columnDefinition
özelliği veritabanından bağımsız değildir ve @PrePersist
ekleme işleminden önce ayarınızı geçersiz kılar, "varsayılan değer" başka bir şeydir, değer açıkça ayarlanmadığında varsayılan değer kullanılır.
başka bir yaklaşım javax.persistence.PrePersist kullanmaktır
@PrePersist
void preInsert() {
if (this.createdTime == null)
this.createdTime = new Date();
}
if (createdt != null) createdt = new Date();
mi? Şu anda, bu gerçekten varsayılan bir değer değil gibi görünen açıkça belirtilen bir değeri geçersiz kılacaktır.
if (createdt == null) createdt = new Date();
null
Çek eklendi .
2017 yılında, JPA 2.1 hala sadece @Column(columnDefinition='...')
sütunun gerçek SQL tanımını koyduğunuza . Bu oldukça esnek olmayan ve JPA uygulamasının bu konudaki görüşünü kısa devre yapan, tür gibi diğer yönleri de beyan etmeye zorlar.
Hazırda bekletme modu var:
@Column(length = 4096, nullable = false)
@org.hibernate.annotations.ColumnDefault("")
private String description;
DDL aracılığıyla ilişkili sütuna uygulanacak VARSAYILAN değerini tanımlar.
Buna iki not:
1) Standart dışı olmaktan korkmayın. JBoss geliştiricisi olarak çalıştığım için, bazı spesifikasyon süreçleri gördüm. Spesifikasyon temel olarak, verilen alandaki büyük oyuncuların önümüzdeki on yıl için destek vermeye istekli oldukları temeldir. Güvenlik için, mesajlaşma için ORM bir fark değil (JPA oldukça fazla olsa da). Bir geliştirici olarak deneyimim, karmaşık bir uygulamada, er ya da geç, yine de standart olmayan bir API'ya ihtiyacınız olacak. Ve @ColumnDefault
standart olmayan bir çözüm kullanmanın olumsuzluklarından daha ağır bastığı bir örnektir.
2) Herkesin @PrePersist ya da kurucu üye başlatmayı nasıl dalgaladığı çok güzel. Ancak bu aynı DEĞİLDİR. Toplu SQL güncellemelerine ne dersiniz? Sütunu ayarlamayan ifadelere ne dersiniz? DEFAULT
rolü vardır ve bu bir Java sınıfı üyesi başlatılarak değiştirilemez.
JPA bunu desteklemiyor ve eğer yapsaydı faydalı olurdu. ColumnDefinition kullanımı DB'ye özgüdür ve çoğu durumda kabul edilemez. null değerleri olan (genellikle eski DBUnit sınamalarını yeniden çalıştırdığınızda gerçekleşen) bir kayıt aldığınızda sınıfta varsayılan olarak ayar yapmak yeterli değildir. Ne yaptığım bu:
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 otomatik boksu bu konuda çok yardımcı olur.
Aynı sorunu çözmeye çalışırken Google'dan tökezlediğimi görünce, birinin yararlı bulması durumunda pişirdiğim çözümü atacağım.
Benim açımdan, bu probleme gerçekten sadece 1 çözüm var - @PrePersist. @PrePersist içinde yaparsanız, değerin önceden ayarlanmış olup olmadığını kontrol etmeniz gerekir.
@PrePersist
OP'nin usecase'ini kesinlikle tercih ederim . @Column(columnDefinition=...)
çok zarif görünmüyor.
@Column(columnDefinition="tinyint(1) default 1")
Sorunu yeni test ettim. Sadece iyi çalışıyor. İpucu için teşekkürler.
Yorumlar hakkında:
@Column(name="price")
private double price = 0.0;
Bu seferki değil (elbette) veritabanında varsayılan sütun değerini ayarlayın.
Java yansıma API'sini kullanabilirsiniz:
@PrePersist
void preInsert() {
PrePersistUtil.pre(this);
}
Bu yaygındır:
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();
içine koymak for()
da uygun olabilir. Ayrıca , yanlışlıkla değiştirilmesini final
istemediğiniz için parametrenize / yakalanan istisnalarınıza da ekleyin object
. Ayrıca bir kontrol eklemek null
: if (null == object) { throw new NullPointerException("Parameter 'object' is null"); }
. Bu, object.getClass()
çağrının güvenli olmasını sağlar ve a'yı tetiklemez NPE
. Bunun nedeni tembel programcılar tarafından yapılan hatalardan kaçınmaktır. ;-)
Kullanıyorum columnDefinition
ve çok iyi çalışıyor
@Column(columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP")
private Date createdDate;
Bunu sütun ek açıklamasıyla yapamazsınız. Tek yol, bir nesne oluşturulduğunda varsayılan değeri ayarlamak olduğunu düşünüyorum. Belki de varsayılan kurucu bunu yapmak için doğru yer olacaktır.
@Column
etrafında genel bir açıklama veya özellik yok . Ayrıca ayarlanan yorumları da özlüyorum (Java doctag'den alınmıştır).
Benim durumumda, yeni bir açıklama eklemek için hazırda bekletme çekirdeği kaynak kodunu değiştirdim @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 );
Bu sadece hazırda bekletme çözümü.
@Column(columnDefinition='...')
veri eklenirken veritabanında varsayılan kısıtlamayı ayarladığınızda çalışmaz.insertable = false
ve kaldırmanız gerekir columnDefinition='...'
, ardından veritabanı varsayılan değeri veritabanından otomatik olarak ekler.insertable = false
Hazırda Beklet / JPA eklemeniz gerekir, işe yarayacaktır.@PrePersist
void preInsert() {
if (this.dateOfConsent == null)
this.dateOfConsent = LocalDateTime.now();
if(this.consentExpiry==null)
this.consentExpiry = this.dateOfConsent.plusMonths(3);
}
Benim durumumda bu alan kullanılan LocalDateTime nedeniyle, satıcı bağımsızlığı nedeniyle önerilir
Ne JPA ne de Hazırda Bekletme notları varsayılan sütun değeri kavramını desteklemez. Bu sınırlamaya geçici bir çözüm olarak, bir Hazırda Bekletme modunu başlatmadan hemen önce save()
veya update()
oturumda tüm varsayılan değerleri ayarlayın . Bu mümkün olduğunca yakından (Hazırda Beklet varsayılan değerlerini ayarlama kısa) veritabanı bir tabloya bir satır kaydettiğinde varsayılan değerleri ayarlayan davranışını taklit eder.
Bu alternatif yanıtın önerdiği gibi model sınıfındaki varsayılan değerlerin ayarlanmasının aksine , bu yaklaşım aynı zamanda bir Example
nesneyi arama için prototip olarak kullanan ölçüt sorgularının eskisi gibi çalışmaya devam etmesini sağlar. Bir model sınıfında null olabilecek bir özniteliğin (ilkel olmayan bir türe sahip olan) varsayılan değerini ayarladığınızda, bir örnek hazırda bekletme sorgusu artık ilişkili olduğu sütunu, daha önce boş olduğu için yoksayar.
Bu JPA'da mümkün değildir.
Sütun ek açıklamasıyla şunları yapabilirsiniz : http://java.sun.com/javaee/5/docs/api/javax/persistence/Column.html
Bir çift kullanıyorsanız aşağıdakileri kullanabilirsiniz:
@Column(columnDefinition="double precision default '96'")
private Double grolsh;
Evet db'ye özgüdür.
Veritabanı tasarımcısında veya tablo oluştururken varsayılan değeri tanımlayabilirsiniz. Örneğin, SQL Server'da bir Tarih alanının varsayılan kasasını ( getDate()
) olarak ayarlayabilirsiniz . insertable=false
Sütun tanımınızda belirtildiği gibi kullanın . JPA, bu sütun ekler üzerinde belirtmez ve veritabanı sizin için değeri oluşturur.
Sen gerek insertable=false
size de @Column
ek açıklama. JPA, veritabanına eklenirken bu sütunu yoksayar ve varsayılan değer kullanılır.
Bu bağlantıya bakın: http://mariemjabloun.blogspot.com/2014/03/resolved-set-database-default-value-in.html
nullable=false
Bir başarısız olur SqlException
: Caused by: java.sql.SQLException: Column 'opening_times_created' cannot be null
. Burada "oluşturulan" zaman damgası ile ayarlamak unuttum openingTime.setOpeningCreated(new Date())
. Tutarlı olmanın güzel bir yoludur, ancak soru soran bu değildi.