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=falseSü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 .
Examplenesneyi 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ı @PrePersistve @PreUpdatedaha iyi bir seçenektir IMHO.
columnDefinitionözelliği veritabanından bağımsız değildir ve @PrePersistekleme 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 @ColumnDefaultstandart 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? DEFAULTrolü 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.
@PrePersistOP'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 finalistemediğ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 columnDefinitionve ç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.
@Columnetrafı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 = falseve kaldırmanız gerekir columnDefinition='...', ardından veritabanı varsayılan değeri veritabanından otomatik olarak ekler.insertable = falseHazı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 Examplenesneyi 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=falseSü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=falsesize de @Columnek 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=falseBir 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.